diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-08-07 20:59:32 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-07 20:59:32 +0300 |
| commit | bdb5222eec9109f4a337ab79d12c3f3ee3cd7a4b (patch) | |
| tree | 1081a0036513199d17b35f8de9e43c9841c7be36 /src/config | |
| parent | 00f04ac65cedf882f9785c0aa20831523c554dd0 (diff) | |
fix(config): report unknown theme names (#2488)
refs #2452
Diffstat (limited to 'src/config')
| -rw-r--r-- | src/config/io.rs | 16 | ||||
| -rw-r--r-- | src/config/theme.rs | 98 |
2 files changed, 114 insertions, 0 deletions
diff --git a/src/config/io.rs b/src/config/io.rs index b6a70427..7ed82395 100644 --- a/src/config/io.rs +++ b/src/config/io.rs @@ -341,6 +341,8 @@ fn load_live_config_from_str(content: &str) -> Result<LoadedConfig, Vec<String>> |section| config.remote = section, ); + diagnostics.extend(config.theme.diagnostics()); + Ok(LoadedConfig { config, diagnostics, @@ -864,6 +866,20 @@ resume_agents_on_restore = true } #[test] + fn load_live_config_warns_about_unknown_theme_names() { + let loaded = load_live_config_from_str( + r#" +[theme] +name = "catppucin" +"#, + ) + .unwrap(); + + assert_eq!(loaded.diagnostics.len(), 1); + assert!(loaded.diagnostics[0].contains("theme.name = \"catppucin\"")); + } + + #[test] fn load_live_config_warns_about_unknown_top_level_sections() { let loaded = load_live_config_from_str( r#" diff --git a/src/config/theme.rs b/src/config/theme.rs index e98c9dfb..e89fe99c 100644 --- a/src/config/theme.rs +++ b/src/config/theme.rs @@ -1,6 +1,51 @@ use serde::Deserialize; use tracing::warn; +pub const THEME_NAMES: &[&str] = &[ + "catppuccin", + "catppuccin-latte", + "terminal", + "tokyo-night", + "tokyo-night-day", + "dracula", + "nord", + "gruvbox", + "gruvbox-light", + "one-dark", + "one-light", + "solarized", + "solarized-light", + "kanagawa", + "kanagawa-lotus", + "rose-pine", + "rose-pine-dawn", + "vesper", +]; + +pub(crate) fn canonical_theme_name(name: &str) -> Option<&'static str> { + match name.to_lowercase().replace([' ', '_'], "-").as_str() { + "catppuccin" | "catppuccin-mocha" => Some("catppuccin"), + "catppuccin-latte" | "latte" | "light" => Some("catppuccin-latte"), + "terminal" => Some("terminal"), + "tokyo-night" | "tokyonight" => Some("tokyo-night"), + "tokyo-night-day" | "tokyo-day" | "tokyonight-day" => Some("tokyo-night-day"), + "dracula" => Some("dracula"), + "nord" => Some("nord"), + "gruvbox" | "gruvbox-dark" => Some("gruvbox"), + "gruvbox-light" => Some("gruvbox-light"), + "one-dark" | "onedark" => Some("one-dark"), + "one-light" | "onelight" => Some("one-light"), + "solarized" | "solarized-dark" => Some("solarized"), + "solarized-light" => Some("solarized-light"), + "kanagawa" => Some("kanagawa"), + "kanagawa-lotus" | "lotus" => Some("kanagawa-lotus"), + "rose-pine" | "rosepine" => Some("rose-pine"), + "rose-pine-dawn" | "rosepine-dawn" | "dawn" => Some("rose-pine-dawn"), + "vesper" => Some("vesper"), + _ => None, + } +} + /// Theme configuration: pick a built-in or override individual tokens. /// /// ```toml @@ -26,6 +71,31 @@ pub struct ThemeConfig { pub custom: Option<CustomThemeColors>, } +impl ThemeConfig { + pub(crate) fn diagnostics(&self) -> Vec<String> { + let valid = THEME_NAMES.join(", "); + [ + ("theme.name", self.name.as_deref(), "catppuccin"), + ("theme.dark_name", self.dark_name.as_deref(), "catppuccin"), + ( + "theme.light_name", + self.light_name.as_deref(), + "catppuccin-latte", + ), + ] + .into_iter() + .filter_map(|(field, value, fallback)| { + let value = value?; + canonical_theme_name(value).is_none().then(|| { + format!( + "unknown theme name {field} = {value:?}; using {fallback:?}; valid themes: {valid}" + ) + }) + }) + .collect() + } +} + /// Per-token color overrides. All fields optional — only set what you want to change. #[derive(Debug, Clone, Default, Deserialize)] #[serde(default)] @@ -133,6 +203,34 @@ name = "dracula" } #[test] + fn unknown_theme_names_are_diagnosed() { + let config: Config = toml::from_str( + r#" +[theme] +name = "catppucin" +dark_name = "tokio-night" +light_name = "lattee" +"#, + ) + .unwrap(); + + let diagnostics = config.theme.diagnostics(); + assert_eq!(diagnostics.len(), 3); + assert!(diagnostics[0].contains("theme.name = \"catppucin\"")); + assert!(diagnostics[0].contains("using \"catppuccin\"")); + assert!(diagnostics[1].contains("theme.dark_name = \"tokio-night\"")); + assert!(diagnostics[2].contains("theme.light_name = \"lattee\"")); + assert!(diagnostics[2].contains("using \"catppuccin-latte\"")); + } + + #[test] + fn theme_name_aliases_are_valid() { + for name in ["catppuccin-mocha", "tokyonight", "gruvbox-dark", "dawn"] { + assert!(canonical_theme_name(name).is_some(), "alias: {name}"); + } + } + + #[test] fn parse_color_accepts_reset_aliases() { use ratatui::style::Color; |
