diff --git a/cli/src/zshell/subcommands/scheme.py b/cli/src/zshell/subcommands/scheme.py index 7bdba21..ef2a303 100644 --- a/cli/src/zshell/subcommands/scheme.py +++ b/cli/src/zshell/subcommands/scheme.py @@ -21,18 +21,36 @@ app = typer.Typer() @app.command() -def list_presets(): +def list_presets( + json_format: bool = typer.Option(False, "--json", help="Output in JSON format"), +): schemes = list_schemes() - for sid, meta in sorted(schemes.items()): - var_list = [] - for v in meta.variants: - parts = [f"{v.id} ({', '.join(sorted(v.modes))})"] - if v.accents: - parts.append(f"accents: {', '.join(v.accents)}") - var_list.append(" | ".join(parts)) - print(f"{meta.name} ({sid})") - print(f" Variants: {', '.join(var_list)}") - print() + if json_format: + out = {} + for sid, meta in sorted(schemes.items()): + variants = {} + for v in meta.variants: + entry = {"modes": sorted(v.modes)} + if v.accents: + entry["accents"] = sorted(v.accents) + entry["default_accent"] = sorted(v.accents)[0] + variants[v.id] = entry + out[meta.name] = { + "id": sid, + "variants": variants, + } + print(json.dumps({"presets": out}, indent=2)) + else: + for sid, meta in sorted(schemes.items()): + var_list = [] + for v in meta.variants: + parts = [f"{v.id} ({', '.join(sorted(v.modes))})"] + if v.accents: + parts.append(f"accents: {', '.join(v.accents)}") + var_list.append(" | ".join(parts)) + print(f"{meta.name} ({sid})") + print(f" Variants: {', '.join(var_list)}") + print() @app.command() @@ -41,10 +59,9 @@ def generate( scheme: Optional[str] = typer.Option( None, help="Color scheme algorithm to use for image mode. Ignored in preset mode." ), - preset: Optional[str] = typer.Option( - None, help="Name of a premade scheme in this format: :[:]" - ), + preset: Optional[str] = typer.Option(None, help="Name of a premade scheme in this format: :"), mode: Optional[str] = typer.Option(None, help="Mode of the preset scheme (dark or light)."), + accent: Optional[str] = typer.Option(None, help="Accent for schemes that support it (e.g. mauve)."), ): HOME = str(os.getenv("HOME")) @@ -473,8 +490,17 @@ def generate( scheme_class = get_scheme_class(scheme) if preset: - p_scheme, p_variant, p_accent = resolve_preset(preset) - palette_obj = get_palette(p_scheme, p_variant, mode or config_mode, accent=p_accent) + p_scheme, p_variant = resolve_preset(preset) + schemes = list_schemes() + if accent and p_scheme in schemes: + meta = schemes[p_scheme] + var_accents = next((v.accents for v in meta.variants if v.id == p_variant), ()) + if accent not in var_accents: + available = ", ".join(var_accents) if var_accents else "none" + raise typer.BadParameter( + f"Accent '{accent}' not available for '{p_scheme}:{p_variant}'. Available accents: {available}" + ) + palette_obj = get_palette(p_scheme, p_variant, mode or config_mode, accent=accent) colors = palette_obj.colors effective_mode = palette_obj.mode name = palette_obj.scheme diff --git a/cli/src/zshell/utils/schemepalettes.py b/cli/src/zshell/utils/schemepalettes.py index 2e21775..0fc136c 100644 --- a/cli/src/zshell/utils/schemepalettes.py +++ b/cli/src/zshell/utils/schemepalettes.py @@ -132,12 +132,10 @@ def list_schemes() -> dict[str, SchemeMeta]: return dict(SCHEMES) -def resolve_preset(spec: str) -> tuple[str, str, str | None]: +def resolve_preset(spec: str) -> tuple[str, str]: parts = spec.split(":") - if len(parts) == 3: - return parts[0], parts[1], parts[2] if len(parts) == 2: - return parts[0], parts[1], None + return parts[0], parts[1] if len(parts) == 1: - return parts[0], "default", None - raise ValueError(f"Invalid preset spec '{spec}'. Use :[:]") + return parts[0], "default" + raise ValueError(f"Invalid preset spec '{spec}'. Use :") diff --git a/cli/tests/test_schemepalettes.py b/cli/tests/test_schemepalettes.py index 8a4ff7b..ac4fa67 100644 --- a/cli/tests/test_schemepalettes.py +++ b/cli/tests/test_schemepalettes.py @@ -154,13 +154,14 @@ class TestListSchemes: class TestResolvePreset: def test_two_parts(self): - assert sp.resolve_preset("gruvbox:medium") == ("gruvbox", "medium", None) + assert sp.resolve_preset("gruvbox:medium") == ("gruvbox", "medium") def test_three_parts(self): - assert sp.resolve_preset("catppuccin:mocha:mauve") == ("catppuccin", "mocha", "mauve") + with pytest.raises(ValueError, match="Invalid preset spec"): + sp.resolve_preset("catppuccin:mocha:mauve") def test_one_part(self): - assert sp.resolve_preset("default") == ("default", "default", None) + assert sp.resolve_preset("default") == ("default", "default") def test_edge_spaces(self): - assert sp.resolve_preset(" catppuccin : mocha : mauve ") == (" catppuccin ", " mocha ", " mauve ") + assert sp.resolve_preset(" catppuccin : mocha ") == (" catppuccin ", " mocha ")