Repository navigation
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Coverage Report
|
||||||||||||||||||||||||||||||||||||||||||||||||||
🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Get PR details
gh pr view 81 --repo rtuszik/KoalaKeys
echo "---FILES CHANGED---"
gh pr diff 81 --repo rtuszik/KoalaKeys --name-only
echo "---PR COMMENTS---"
gh pr view 81 --repo rtuszik/KoalaKeys --commentsLength of output: 31529 🏁 Script executed: #!/bin/bash
# Check pyproject.toml for scripts/entry points
cat pyproject.toml
echo "---README---"
cat README.md | head -100Length of output: 5139
Outstanding TODOs
Other things to check
Would you like me to do a full review of all the changes now? ✅ Action performedReview finished.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough
Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
pyproject.toml (2)
3-3:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAlign package version with the 1.0.0 release intent.
Line 3 still declares
0.6.0, which conflicts with the stated release target and can break release automation and artefact labelling.Proposed change
-version = "0.6.0" +version = "1.0.0"
1-8:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd a CLI script entry for
uv run koalakeys.
[project.scripts]is missing, so theuv run koalakeysobjective will not work.Proposed change
[project] name = "koalakeys" version = "0.6.0" description = "Build interactive, portable cheatsheets from YAML." readme = "README.md" requires-python = ">=3.9" dependencies = ["jinja2>=3.1.6", "python-dotenv>=1.1.1", "ruamel-yaml>=0.18.14"] + +[project.scripts] +koalakeys = "koalakeys.<module_with_main>:main"
🧹 Nitpick comments (2)
.github/workflows/pytest.yml (1)
18-20: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winRemove the duplicated non-browser test run.
Line 18-Line 20 already runs pytest, then Line 32-Line 38 runs it again with coverage. Keeping only the coverage run avoids duplicate CI time.
Proposed change
- - run: | - uv run pytest - # Browser/Playwright tests are excluded by default (pytest.ini) and run # only on pull requests, where we install Chromium for them. - name: Install Playwright browser if: github.event_name == 'pull_request' run: | uv run playwright install --with-deps chromiumAlso applies to: 32-38
tests/conftest.py (1)
9-16: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winConsolidate
isolated_outputinto one fixture definition.Line 9 introduces a shared fixture, but there is also an
isolated_outputfixture intests/test_generate_cheatsheet.py(Lines 10-16 in graph context). Keeping both increases drift risk and can create subtle shadowing differences; prefer using only theconftest.pyfixture.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f15c7b39-fc7a-4cef-ac6b-5dc0bba20052
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (45)
.github/actions/setup-python/action.yml.github/workflows/lint.yml.github/workflows/pytest.yml.pre-commit-config.ymldocs/schema/cheatsheet.schema.jsonpyproject.tomlpytest.inisrc/koalakeys/__init__.pysrc/koalakeys/generate_cheatsheet.pysrc/koalakeys/layouts/keyboard_layouts.yamlsrc/koalakeys/layouts/system_mappings.yamlsrc/koalakeys/logger.pysrc/koalakeys/template_renderer.pysrc/koalakeys/templates/base.htmlsrc/koalakeys/templates/cheatsheets/assets/cheatsheets.csssrc/koalakeys/templates/cheatsheets/cheatsheet-template.htmlsrc/koalakeys/templates/cheatsheets/components/body.htmlsrc/koalakeys/templates/cheatsheets/scripts/main.jssrc/koalakeys/templates/index/assets/index.csssrc/koalakeys/templates/index/components/body.htmlsrc/koalakeys/templates/index/components/footer.htmlsrc/koalakeys/templates/index/index_template.htmlsrc/koalakeys/templates/index/scripts/main.jssrc/koalakeys/themes/catppuccin.yamlsrc/koalakeys/themes/dracula.yamlsrc/koalakeys/themes/gruvbox.yamlsrc/koalakeys/themes/nord.yamlsrc/koalakeys/themes/rose-pine.yamlsrc/koalakeys/themes/solarized.yamlsrc/koalakeys/theming.pysrc/koalakeys/validate_yaml.pysrc/layouts/keyboard_layouts.yamlsrc/layouts/system_mappings.yamlsrc/templates/cheatsheets/assets/cheatsheets.csssrc/templates/cheatsheets/cheatsheet-template.htmlsrc/templates/index/assets/index.csstests/__init__.pytests/conftest.pytests/test_browser.pytests/test_generate_cheatsheet.pytests/test_integration.pytests/test_replace_shortcut_names.pytests/test_template_renderer.pytests/test_theming.pytests/test_validate_yaml.py
💤 Files with no reviewable changes (6)
- src/templates/index/assets/index.css
- tests/init.py
- src/layouts/keyboard_layouts.yaml
- src/templates/cheatsheets/cheatsheet-template.html
- src/layouts/system_mappings.yaml
- src/templates/cheatsheets/assets/cheatsheets.css
| THEMES_DIR = PROJECT_ROOT / "themes" | ||
| STYLES_DIR = PROJECT_ROOT / "styles" | ||
| LAYOUTS_DIR = PACKAGE_DIR / "layouts" |
There was a problem hiding this comment.
Theme lookup path is inconsistent with the relocated package assets.
Line 29 points to PROJECT_ROOT / "themes", but this PR moves themes into the package namespace. That can cause resolve_theme(...) to miss theme files and make generate_html(...) return None on otherwise valid input.
💡 Proposed fix
-THEMES_DIR = PROJECT_ROOT / "themes"
+THEMES_DIR = PACKAGE_DIR / "themes"There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟠 Major · Align the package version with the 1.0.0 release target. · pyproject.toml:3
pyproject.toml:3
🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAlign the package version with the 1.0.0 release target.
A build from this PR will declare version
0.6.0in its package metadata. Setversionto1.0.0, or align the release target with0.6.0, before publishing.
🟠 Major · Confine custom_css to STYLES_DIR. · generate_cheatsheet.py:142
src/koalakeys/generate_cheatsheet.py:142
🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟠 Major | ⚡ Quick winPath Traversal
Exploitability: Moderate
CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')Confine
custom_csstoSTYLES_DIR.If an attacker can supply a cheatsheet,
custom_csscan contain an absolute path or..segments.STYLES_DIR / filenamecan then read any readable file outsidestyles/, and the content is inserted into the generated HTML. Resolve the root and candidate paths, then reject candidates outside the resolvedSTYLES_DIRbefore callingread_text.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Advanced
Run ID: c1f8a17f-2c12-4f4f-a227-b9c9220a260a
⛔ Files ignored due to path filters (1)
assets/images/KoalaKeys_Example.pngis excluded by!**/*.png
📒 Files selected for processing (12)
README.mdpyproject.tomlsrc/koalakeys/cli.pysrc/koalakeys/generate_cheatsheet.pysrc/koalakeys/scaffold/__init__.pysrc/koalakeys/scaffold/example.yamlsrc/koalakeys/theming.pysrc/koalakeys/validate_yaml.pytests/test_cli.pytests/test_generate_cheatsheet.pytests/test_scaffold.pyyaml_cheatsheet_spec.md
💤 Files with no reviewable changes (1)
- src/koalakeys/validate_yaml.py
🚧 Files skipped from review as they are similar to previous changes (1)
- src/koalakeys/theming.py
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

Major · Replace the flagged prek release before merge. · pyproject.toml:14
pyproject.toml:14
🔒 Security & Privacy | 🟠 Major | ⚡ Quick winReplace the flagged
prekrelease before merge.
uv.lockresolves the development dependency toprek==0.3.6and records PyPI distribution URLs and hashes. This is the release flagged by Socket Security. Replace it with a vetted release and regenerateuv.lock, or remove the dependency.

Minor · Correct the current-directory statement. · README.md:95
README.md:95
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCorrect the current-directory statement.
init [path]can scaffold a directory other than the current directory. State thatgenerateandvalidateuse the current directory, whileinituses its path argument.
🔇 Additional comments: 2
pyproject.toml: 1
3-10: LGTM!Also applies to: 13-13, 15-20, 23-29, 34-37, 64-81
README.md-15-15: 1
15-15:Minor · Maintainability & Code Quality · Quick win
⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.Split the quick-start instruction into separate steps.
The punctuation makes the command sequence unclear. LanguageTool also reports
DOUBLE_PUNCTUATIONon this line. Use separate sentences or an ordered list.Source: Linters/SAST tools

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
b7a4da03-740a-43b1-bfd0-f945d6ed974f
Files ignored due to path filters: 1-
uv.lockis excluded by!**/*.lock
Files selected for processing: 2-
README.mdpyproject.toml
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.
-
There was a problem hiding this comment.
Actionable comments posted: 7

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
8b36972f-54a1-4187-bcbc-3cb9199432c0
Files ignored due to path filters: 2-
assets/images/KoalaKeys_Example.pngis excluded by!**/*.pnguv.lockis excluded by!**/*.lock
Files selected for processing: 52-
.github/actions/setup-python/action.yml.github/workflows/lint.yml.github/workflows/pytest.yml.pre-commit-config.ymlREADME.mddocs/schema/cheatsheet.schema.jsonpyproject.tomlpytest.inisrc/koalakeys/__init__.pysrc/koalakeys/cli.pysrc/koalakeys/generate_cheatsheet.pysrc/koalakeys/layouts/keyboard_layouts.yamlsrc/koalakeys/layouts/system_mappings.yamlsrc/koalakeys/logger.pysrc/koalakeys/scaffold/__init__.pysrc/koalakeys/scaffold/example.yamlsrc/koalakeys/template_renderer.pysrc/koalakeys/templates/base.htmlsrc/koalakeys/templates/cheatsheets/assets/cheatsheets.csssrc/koalakeys/templates/cheatsheets/cheatsheet-template.htmlsrc/koalakeys/templates/cheatsheets/components/body.htmlsrc/koalakeys/templates/cheatsheets/scripts/main.jssrc/koalakeys/templates/index/assets/index.csssrc/koalakeys/templates/index/components/body.htmlsrc/koalakeys/templates/index/components/footer.htmlsrc/koalakeys/templates/index/index_template.htmlsrc/koalakeys/templates/index/scripts/main.jssrc/koalakeys/themes/catppuccin.yamlsrc/koalakeys/themes/dracula.yamlsrc/koalakeys/themes/gruvbox.yamlsrc/koalakeys/themes/nord.yamlsrc/koalakeys/themes/rose-pine.yamlsrc/koalakeys/themes/solarized.yamlsrc/koalakeys/theming.pysrc/koalakeys/validate_yaml.pysrc/layouts/keyboard_layouts.yamlsrc/layouts/system_mappings.yamlsrc/templates/cheatsheets/assets/cheatsheets.csssrc/templates/cheatsheets/cheatsheet-template.htmlsrc/templates/index/assets/index.csstests/__init__.pytests/conftest.pytests/test_browser.pytests/test_cli.pytests/test_generate_cheatsheet.pytests/test_integration.pytests/test_replace_shortcut_names.pytests/test_scaffold.pytests/test_template_renderer.pytests/test_theming.pytests/test_validate_yaml.pyyaml_cheatsheet_spec.md
Files with no reviewable changes: 11-
- src/koalakeys/templates/index/components/body.html
- tests/init.py
- src/koalakeys/init.py
- src/layouts/system_mappings.yaml
- src/koalakeys/templates/index/components/footer.html
- src/koalakeys/templates/index/scripts/main.js
- src/templates/index/assets/index.css
- src/templates/cheatsheets/assets/cheatsheets.css
- src/templates/cheatsheets/cheatsheet-template.html
- src/layouts/keyboard_layouts.yaml
- src/koalakeys/templates/index/index_template.html
Files skipped from review as they are similar to previous changes: 1-
- src/koalakeys/templates/index/assets/index.css
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.
-
|
|
||
| - Generate HTML cheat sheets from YAML files | ||
| - Interactive keyboard layout with real-time highlighting | ||
| - Six built-in themes (catppuccin, dracula, gruvbox, nord, rosé-pine, solarized) plus user themes and custom CSS |
There was a problem hiding this comment.
🔎 Supported by static analysis
-
Script executed:
#!/bin/bash # Check the built-in identifier and any normalisation in theme resolution. rg -n -C 5 'rose-pine|rosé-pine|def resolve_theme|BUILTIN' \ src/koalakeys/theming.py src/koalakeys/themes/rose-pine.yaml yaml_cheatsheet_spec.md
Repository: rtuszik/KoalaKeys
Length of output: 2261
Script executed:
#!/bin/bash printf '%s\n' '--- resolver ---' nl -ba src/koalakeys/theming.py | sed -n '186,225p' printf '%s\n' '--- README ---' nl -ba README.md | sed -n '24,32p'
Repository: rtuszik/KoalaKeys
Length of output: 2485
Use the accepted theme identifier.
The built-in identifier is rose-pine. Replace rosé-pine in the README.
Suggested fix
-
--- "a/README.md" +++ "b/README.md" @@ -25,7 +25,7 @@ - Generate HTML cheat sheets from YAML files - Interactive keyboard layout with real-time highlighting -- Six built-in themes (catppuccin, dracula, gruvbox, nord, rosé-pine, solarized) plus user themes and custom CSS +- Six built-in themes (catppuccin, dracula, gruvbox, nord, rose-pine, solarized) plus user themes and custom CSS - Categorized shortcuts with descriptions - Index page for quick access to all cheat sheets - Search functionality
Committable suggestion
-
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.Suggested change- Six built-in themes (catppuccin, dracula, gruvbox, nord, rosé-pine, solarized) plus user themes and custom CSS - Six built-in themes (catppuccin, dracula, gruvbox, nord, rose-pine, solarized) plus user themes and custom CSS
| if in_place: | ||
| print(" koalakeys generate") | ||
| else: | ||
| print(f" cd {args.path or DEFAULT_PROJECT_DIRNAME} && koalakeys generate") |
There was a problem hiding this comment.
Make the printed command work for paths with spaces.
If a user runs koalakeys init "my project", the next step prints cd my project && koalakeys generate. That command does not enter the new directory. Quote or escape the path in the printed command.
| all_valid = False | ||
| if not path.is_file(): | ||
| continue | ||
| for warning in lint_yaml(path): |
There was a problem hiding this comment.
Handle lint read errors after validation fails.
If a .yaml file contains invalid UTF-8, validate_yaml() returns False, but path.is_file() remains true. lint_yaml() then raises UnicodeDecodeError, so validate prints a traceback instead of completing its file checks. Catch read and decoding errors around the lint call, and retain the non-zero result.
| def _validate_token_value(name: str, value: object) -> str: | ||
| if not isinstance(value, (str, int, float)): | ||
| raise ThemeError(f"Token '{name}' must be a scalar, got {type(value).__name__}") |
There was a problem hiding this comment.
🔎 Supported by static analysis
-
Script executed:
sed -n '35,155p' src/koalakeys/theming.pyRepository: rtuszik/KoalaKeys
Length of output: 3829
Script executed:
sed -n '156,260p' src/koalakeys/theming.py echo '--- spec sanitisation mentions ---' rg -n -i -- 'sanitis|sanitiz' --glob '*.md' . || test "$?" -eq 1
Repository: rtuszik/KoalaKeys
Length of output: 5594
Script executed:
sed -n '260,330p' src/koalakeys/theming.py echo '--- spec context ---' sed -n '190,205p' yaml_cheatsheet_spec.md
Repository: rtuszik/KoalaKeys
Length of output: 2270
Validate token names before emitting them as CSS custom properties.
_validate_token_value checks only the value. The token name is never checked. _split_theme_doc stores each non-reserved key in shared without validating it. _resolve_user_theme merges these keys without filtering, and render_token_css writes --kk-{k}: {v}; for each one. The token CSS does not pass through sanitize_css.
A user theme can use the key "a: red; } body {display:none} .x {--b" with the value red. The value red passes _COLOR_RE. The key is not matched against _COLOR_RE. The output then contains --kk-a: red; } body {display:none} .x {--b: red;, which adds a body rule. A key such as </style><script> can also end the <style> block, because the template emits the token CSS with | safe.
yaml_cheatsheet_spec.md states that theme values are validated and custom CSS is sanitised before emission. It does not mention token names, so token names are emitted without a check.
Restrict token names to a safe pattern. Alternatively, accept only names in COLOR_TOKENS and SHARED_TOKENS.
🛡️ Proposed fix
--- "a/src/koalakeys/theming.py"
+++ "b/src/koalakeys/theming.py"
@@ -106,8 +106,13 @@
return "\n".join(lines)
+_TOKEN_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$")
+
+
def _validate_token_value(name: str, value: object) -> str:
+ if not isinstance(name, str) or not _TOKEN_NAME_RE.match(name):
+ raise ThemeError(f"Invalid token name: {name!r}")
if not isinstance(value, (str, int, float)):
raise ThemeError(f"Token '{name}' must be a scalar, got {type(value).__name__}")
text = str(value).strip()
if name == "font":Place _TOKEN_NAME_RE with the other regular expressions. Check that existing theme files use only lowercase letters, digits and hyphens in token names.
| parent = data.get("extends") | ||
| if not parent: | ||
| raise ThemeError(f"User theme '{name}' must declare 'extends: <built-in>'") | ||
| if parent not in builtin_names: |
There was a problem hiding this comment.
Reject non-string extends values with ThemeError.
parent not in builtin_names raises TypeError for an unhashable value, for example extends: [catppuccin]. apply_styling catches only ThemeError. The exception therefore escapes generate_html and aborts generate_all. The same problem applies to an unhashable default_mode at Line 285 (default_mode not in modes).
Check isinstance(parent, str) first. Apply the same check to default_mode.
🛡️ Proposed fix
-
--- "a/src/koalakeys/theming.py" +++ "b/src/koalakeys/theming.py" @@ -247,7 +247,7 @@ parent = data.get("extends") if not parent: raise ThemeError(f"User theme '{name}' must declare 'extends: <built-in>'") - if parent not in builtin_names: + if not isinstance(parent, str) or parent not in builtin_names: raise ThemeError(f"User theme '{name}' extends unknown built-in '{parent}'. Available: {sorted(builtin_names)}") base = _resolve_builtin(parent, builtins_dir)
Committable suggestion
-
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.Suggested changeif parent not in builtin_names: if not isinstance(parent, str) or parent not in builtin_names:
| merged_modes: dict[str, dict[str, str]] = {} | ||
| for mode in MODES: | ||
| base_tokens = getattr(base, mode) | ||
| if base_tokens is None and mode not in user_modes: | ||
| continue | ||
| merged_modes[mode] = {**(base_tokens or {}), **user_modes.get(mode, {})} |
There was a problem hiding this comment.
Check that merged user-theme modes define every colour token.
_assert_complete runs only for built-in themes. A user theme can extend a dark-only built-in (for example nord) and declare a partial light: block. The merge then creates a light mode that holds only the user's tokens. both_modes becomes true, so the toggle appears. Switching to light mode leaves most --kk-* variables undefined.
After merging, check each mode against COLOR_TOKENS and raise ThemeError when tokens are missing.
🐛 Proposed fix
-
merged_modes[mode] = {**(base_tokens or {}), **user_modes.get(mode, {})} + missing = [t for t in COLOR_TOKENS if t not in merged_modes[mode]] + if missing: + raise ThemeError(f"User theme '{name}' mode '{mode}' missing tokens: {missing}")
Committable suggestion
-
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.Suggested changemerged_modes: dict[str, dict[str, str]] = {} for mode in MODES: base_tokens = getattr(base, mode) if base_tokens is None and mode not in user_modes: continue merged_modes[mode] = {**(base_tokens or {}), **user_modes.get(mode, {})} merged_modes: dict[str, dict[str, str]] = {} for mode in MODES: base_tokens = getattr(base, mode) if base_tokens is None and mode not in user_modes: continue merged_modes[mode] = {**(base_tokens or {}), **user_modes.get(mode, {})} missing = [t for t in COLOR_TOKENS if t not in merged_modes[mode]] if missing: raise ThemeError(f"User theme '{name}' mode '{mode}' missing tokens: {missing}")
| sheet exactly like a built-in: `theme: my-dracula`. | ||
|
|
||
| The full token list (all color, typography, and spacing tokens a theme can set) | ||
| is documented in [docs/themes/tokens.md](docs/themes/tokens.md). |
There was a problem hiding this comment.
🔎 Supported by static analysis
-
Script executed:
#!/bin/bash fd -i 'tokens.md' docs rg -n 'docs/themes/tokens.md' --glob '*.md' --glob '*.yaml' --glob '*.css'
Repository: rtuszik/KoalaKeys
Length of output: 453
Script executed:
test -e docs/themes/tokens.md && echo "target: present" || echo "target: absent" test -d docs && echo "docs dir: present" || echo "docs dir: absent" ls -la docs 2>&1 || true git ls-files | rg -i 'tokens' || test "$?" -eq 1 git ls-files | rg -i '^docs/' || test "$?" -eq 1
Repository: rtuszik/KoalaKeys
Length of output: 379
Add the missing token reference or fix the broken link.
The link in yaml_cheatsheet_spec.md points to docs/themes/tokens.md, and that file does not exist in the checkout. The docs/ directory contains only docs/schema/cheatsheet.schema.json. Readers who follow the link reach a missing page. They cannot find the token list that the Theming section says is documented there.
Either add docs/themes/tokens.md or change the link to a document that exists. Also update the other references to the same path in src/koalakeys/themes/catppuccin.yaml:5 and src/koalakeys/templates/cheatsheets/assets/cheatsheets.css:5, so that they do not point to a missing file.
TODO:
uv run koalakeys