diff --git a/cli/pyproject.toml b/cli/pyproject.toml index a234745..d7e1888 100644 --- a/cli/pyproject.toml +++ b/cli/pyproject.toml @@ -9,6 +9,7 @@ version = "0.1.0" dependencies = [ "typer", "pillow", + "jinja2", "materialyoucolor" ] diff --git a/cli/src/zshell/__init__.py b/cli/src/zshell/__init__.py index 886455a..a3ad048 100644 --- a/cli/src/zshell/__init__.py +++ b/cli/src/zshell/__init__.py @@ -8,7 +8,7 @@ app.add_typer(shell.app, name="shell") app.add_typer(scheme.app, name="scheme") app.add_typer(screenshot.app, name="screenshot") app.add_typer(wallpaper.app, name="wallpaper") -# app.add_typer(preset.app, name="preset") +app.add_typer(preset.app, name="preset") def main() -> None: diff --git a/cli/src/zshell/__pycache__/__init__.cpython-314.pyc b/cli/src/zshell/__pycache__/__init__.cpython-314.pyc index 59c8399..97f79ca 100644 Binary files a/cli/src/zshell/__pycache__/__init__.cpython-314.pyc and b/cli/src/zshell/__pycache__/__init__.cpython-314.pyc differ diff --git a/cli/src/zshell/__pycache__/__main__.cpython-314.pyc b/cli/src/zshell/__pycache__/__main__.cpython-314.pyc index bb3ed33..76757e4 100644 Binary files a/cli/src/zshell/__pycache__/__main__.cpython-314.pyc and b/cli/src/zshell/__pycache__/__main__.cpython-314.pyc differ diff --git a/cli/src/zshell/assets/presets/catppuccin/catppuccin.json b/cli/src/zshell/assets/presets/catppuccin/catppuccin.json new file mode 100644 index 0000000..283bba4 --- /dev/null +++ b/cli/src/zshell/assets/presets/catppuccin/catppuccin.json @@ -0,0 +1,544 @@ +{ + "id": "catppuccin", + "name": "Catppuccin", + "version": "1.0.0", + "author": "Catppuccin Org", + "description": "Soothing pastel theme for the high-spirited!", + "dark": {}, + "light": {}, + "variants": { + "type": "multi", + "defaults": { + "dark": { "flavor": "mocha", "accent": "mauve" }, + "light": { "flavor": "latte", "accent": "mauve" } + }, + "flavors": [ + { + "id": "latte", + "name": "Latte", + "light": { + "surface": "#ccd0da", + "surfaceText": "#4c4f69", + "surfaceVariant": "#eff1f5", + "surfaceVariantText": "#6c6f85", + "background": "#eff1f5", + "backgroundText": "#4c4f69", + "outline": "#9ca0b0", + "surfaceContainer": "#eff1f5", + "surfaceContainerHigh": "#e6e9ef", + "surfaceContainerHighest": "#dce0e8", + "error": "#d20f39", + "warning": "#fe640b", + "info": "#1e66f5" + } + }, + { + "id": "frappe", + "name": "Frappé", + "dark": { + "surface": "#414559", + "surfaceText": "#c6d0f5", + "surfaceVariant": "#303446", + "surfaceVariantText": "#a5adce", + "background": "#303446", + "backgroundText": "#c6d0f5", + "outline": "#737994", + "surfaceContainer": "#303446", + "surfaceContainerHigh": "#292c3c", + "surfaceContainerHighest": "#232634", + "error": "#e78284", + "warning": "#ef9f76", + "info": "#8caaee" + } + }, + { + "id": "macchiato", + "name": "Macchiato", + "dark": { + "surface": "#363a4f", + "surfaceText": "#cad3f5", + "surfaceVariant": "#24273a", + "surfaceVariantText": "#a5adcb", + "background": "#24273a", + "backgroundText": "#cad3f5", + "outline": "#6e738d", + "surfaceContainer": "#24273a", + "surfaceContainerHigh": "#1e2030", + "surfaceContainerHighest": "#181926", + "error": "#ed8796", + "warning": "#f5a97f", + "info": "#8aadf4" + } + }, + { + "id": "mocha", + "name": "Mocha", + "dark": { + "surface": "#313244", + "surfaceText": "#cdd6f4", + "surfaceVariant": "#1e1e2e", + "surfaceVariantText": "#a6adc8", + "background": "#1e1e2e", + "backgroundText": "#cdd6f4", + "outline": "#6c7086", + "surfaceContainer": "#1e1e2e", + "surfaceContainerHigh": "#181825", + "surfaceContainerHighest": "#11111b", + "error": "#f38ba8", + "warning": "#fab387", + "info": "#89b4fa" + } + } + ], + "accents": [ + { + "id": "rosewater", + "name": "Rosewater", + "latte": { + "primary": "#dc8a78", + "primaryText": "#eff1f5", + "primaryContainer": "#e1a99d", + "secondary": "#d8c7c4", + "surfaceTint": "#e1a99d" + }, + "frappe": { + "primary": "#f2d5cf", + "primaryText": "#303446", + "primaryContainer": "#b8a5a6", + "secondary": "#a2748b", + "surfaceTint": "#b8a5a6" + }, + "macchiato": { + "primary": "#f4dbd6", + "primaryText": "#24273a", + "primaryContainer": "#b6a6a7", + "secondary": "#9f6f8d", + "surfaceTint": "#b6a6a7" + }, + "mocha": { + "primary": "#f5e0dc", + "primaryText": "#1e1e2e", + "primaryContainer": "#b5a6a8", + "secondary": "#9d6d87", + "surfaceTint": "#b5a6a8" + } + }, + { + "id": "flamingo", + "name": "Flamingo", + "latte": { + "primary": "#dd7878", + "primaryText": "#eff1f5", + "primaryContainer": "#e29c9d", + "secondary": "#d7c3c4", + "surfaceTint": "#e29c9d" + }, + "frappe": { + "primary": "#eebebe", + "primaryText": "#303446", + "primaryContainer": "#b5949a", + "secondary": "#9d6b80", + "surfaceTint": "#b5949a" + }, + "macchiato": { + "primary": "#f0c6c6", + "primaryText": "#24273a", + "primaryContainer": "#b3979c", + "secondary": "#996780", + "surfaceTint": "#b3979c" + }, + "mocha": { + "primary": "#f2cdcd", + "primaryText": "#1e1e2e", + "primaryContainer": "#b3999e", + "secondary": "#98667c", + "surfaceTint": "#b3999e" + } + }, + { + "id": "pink", + "name": "Pink", + "latte": { + "primary": "#ea76cb", + "primaryText": "#eff1f5", + "primaryContainer": "#eb9bd7", + "secondary": "#d9c7d5", + "surfaceTint": "#eb9bd7" + }, + "frappe": { + "primary": "#f4b8e4", + "primaryText": "#303446", + "primaryContainer": "#b990b5", + "secondary": "#996e9e", + "surfaceTint": "#b990b5" + }, + "macchiato": { + "primary": "#f5bde6", + "primaryText": "#24273a", + "primaryContainer": "#b791b2", + "secondary": "#95689a", + "surfaceTint": "#b791b2" + }, + "mocha": { + "primary": "#f5c2e7", + "primaryText": "#1e1e2e", + "primaryContainer": "#b591b0", + "secondary": "#966597", + "surfaceTint": "#b591b0" + } + }, + { + "id": "mauve", + "name": "Mauve", + "latte": { + "primary": "#8839ef", + "primaryText": "#eff1f5", + "primaryContainer": "#a670f1", + "secondary": "#c2b8d0", + "surfaceTint": "#a670f1" + }, + "frappe": { + "primary": "#ca9ee6", + "primaryText": "#303446", + "primaryContainer": "#9c7eb6", + "secondary": "#7d6799", + "surfaceTint": "#9c7eb6" + }, + "macchiato": { + "primary": "#c6a0f6", + "primaryText": "#24273a", + "primaryContainer": "#967cbe", + "secondary": "#766597", + "surfaceTint": "#967cbe" + }, + "mocha": { + "primary": "#cba6f7", + "primaryText": "#1e1e2e", + "primaryContainer": "#977ebb", + "secondary": "#756294", + "surfaceTint": "#977ebb" + } + }, + { + "id": "red", + "name": "Red", + "latte": { + "primary": "#d20f39", + "primaryText": "#eff1f5", + "primaryContainer": "#da5371", + "secondary": "#c0a0a8", + "surfaceTint": "#da5371" + }, + "frappe": { + "primary": "#e78284", + "primaryText": "#303446", + "primaryContainer": "#b06a72", + "secondary": "#8b5d66", + "surfaceTint": "#b06a72" + }, + "macchiato": { + "primary": "#ed8796", + "primaryText": "#24273a", + "primaryContainer": "#b16b7a", + "secondary": "#865a69", + "surfaceTint": "#b16b7a" + }, + "mocha": { + "primary": "#f38ba8", + "primaryText": "#1e1e2e", + "primaryContainer": "#b46b84", + "secondary": "#85596b", + "surfaceTint": "#b46b84" + } + }, + { + "id": "maroon", + "name": "Maroon", + "latte": { + "primary": "#e64553", + "primaryText": "#eff1f5", + "primaryContainer": "#e87883", + "secondary": "#cfb7ba", + "surfaceTint": "#e87883" + }, + "frappe": { + "primary": "#ea999c", + "primaryText": "#303446", + "primaryContainer": "#b27a83", + "secondary": "#92626f", + "surfaceTint": "#b27a83" + }, + "macchiato": { + "primary": "#ee99a0", + "primaryText": "#24273a", + "primaryContainer": "#b27781", + "secondary": "#8c5e6c", + "surfaceTint": "#b27781" + }, + "mocha": { + "primary": "#eba0ac", + "primaryText": "#1e1e2e", + "primaryContainer": "#ae7987", + "secondary": "#895b6c", + "surfaceTint": "#ae7987" + } + }, + { + "id": "peach", + "name": "Peach", + "latte": { + "primary": "#fe640b", + "primaryText": "#eff1f5", + "primaryContainer": "#f98e51", + "secondary": "#c9b7ad", + "surfaceTint": "#f98e51" + }, + "frappe": { + "primary": "#ef9f76", + "primaryText": "#303446", + "primaryContainer": "#b67f68", + "secondary": "#8f6a5f", + "surfaceTint": "#b67f68" + }, + "macchiato": { + "primary": "#f5a97f", + "primaryText": "#24273a", + "primaryContainer": "#b7836a", + "secondary": "#8c695e", + "surfaceTint": "#b7836a" + }, + "mocha": { + "primary": "#fab387", + "primaryText": "#1e1e2e", + "primaryContainer": "#b8876d", + "secondary": "#8b6a5d", + "surfaceTint": "#b8876d" + } + }, + { + "id": "yellow", + "name": "Yellow", + "latte": { + "primary": "#df8e1d", + "primaryText": "#eff1f5", + "primaryContainer": "#e4ac5d", + "secondary": "#c6baaa", + "surfaceTint": "#e4ac5d" + }, + "frappe": { + "primary": "#e5c890", + "primaryText": "#303446", + "primaryContainer": "#af9b7a", + "secondary": "#948062", + "surfaceTint": "#af9b7a" + }, + "macchiato": { + "primary": "#eed49f", + "primaryText": "#24273a", + "primaryContainer": "#b2a181", + "secondary": "#947e62", + "surfaceTint": "#b2a181" + }, + "mocha": { + "primary": "#f9e2af", + "primaryText": "#1e1e2e", + "primaryContainer": "#b8a889", + "secondary": "#978265", + "surfaceTint": "#b8a889" + } + }, + { + "id": "green", + "name": "Green", + "latte": { + "primary": "#40a02b", + "primaryText": "#eff1f5", + "primaryContainer": "#74b867", + "secondary": "#9fbd9b", + "surfaceTint": "#74b867" + }, + "frappe": { + "primary": "#a6d189", + "primaryText": "#303446", + "primaryContainer": "#83a275", + "secondary": "#648e5e", + "surfaceTint": "#83a275" + }, + "macchiato": { + "primary": "#a6da95", + "primaryText": "#24273a", + "primaryContainer": "#80a57a", + "secondary": "#5c8a61", + "surfaceTint": "#80a57a" + }, + "mocha": { + "primary": "#a6e3a1", + "primaryText": "#1e1e2e", + "primaryContainer": "#7ea87f", + "secondary": "#5b8964", + "surfaceTint": "#7ea87f" + } + }, + { + "id": "teal", + "name": "Teal", + "latte": { + "primary": "#179299", + "primaryText": "#eff1f5", + "primaryContainer": "#57aeb4", + "secondary": "#93b4b7", + "surfaceTint": "#57aeb4" + }, + "frappe": { + "primary": "#81c8be", + "primaryText": "#303446", + "primaryContainer": "#699b9a", + "secondary": "#588084", + "surfaceTint": "#699b9a" + }, + "macchiato": { + "primary": "#8bd5ca", + "primaryText": "#24273a", + "primaryContainer": "#6da29f", + "secondary": "#577e83", + "surfaceTint": "#6da29f" + }, + "mocha": { + "primary": "#94e2d5", + "primaryText": "#1e1e2e", + "primaryContainer": "#71a8a4", + "secondary": "#588284", + "surfaceTint": "#71a8a4" + } + }, + { + "id": "sky", + "name": "Sky", + "latte": { + "primary": "#04a5e5", + "primaryText": "#eff1f5", + "primaryContainer": "#4abcea", + "secondary": "#a4b9c2", + "surfaceTint": "#4abcea" + }, + "frappe": { + "primary": "#99d1db", + "primaryText": "#303446", + "primaryContainer": "#79a2af", + "secondary": "#628494", + "surfaceTint": "#79a2af" + }, + "macchiato": { + "primary": "#91d7e3", + "primaryText": "#24273a", + "primaryContainer": "#71a3b0", + "secondary": "#5e7e8c", + "surfaceTint": "#71a3b0" + }, + "mocha": { + "primary": "#89dceb", + "primaryText": "#1e1e2e", + "primaryContainer": "#69a3b3", + "secondary": "#5a7b88", + "surfaceTint": "#69a3b3" + } + }, + { + "id": "sapphire", + "name": "Sapphire", + "latte": { + "primary": "#209fb5", + "primaryText": "#eff1f5", + "primaryContainer": "#5db8c8", + "secondary": "#9eb9be", + "surfaceTint": "#5db8c8" + }, + "frappe": { + "primary": "#85c1dc", + "primaryText": "#303446", + "primaryContainer": "#6b96af", + "secondary": "#5e7b8e", + "surfaceTint": "#6b96af" + }, + "macchiato": { + "primary": "#7dc4e4", + "primaryText": "#24273a", + "primaryContainer": "#6396b1", + "secondary": "#5a7486", + "surfaceTint": "#6396b1" + }, + "mocha": { + "primary": "#74c7ec", + "primaryText": "#1e1e2e", + "primaryContainer": "#5a95b4", + "secondary": "#567080", + "surfaceTint": "#5a95b4" + } + }, + { + "id": "blue", + "name": "Blue", + "latte": { + "primary": "#1e66f5", + "primaryText": "#eff1f5", + "primaryContainer": "#5c90f5", + "secondary": "#b1bacb", + "surfaceTint": "#5c90f5" + }, + "frappe": { + "primary": "#8caaee", + "primaryText": "#303446", + "primaryContainer": "#7086bc", + "secondary": "#637195", + "surfaceTint": "#7086bc" + }, + "macchiato": { + "primary": "#8aadf4", + "primaryText": "#24273a", + "primaryContainer": "#6c85bc", + "secondary": "#5f6d8f", + "surfaceTint": "#6c85bc" + }, + "mocha": { + "primary": "#89b4fa", + "primaryText": "#1e1e2e", + "primaryContainer": "#6987bd", + "secondary": "#5d6c8b", + "surfaceTint": "#6987bd" + } + }, + { + "id": "lavender", + "name": "Lavender", + "latte": { + "primary": "#7287fd", + "primaryText": "#eff1f5", + "primaryContainer": "#97a7fb", + "secondary": "#cdcfdd", + "surfaceTint": "#97a7fb" + }, + "frappe": { + "primary": "#babbf1", + "primaryText": "#303446", + "primaryContainer": "#9192be", + "secondary": "#7175a1", + "surfaceTint": "#9192be" + }, + "macchiato": { + "primary": "#b7bdf8", + "primaryText": "#24273a", + "primaryContainer": "#8b91bf", + "secondary": "#6b709d", + "surfaceTint": "#8b91bf" + }, + "mocha": { + "primary": "#b4befe", + "primaryText": "#1e1e2e", + "primaryContainer": "#878ec0", + "secondary": "#676d99", + "surfaceTint": "#878ec0" + } + } + ] + } +} diff --git a/cli/src/zshell/subcommands/__pycache__/preset.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/preset.cpython-314.pyc new file mode 100644 index 0000000..3b59a24 Binary files /dev/null and b/cli/src/zshell/subcommands/__pycache__/preset.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/scheme.cpython-313.pyc b/cli/src/zshell/subcommands/__pycache__/scheme.cpython-313.pyc deleted file mode 100644 index 71fa4f8..0000000 Binary files a/cli/src/zshell/subcommands/__pycache__/scheme.cpython-313.pyc and /dev/null differ diff --git a/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc index 71fe817..3b93eea 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-313.pyc b/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-313.pyc deleted file mode 100644 index 043fc1d..0000000 Binary files a/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-313.pyc and /dev/null differ diff --git a/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc index 91589d5..c376e62 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/shell.cpython-313.pyc b/cli/src/zshell/subcommands/__pycache__/shell.cpython-313.pyc deleted file mode 100644 index af12c92..0000000 Binary files a/cli/src/zshell/subcommands/__pycache__/shell.cpython-313.pyc and /dev/null differ diff --git a/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc index f3b6e65..8f053bd 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-313.pyc b/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-313.pyc deleted file mode 100644 index e70c525..0000000 Binary files a/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-313.pyc and /dev/null differ diff --git a/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc index 9ec1eec..0d1d2f4 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/preset.py b/cli/src/zshell/subcommands/preset.py index ee685b3..4feb6f3 100644 --- a/cli/src/zshell/subcommands/preset.py +++ b/cli/src/zshell/subcommands/preset.py @@ -1,11 +1,60 @@ +from __future__ import annotations + import json +import os +from pathlib import Path + import typer -from zshell.assets.schemes.catppuccin import catppuccin + +from zshell.utils.presets import build_palette, list_presets, load_preset_file app = typer.Typer() +OUTPUT = Path(os.getenv("HOME", "")) / ".local/state/zshell/scheme.json" -SCHEMES = catppuccin.variants.flavors + +def _split_preset(value: str) -> tuple[str, str | None, str | None]: + parts = value.split(":") + if len(parts) == 1: + return parts[0], None, None + if len(parts) == 3: + return parts[0], parts[1], parts[2] + raise typer.BadParameter("Preset must be or ::") @app.command() -def set(): +def list(): + for preset_name in list_presets(): + data = load_preset_file(preset_name) + flavors = [item["id"] for item in data["variants"]["flavors"]] + accents = [item["id"] for item in data["variants"]["accents"]] + for flavor in flavors: + for accent in accents: + typer.echo(f"{preset_name}:{flavor}:{accent}") + + +@app.command() +def set( + preset: str = typer.Argument(..., help="Preset in form or ::"), + mode: str = typer.Option("dark", help="Mode for preset colors."), +): + name, flavor, accent = _split_preset(preset) + data = load_preset_file(name) + + if flavor is None or accent is None: + defaults = data["variants"]["defaults"][mode] + flavor = defaults["flavor"] + accent = defaults["accent"] + + palette = build_palette(data, flavor, accent, mode) + out = { + "name": palette.name, + "flavor": f"{palette.flavor}:{palette.accent}", + "mode": palette.mode, + "variant": name, + "colors": dict(palette.colors), + "seed": int(palette.colors["primary"].lstrip("#"), 16), + } + + OUTPUT.parent.mkdir(parents=True, exist_ok=True) + OUTPUT.write_text(json.dumps(out, indent=4), encoding="utf-8") + typer.echo(f"applied: {name}:{flavor}:{accent}") diff --git a/cli/src/zshell/utils/__pycache__/presets.cpython-314.pyc b/cli/src/zshell/utils/__pycache__/presets.cpython-314.pyc new file mode 100644 index 0000000..1094349 Binary files /dev/null and b/cli/src/zshell/utils/__pycache__/presets.cpython-314.pyc differ diff --git a/cli/src/zshell/utils/__pycache__/schemepalettes.cpython-313.pyc b/cli/src/zshell/utils/__pycache__/schemepalettes.cpython-313.pyc deleted file mode 100644 index 4f3c04b..0000000 Binary files a/cli/src/zshell/utils/__pycache__/schemepalettes.cpython-313.pyc and /dev/null differ diff --git a/cli/src/zshell/utils/__pycache__/schemepalettes.cpython-314.pyc b/cli/src/zshell/utils/__pycache__/schemepalettes.cpython-314.pyc new file mode 100644 index 0000000..a6c8356 Binary files /dev/null and b/cli/src/zshell/utils/__pycache__/schemepalettes.cpython-314.pyc differ diff --git a/cli/src/zshell/utils/presets.py b/cli/src/zshell/utils/presets.py new file mode 100644 index 0000000..2041527 --- /dev/null +++ b/cli/src/zshell/utils/presets.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import json +from dataclasses import dataclass +from pathlib import Path +from typing import Any + + +@dataclass(frozen=True) +class PresetPalette: + name: str + flavor: str + accent: str + mode: str + colors: dict[str, str] + + +PRESET_ROOT = Path(__file__).resolve().parent.parent / "assets" / "presets" + + +def load_preset_file(name: str) -> dict[str, Any]: + path = PRESET_ROOT / name / f"{name}.json" + if not path.exists(): + raise FileNotFoundError(path) + return json.loads(path.read_text(encoding="utf-8")) + + +def build_palette(data: dict[str, Any], flavor: str, accent: str, mode: str) -> PresetPalette: + flavor_entry = next(item for item in data["variants"]["flavors"] if item["id"] == flavor) + accent_entry = next(item for item in data["variants"]["accents"] if item["id"] == accent) + flavor_block = flavor_entry.get(mode, {}) + accent_block = accent_entry.get(flavor, {}) + + colors = { + "primary": accent_block.get("primary", "#000000"), + "secondary": accent_block.get("secondary", accent_block.get("primary", "#000000")), + "tertiary": accent_block.get("primaryContainer", accent_block.get("surfaceTint", accent_block.get("primary", "#000000"))), + "neutral": flavor_block.get("background", "#000000"), + "neutral_variant": flavor_block.get("surfaceVariant", flavor_block.get("outline", flavor_block.get("background", "#000000"))), + } + + return PresetPalette( + name=data.get("name", "preset"), + flavor=flavor, + accent=accent, + mode=mode, + colors=colors, + ) + + +def list_presets() -> list[str]: + out: list[str] = [] + if not PRESET_ROOT.exists(): + return out + + for preset_dir in sorted(p for p in PRESET_ROOT.iterdir() if p.is_dir()): + json_path = preset_dir / f"{preset_dir.name}.json" + if json_path.exists(): + out.append(preset_dir.name) + + return out