Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clients/cli/yonerai_cli/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ConfigCommandError(Exception):
CONFIG_KEY_CHOICES = (
"language",
"lang",
"theme",
"command_display",
"command_display_mode",
"command_aliases",
Expand Down Expand Up @@ -109,6 +110,7 @@ def format_config_pretty(report: dict[str, Any], *, lang: str = "ja", color: Col
boundary_title = "Boundary"
rows = (
CliRow("language", config.get("language") or "ja", "ok"),
CliRow("theme", config.get("theme") or "auto", "ok"),
CliRow("command_display", config.get("command_display_mode") or "ja_only", "ok"),
CliRow("provider", config.get("provider_preference"), "ok"),
CliRow("model", config.get("model_preference"), "ok"),
Expand Down
6 changes: 6 additions & 0 deletions clients/cli/yonerai_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ def parse_config_value(key: str, value: str) -> object:
if raw not in LANGUAGES:
raise ConfigError("language must be ja or en.")
return raw
if key == "theme":
normalized = raw.lower()
if normalized not in THEMES:
raise ConfigError("theme must be auto, dark, light, or mono.")
Comment on lines +210 to +212

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reuse the theme alias parser for config values

When users follow the Japanese-first theme UI, this new config path still rejects values that the existing theme flows accept, such as yonerai config set theme ダーク or yonerai config set theme 2; the first-launch picker and /テーマ path route through theme_from_input, but this branch only lowercases and checks canonical English names. That leaves the non-interactive theme setter unusable for the same values already exposed by the theme UI, so the value should be normalized through the shared theme alias parser before raising ConfigError.

Useful? React with 👍 / 👎.

return normalized
Comment on lines +209 to +213

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of parse_config_value for "theme" only converts the input to lowercase and checks if it is in THEMES. However, other parts of the CLI (such as the /theme slash command and onboarding) support theme aliases like "々" (e.g., "々" or "々"), Japanese translations like "々", or numbers like "2". To maintain consistency across the CLI and prevent ConfigError when users set these valid aliases via config set theme, we should use theme_from_input to normalize the value.

Suggested change
if key == "theme":
normalized = raw.lower()
if normalized not in THEMES:
raise ConfigError("theme must be auto, dark, light, or mono.")
return normalized
if key == "theme":
from yonerai_cli.tui.themes import theme_from_input
normalized = theme_from_input(raw)
if normalized is None:
raise ConfigError("theme must be auto, dark, light, or mono.")
return normalized

if key == "provider_preference":
if raw not in PROVIDER_PREFERENCES:
raise ConfigError("provider must be auto, mock, local, openai-compatible, anthropic, or gemini.")
Expand Down Expand Up @@ -312,6 +317,7 @@ def build_config_report(config: Mapping[str, object], *, exists: bool) -> dict[s
"secrets_supported": False,
"config": {
"language": validated["language"],
"theme": validated["theme"],
"command_display_mode": validated["command_display_mode"],
"provider_preference": validated["provider_preference"],
"model_preference": validated["model_preference"],
Expand Down
31 changes: 30 additions & 1 deletion tests/test_cli_theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from __future__ import annotations

import io
import json
import sys
from pathlib import Path
from typing import Any
Expand All @@ -21,7 +22,15 @@
if str(path) not in sys.path:
sys.path.insert(0, str(path))

from yonerai_cli.config import DEFAULT_CONFIG, ConfigError, THEMES, save_cli_config, validate_cli_config
from yonerai_cli.config import (
DEFAULT_CONFIG,
THEMES,
ConfigError,
save_cli_config,
set_cli_config_value,
validate_cli_config,
)
from yonerai_cli.commands.config import CONFIG_KEY_CHOICES
from yonerai_cli.startup_home import render_startup_home_header
from yonerai_cli.tui.themes import normalize_theme, theme_from_input, theme_palette, theme_uses_truecolor

Expand Down Expand Up @@ -58,6 +67,26 @@ def test_invalid_theme_rejected() -> None:
validate_cli_config(cfg)


def test_config_setter_persists_theme(tmp_path: Path) -> None:
config_path = tmp_path / "config.json"

updated = set_cli_config_value("theme", "DARK", config_path)

assert updated["theme"] == "dark"
assert json.loads(config_path.read_text(encoding="utf-8"))["theme"] == "dark"
Comment on lines +73 to +76

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since we are supporting theme aliases (like "々" or "2") in the config parser, we should add test assertions to verify that these aliases are correctly resolved and persisted.

Suggested change
updated = set_cli_config_value("theme", "DARK", config_path)
assert updated["theme"] == "dark"
assert json.loads(config_path.read_text(encoding="utf-8"))["theme"] == "dark"
updated = set_cli_config_value("theme", "DARK", config_path)
assert updated["theme"] == "dark"
assert json.loads(config_path.read_text(encoding="utf-8"))["theme"] == "dark"
# Verify alias resolution
updated_alias = set_cli_config_value("theme", "々", config_path)
assert updated_alias["theme"] == "dark"



def test_config_setter_rejects_invalid_theme(tmp_path: Path) -> None:
config_path = tmp_path / "config.json"

with pytest.raises(ConfigError, match="theme must be auto, dark, light, or mono"):
set_cli_config_value("theme", "neon", config_path)


def test_config_command_choices_include_theme() -> None:
assert "theme" in CONFIG_KEY_CHOICES


# --- palette / rendering ---


Expand Down
Loading