Merge pull request #2270 from AiurArtanis/feat/theme-solarized-light

feat(theme): add Solarized Light theme
This commit is contained in:
Hunter Bown
2026-05-31 00:38:08 -07:00
committed by GitHub
3 changed files with 320 additions and 18 deletions
+27
View File
@@ -114,6 +114,32 @@ impl Theme {
}
}
/// Solarized Light theme tokens — warm ivory tones, high contrast.
#[must_use]
pub const fn solarized_light() -> Self {
Self {
variant: Variant::Light,
section_borders: Borders::ALL,
section_border_type: BorderType::Plain,
section_border_color: palette::SOLARIZED_BORDER,
section_bg: palette::SOLARIZED_PANEL,
section_title_color: palette::SOLARIZED_BLUE,
section_padding: Padding::horizontal(1),
tool_title_color: palette::SOLARIZED_TEXT_SOFT,
tool_value_color: palette::SOLARIZED_TEXT_MUTED,
tool_label_color: palette::SOLARIZED_TEXT_DIM,
tool_running_accent: palette::SOLARIZED_BLUE,
tool_success_accent: palette::SOLARIZED_CYAN,
tool_failed_accent: palette::SOLARIZED_RED,
plan_progress_color: palette::SOLARIZED_BLUE,
plan_summary_color: palette::SOLARIZED_TEXT_MUTED,
plan_explanation_color: palette::SOLARIZED_TEXT_DIM,
plan_pending_color: palette::SOLARIZED_TEXT_MUTED,
plan_in_progress_color: palette::SOLARIZED_ORANGE,
plan_completed_color: palette::SOLARIZED_BLUE,
}
}
/// Neutral black/white tokens for users who want minimal brand color.
#[must_use]
pub const fn grayscale() -> Self {
@@ -146,6 +172,7 @@ impl Theme {
PaletteMode::Dark => Self::dark(),
PaletteMode::Light => Self::light(),
PaletteMode::Grayscale => Self::grayscale(),
PaletteMode::SolarizedLight => Self::solarized_light(),
}
}
+279 -9
View File
@@ -30,6 +30,27 @@ pub const WHALE_BORDER_RGB: (u8, u8, u8) = (52, 88, 145); // #345891
pub const WHALE_REASONING_TEXT_RGB: (u8, u8, u8) = (224, 153, 72); // #E09948
pub const WHALE_REASONING_SURFACE_RGB: (u8, u8, u8) = (42, 34, 24); // #2A2218
pub const WHALE_REASONING_TINT_RGB: (u8, u8, u8) = (24, 36, 52); // #182434
// Solarized Light palette RGB tuples
pub const SOLARIZED_BASE03_RGB: (u8, u8, u8) = (0x00, 0x2B, 0x36);
pub const SOLARIZED_BASE02_RGB: (u8, u8, u8) = (0x07, 0x36, 0x42);
pub const SOLARIZED_BASE01_RGB: (u8, u8, u8) = (0x58, 0x6E, 0x75);
pub const SOLARIZED_BASE00_RGB: (u8, u8, u8) = (0x65, 0x7B, 0x83);
pub const SOLARIZED_BASE0_RGB: (u8, u8, u8) = (0x83, 0x94, 0x96);
pub const SOLARIZED_BASE1_RGB: (u8, u8, u8) = (0x93, 0xA1, 0xA1);
#[allow(dead_code)]
pub const SOLARIZED_BASE2_RGB: (u8, u8, u8) = (0xEE, 0xE8, 0xD5);
pub const SOLARIZED_BASE3_RGB: (u8, u8, u8) = (0xFD, 0xF6, 0xE3);
pub const SOLARIZED_YELLOW_RGB: (u8, u8, u8) = (0xB5, 0x89, 0x00);
pub const SOLARIZED_ORANGE_RGB: (u8, u8, u8) = (0xCB, 0x4B, 0x16);
pub const SOLARIZED_RED_RGB: (u8, u8, u8) = (0xDC, 0x32, 0x2F);
pub const SOLARIZED_BLUE_RGB: (u8, u8, u8) = (0x26, 0x8B, 0xD2);
pub const SOLARIZED_CYAN_RGB: (u8, u8, u8) = (0x2A, 0xA1, 0x98);
pub const SOLARIZED_GREEN_RGB: (u8, u8, u8) = (0x85, 0x99, 0x00);
pub const SOLARIZED_PANEL_RGB: (u8, u8, u8) = (0xF0, 0xED, 0xE7);
pub const SOLARIZED_ELEVATED_RGB: (u8, u8, u8) = (0xE4, 0xDF, 0xCF);
pub const SOLARIZED_SELECT_RGB: (u8, u8, u8) = (0xD6, 0xD2, 0xC9);
pub const WHALE_DIFF_ADDED_RGB: (u8, u8, u8) = (87, 199, 133); // #57C785
#[allow(dead_code)]
pub const WHALE_DIFF_DELETED_RGB: (u8, u8, u8) = (255, 92, 122); // #FF5C7A Rose Red
@@ -66,6 +87,100 @@ pub const LIGHT_TEXT_BODY_RGB: (u8, u8, u8) = (15, 23, 42); // #0F172A
pub const LIGHT_TEXT_MUTED_RGB: (u8, u8, u8) = (51, 65, 85); // #334155
pub const LIGHT_TEXT_HINT_RGB: (u8, u8, u8) = (100, 116, 139); // #64748B
pub const LIGHT_TEXT_SOFT_RGB: (u8, u8, u8) = (30, 41, 59); // #1E293B
// Solarized Light palette colors
pub const SOLARIZED_TEXT_DIM: Color = Color::Rgb(
SOLARIZED_BASE00_RGB.0,
SOLARIZED_BASE00_RGB.1,
SOLARIZED_BASE00_RGB.2,
);
pub const SOLARIZED_TEXT_HINT: Color = Color::Rgb(
SOLARIZED_BASE0_RGB.0,
SOLARIZED_BASE0_RGB.1,
SOLARIZED_BASE0_RGB.2,
);
pub const SOLARIZED_TEXT_MUTED: Color = Color::Rgb(
SOLARIZED_BASE01_RGB.0,
SOLARIZED_BASE01_RGB.1,
SOLARIZED_BASE01_RGB.2,
);
pub const SOLARIZED_TEXT_BODY: Color = Color::Rgb(
SOLARIZED_BASE03_RGB.0,
SOLARIZED_BASE03_RGB.1,
SOLARIZED_BASE03_RGB.2,
);
pub const SOLARIZED_TEXT_SOFT: Color = Color::Rgb(
SOLARIZED_BASE02_RGB.0,
SOLARIZED_BASE02_RGB.1,
SOLARIZED_BASE02_RGB.2,
);
pub const SOLARIZED_BORDER: Color = Color::Rgb(
SOLARIZED_BASE1_RGB.0,
SOLARIZED_BASE1_RGB.1,
SOLARIZED_BASE1_RGB.2,
);
pub const SOLARIZED_BLUE: Color = Color::Rgb(
SOLARIZED_BLUE_RGB.0,
SOLARIZED_BLUE_RGB.1,
SOLARIZED_BLUE_RGB.2,
);
pub const SOLARIZED_CYAN: Color = Color::Rgb(
SOLARIZED_CYAN_RGB.0,
SOLARIZED_CYAN_RGB.1,
SOLARIZED_CYAN_RGB.2,
);
pub const SOLARIZED_RED: Color = Color::Rgb(
SOLARIZED_RED_RGB.0,
SOLARIZED_RED_RGB.1,
SOLARIZED_RED_RGB.2,
);
pub const SOLARIZED_ORANGE: Color = Color::Rgb(
SOLARIZED_ORANGE_RGB.0,
SOLARIZED_ORANGE_RGB.1,
SOLARIZED_ORANGE_RGB.2,
);
pub const SOLARIZED_YELLOW: Color = Color::Rgb(
SOLARIZED_YELLOW_RGB.0,
SOLARIZED_YELLOW_RGB.1,
SOLARIZED_YELLOW_RGB.2,
);
pub const SOLARIZED_GREEN: Color = Color::Rgb(
SOLARIZED_GREEN_RGB.0,
SOLARIZED_GREEN_RGB.1,
SOLARIZED_GREEN_RGB.2,
);
pub const SOLARIZED_SURFACE: Color = Color::Rgb(
SOLARIZED_BASE3_RGB.0,
SOLARIZED_BASE3_RGB.1,
SOLARIZED_BASE3_RGB.2,
);
pub const SOLARIZED_PANEL: Color = Color::Rgb(
SOLARIZED_PANEL_RGB.0,
SOLARIZED_PANEL_RGB.1,
SOLARIZED_PANEL_RGB.2,
);
pub const SOLARIZED_ELEVATED: Color = Color::Rgb(
SOLARIZED_ELEVATED_RGB.0,
SOLARIZED_ELEVATED_RGB.1,
SOLARIZED_ELEVATED_RGB.2,
);
pub const SOLARIZED_SELECT_BG: Color = Color::Rgb(
SOLARIZED_SELECT_RGB.0,
SOLARIZED_SELECT_RGB.1,
SOLARIZED_SELECT_RGB.2,
);
pub const SOLARIZED_DIFF_ADDED_BG: Color = Color::Rgb(0xEA, 0xF2, 0xE0);
pub const SOLARIZED_ERROR_SURFACE: Color = Color::Rgb(0xFD, 0xEE, 0xEB);
/// Same tone as the error surface; kept as a distinct alias for diff context.
pub const SOLARIZED_DIFF_DELETED_BG: Color = SOLARIZED_ERROR_SURFACE;
pub const SOLARIZED_ERROR_TEXT: Color = Color::Rgb(0x8B, 0x00, 0x00);
pub const SOLARIZED_ERROR_HOVER: Color = Color::Rgb(0xE0, 0x55, 0x52);
pub const SOLARIZED_COMPOSER: Color = Color::Rgb(
SOLARIZED_PANEL_RGB.0,
SOLARIZED_PANEL_RGB.1,
SOLARIZED_PANEL_RGB.2,
);
pub const LIGHT_BORDER_RGB: (u8, u8, u8) = (139, 161, 184); // #8BA1B8
pub const LIGHT_SELECTION_RGB: (u8, u8, u8) = (207, 224, 247); // #CFE0F7
pub const GRAYSCALE_SURFACE_RGB: (u8, u8, u8) = (10, 10, 10); // #0A0A0A
@@ -423,6 +538,7 @@ pub enum PaletteMode {
Dark,
Light,
Grayscale,
SolarizedLight,
}
impl PaletteMode {
@@ -663,6 +779,49 @@ pub const LIGHT_UI_THEME: UiTheme = UiTheme {
tool_failed: Color::Rgb(200, 40, 60), // red
};
pub const SOLARIZED_LIGHT_UI_THEME: UiTheme = UiTheme {
name: "solarized-light",
mode: PaletteMode::SolarizedLight,
surface_bg: SOLARIZED_SURFACE,
panel_bg: SOLARIZED_PANEL,
elevated_bg: SOLARIZED_ELEVATED,
composer_bg: SOLARIZED_COMPOSER,
selection_bg: SOLARIZED_SELECT_BG,
header_bg: SOLARIZED_SURFACE,
footer_bg: SOLARIZED_SURFACE,
text_dim: SOLARIZED_TEXT_DIM,
text_hint: SOLARIZED_TEXT_HINT,
text_muted: SOLARIZED_TEXT_MUTED,
text_body: SOLARIZED_TEXT_BODY,
text_soft: SOLARIZED_TEXT_SOFT,
border: SOLARIZED_BORDER,
accent_primary: SOLARIZED_BLUE,
accent_secondary: SOLARIZED_CYAN,
accent_action: SOLARIZED_ORANGE,
error_fg: SOLARIZED_RED,
error_hover: SOLARIZED_ERROR_HOVER,
error_surface: SOLARIZED_ERROR_SURFACE,
error_border: SOLARIZED_RED,
error_text: SOLARIZED_ERROR_TEXT,
warning: SOLARIZED_YELLOW,
success: SOLARIZED_GREEN,
info: SOLARIZED_BLUE,
mode_agent: SOLARIZED_BLUE,
mode_yolo: SOLARIZED_RED,
mode_plan: SOLARIZED_ORANGE,
mode_goal: SOLARIZED_GREEN,
status_ready: SOLARIZED_CYAN,
status_working: SOLARIZED_BLUE,
status_warning: SOLARIZED_YELLOW,
diff_added_fg: SOLARIZED_GREEN,
diff_deleted_fg: SOLARIZED_RED,
diff_added_bg: SOLARIZED_DIFF_ADDED_BG,
diff_deleted_bg: SOLARIZED_DIFF_DELETED_BG,
tool_running: SOLARIZED_BLUE,
tool_success: SOLARIZED_CYAN,
tool_failed: SOLARIZED_RED,
};
pub const GRAYSCALE_UI_THEME: UiTheme = UiTheme {
name: "grayscale",
mode: PaletteMode::Grayscale,
@@ -1053,6 +1212,7 @@ pub enum ThemeId {
Dracula,
GruvboxDark,
Matrix,
SolarizedLight,
}
impl ThemeId {
@@ -1072,6 +1232,7 @@ impl ThemeId {
"dracula" => Some(Self::Dracula),
"gruvbox-dark" => Some(Self::GruvboxDark),
"matrix" => Some(Self::Matrix),
"solarized-light" => Some(Self::SolarizedLight),
_ => None,
}
}
@@ -1091,6 +1252,7 @@ impl ThemeId {
Self::Dracula => "dracula",
Self::GruvboxDark => "gruvbox-dark",
Self::Matrix => "matrix",
Self::SolarizedLight => "solarized-light",
}
}
@@ -1108,6 +1270,7 @@ impl ThemeId {
Self::Dracula => "Dracula",
Self::GruvboxDark => "Gruvbox Dark",
Self::Matrix => "Matrix",
Self::SolarizedLight => "Solarized Light",
}
}
@@ -1125,6 +1288,9 @@ impl ThemeId {
Self::Dracula => "Classic high-contrast purple",
Self::GruvboxDark => "Vintage warm earth tones",
Self::Matrix => "The Matrix films inspired theme",
Self::SolarizedLight => {
"Solarized light — Light, calming palette on warm ivory — easy on the eyes"
}
}
}
@@ -1145,6 +1311,7 @@ impl ThemeId {
Self::Dracula => DRACULA_UI_THEME,
Self::GruvboxDark => GRUVBOX_DARK_UI_THEME,
Self::Matrix => MATRIX_UI_THEME,
Self::SolarizedLight => SOLARIZED_LIGHT_UI_THEME,
}
}
}
@@ -1161,6 +1328,7 @@ pub const SELECTABLE_THEMES: &[ThemeId] = &[
ThemeId::Dracula,
ThemeId::GruvboxDark,
ThemeId::Matrix,
ThemeId::SolarizedLight,
];
impl UiTheme {
@@ -1170,6 +1338,7 @@ impl UiTheme {
PaletteMode::Dark => UI_THEME,
PaletteMode::Light => LIGHT_UI_THEME,
PaletteMode::Grayscale => GRAYSCALE_UI_THEME,
PaletteMode::SolarizedLight => SOLARIZED_LIGHT_UI_THEME,
}
}
@@ -1206,6 +1375,7 @@ pub fn normalize_theme_name(value: &str) -> Option<&'static str> {
"dracula" => Some("dracula"),
"gruvbox-dark" | "gruvbox" => Some("gruvbox-dark"),
"matrix" | "hacker" => Some("matrix"),
"solarized-light" | "solarized" => Some("solarized-light"),
_ => None,
}
}
@@ -1216,6 +1386,7 @@ pub fn theme_label_for_mode(mode: PaletteMode) -> &'static str {
PaletteMode::Dark => "dark",
PaletteMode::Light => "light",
PaletteMode::Grayscale => "grayscale",
PaletteMode::SolarizedLight => "solarized-light",
}
}
@@ -1260,6 +1431,7 @@ pub fn adapt_fg_for_palette_mode(color: Color, _bg: Color, mode: PaletteMode) ->
PaletteMode::Dark => color,
PaletteMode::Light => adapt_fg_for_light_palette(color),
PaletteMode::Grayscale => adapt_fg_for_grayscale_palette(color),
PaletteMode::SolarizedLight => adapt_fg_for_solarized_light_palette(color),
}
}
@@ -1269,6 +1441,7 @@ pub fn adapt_bg_for_palette_mode(color: Color, mode: PaletteMode) -> Color {
PaletteMode::Dark => color,
PaletteMode::Light => adapt_bg_for_light_palette(color),
PaletteMode::Grayscale => adapt_bg_for_grayscale_palette(color),
PaletteMode::SolarizedLight => adapt_bg_for_solarized_light_palette(color),
}
}
@@ -1329,6 +1502,59 @@ fn adapt_bg_for_light_palette(color: Color) -> Color {
}
}
fn adapt_fg_for_solarized_light_palette(color: Color) -> Color {
if color == TEXT_BODY || color == SELECTION_TEXT || color == Color::White {
SOLARIZED_TEXT_BODY
} else if color == TEXT_SECONDARY || color == TEXT_MUTED {
SOLARIZED_TEXT_MUTED
} else if color == TEXT_HINT || color == TEXT_DIM {
SOLARIZED_TEXT_HINT
} else if color == TEXT_SOFT || color == TEXT_TOOL_OUTPUT {
SOLARIZED_TEXT_SOFT
} else if color == BORDER_COLOR {
SOLARIZED_BORDER
} else if color == TEXT_ACCENT || color == DEEPSEEK_SKY || color == ACCENT_TOOL_LIVE {
SOLARIZED_BLUE
} else if color == TEXT_REASONING || color == ACCENT_REASONING_LIVE {
SOLARIZED_ORANGE
} else if color == ACCENT_TOOL_ISSUE {
SOLARIZED_RED
} else if color == DIFF_ADDED || color == USER_BODY {
SOLARIZED_GREEN
} else {
color
}
}
fn adapt_bg_for_solarized_light_palette(color: Color) -> Color {
if color == DEEPSEEK_INK || color == BACKGROUND_DARK {
SOLARIZED_SURFACE
} else if color == DEEPSEEK_SLATE
|| color == COMPOSER_BG
|| color == SURFACE_PANEL
|| color == SURFACE_TOOL
{
SOLARIZED_PANEL
} else if color == SURFACE_ELEVATED || color == SURFACE_TOOL_ACTIVE {
SOLARIZED_ELEVATED
} else if color == SURFACE_REASONING
|| color == SURFACE_REASONING_TINT
|| color == SURFACE_REASONING_ACTIVE
{
SOLARIZED_PANEL
} else if color == SURFACE_SUCCESS || color == DIFF_ADDED_BG {
SOLARIZED_DIFF_ADDED_BG
} else if color == SURFACE_ERROR {
SOLARIZED_ERROR_SURFACE
} else if color == DIFF_DELETED_BG {
SOLARIZED_DIFF_DELETED_BG
} else if color == SELECTION_BG {
SOLARIZED_SELECT_BG
} else {
color
}
}
// === Community-theme remap ===
//
// The vast majority of render sites in this crate reach for `palette::TEXT_*`,
@@ -1381,6 +1607,7 @@ pub const fn theme_remap_active(theme: ThemeId) -> bool {
| ThemeId::Dracula
| ThemeId::GruvboxDark
| ThemeId::Matrix
| ThemeId::SolarizedLight
)
}
@@ -1875,14 +2102,15 @@ mod tests {
DEEPSEEK_SLATE, DIFF_ADDED, DIFF_ADDED_BG, GRAYSCALE_BORDER, GRAYSCALE_ELEVATED,
GRAYSCALE_PANEL, GRAYSCALE_REASONING, GRAYSCALE_SURFACE, GRAYSCALE_TEXT_BODY,
GRAYSCALE_TEXT_HINT, GRAYSCALE_TEXT_SOFT, GRAYSCALE_UI_THEME, LIGHT_BORDER, LIGHT_ELEVATED,
LIGHT_PANEL, LIGHT_REASONING, LIGHT_SURFACE, LIGHT_TEXT_BODY, LIGHT_TEXT_HINT,
LIGHT_UI_THEME, PaletteMode, SURFACE_REASONING, SURFACE_REASONING_TINT, TERMINAL_UI_THEME,
TEXT_BODY, TEXT_HINT, TEXT_REASONING, TEXT_TOOL_OUTPUT, ThemeId, UI_THEME,
WHALE_REASONING_TEXT_RGB, WHALE_REASONING_TINT_RGB, WHALE_TEXT_BODY_RGB, adapt_bg,
adapt_bg_for_palette_mode, adapt_bg_for_theme, adapt_color, adapt_fg_for_palette_mode,
adapt_fg_for_theme, blend, luma, nearest_ansi16, normalize_hex_rgb_color,
normalize_theme_name, parse_hex_rgb_color, pulse_brightness, reasoning_surface_tint,
rgb_to_ansi256, theme_label_for_mode, ui_theme_from_settings,
LIGHT_PANEL, LIGHT_REASONING, LIGHT_SURFACE, LIGHT_TEXT_BODY, LIGHT_TEXT_BODY_RGB,
LIGHT_TEXT_HINT, LIGHT_UI_THEME, PaletteMode, SOLARIZED_LIGHT_UI_THEME, SOLARIZED_PANEL,
SOLARIZED_SURFACE, SOLARIZED_TEXT_BODY, SOLARIZED_TEXT_HINT, SURFACE_REASONING,
SURFACE_REASONING_TINT, TERMINAL_UI_THEME, TEXT_BODY, TEXT_HINT, TEXT_REASONING,
TEXT_TOOL_OUTPUT, ThemeId, UI_THEME, WHALE_REASONING_TEXT_RGB, WHALE_REASONING_TINT_RGB,
WHALE_TEXT_BODY_RGB, adapt_bg, adapt_bg_for_palette_mode, adapt_bg_for_theme, adapt_color,
adapt_fg_for_palette_mode, adapt_fg_for_theme, blend, luma, nearest_ansi16,
normalize_hex_rgb_color, normalize_theme_name, parse_hex_rgb_color, pulse_brightness,
reasoning_surface_tint, rgb_to_ansi256, theme_label_for_mode, ui_theme_from_settings,
};
use ratatui::style::Color;
@@ -1961,6 +2189,15 @@ mod tests {
assert_eq!(theme.text_body, GRAYSCALE_TEXT_BODY);
}
#[test]
fn ui_theme_selects_solarized_light_variant() {
let theme = super::UiTheme::for_mode(PaletteMode::SolarizedLight);
assert_eq!(theme, SOLARIZED_LIGHT_UI_THEME);
assert_eq!(theme.surface_bg, SOLARIZED_SURFACE);
assert_eq!(theme.panel_bg, SOLARIZED_PANEL);
assert_eq!(theme.text_body, SOLARIZED_TEXT_BODY);
}
#[test]
fn theme_names_normalize_common_grayscale_aliases() {
assert_eq!(normalize_theme_name("system"), Some("system"));
@@ -1970,7 +2207,7 @@ mod tests {
assert_eq!(normalize_theme_name("inherit"), Some("terminal"));
assert_eq!(normalize_theme_name("black-white"), Some("grayscale"));
assert_eq!(normalize_theme_name("mono"), Some("grayscale"));
assert_eq!(normalize_theme_name("solarized"), None);
assert_eq!(normalize_theme_name("solarized"), Some("solarized-light"));
assert_eq!(theme_label_for_mode(PaletteMode::Grayscale), "grayscale");
}
@@ -2009,6 +2246,19 @@ mod tests {
assert_ne!(LIGHT_PANEL, LIGHT_ELEVATED);
}
#[test]
fn solarized_light_does_not_mutate_whale_light_text() {
assert_eq!(
LIGHT_TEXT_BODY,
Color::Rgb(
LIGHT_TEXT_BODY_RGB.0,
LIGHT_TEXT_BODY_RGB.1,
LIGHT_TEXT_BODY_RGB.2
)
);
assert_ne!(LIGHT_TEXT_BODY, SOLARIZED_TEXT_BODY);
}
#[test]
fn dark_palette_uses_soft_body_text_and_warm_reasoning() {
assert_eq!(
@@ -2085,6 +2335,26 @@ mod tests {
);
}
#[test]
fn solarized_light_palette_maps_dark_surfaces_and_text_to_solarized_roles() {
assert_eq!(
adapt_bg_for_palette_mode(DEEPSEEK_INK, PaletteMode::SolarizedLight),
SOLARIZED_SURFACE
);
assert_eq!(
adapt_bg_for_palette_mode(DEEPSEEK_SLATE, PaletteMode::SolarizedLight),
SOLARIZED_PANEL
);
assert_eq!(
adapt_fg_for_palette_mode(Color::White, SOLARIZED_SURFACE, PaletteMode::SolarizedLight),
SOLARIZED_TEXT_BODY
);
assert_eq!(
adapt_fg_for_palette_mode(TEXT_HINT, SOLARIZED_SURFACE, PaletteMode::SolarizedLight),
SOLARIZED_TEXT_HINT
);
}
#[test]
fn grayscale_palette_maps_brand_hues_to_neutral_roles() {
assert_eq!(
+14 -9
View File
@@ -156,7 +156,7 @@ impl TuiPrefs {
let theme = self.theme.trim().to_ascii_lowercase();
let Some(theme) = normalize_theme_name(&theme) else {
anyhow::bail!(
"Invalid tui.toml theme '{}': expected system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, or gruvbox-dark.",
"Invalid tui.toml theme '{}': expected system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark, or solarized-light.",
self.theme
);
};
@@ -520,7 +520,7 @@ impl Settings {
"theme" => {
let Some(id) = crate::palette::ThemeId::from_name(value) else {
anyhow::bail!(
"Failed to update setting: invalid theme '{value}'. Expected: system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark."
"Failed to update setting: invalid theme '{value}'. Expected: system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark, solarized-light."
);
};
self.theme = id.name().to_string();
@@ -528,7 +528,7 @@ impl Settings {
"ui_theme" => {
let Some(id) = crate::palette::ThemeId::from_name(value) else {
anyhow::bail!(
"Failed to update setting: invalid theme '{value}'. Expected: system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark."
"Failed to update setting: invalid theme '{value}'. Expected: system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark, solarized-light."
);
};
self.theme = id.name().to_string();
@@ -780,7 +780,7 @@ impl Settings {
),
(
"theme",
"UI theme: system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark",
"UI theme: system, dark, light, grayscale, catppuccin-mocha, tokyo-night, dracula, gruvbox-dark, solarized-light",
),
(
"background_color",
@@ -1153,8 +1153,13 @@ mod tests {
.expect("set community theme alias");
assert_eq!(settings.theme, "tokyo-night");
let err = settings
settings
.set("theme", "solarized")
.expect("set solarized alias");
assert_eq!(settings.theme, "solarized-light");
let err = settings
.set("theme", "nord")
.expect_err("unknown theme should fail");
assert!(err.to_string().contains("invalid theme"));
}
@@ -2052,6 +2057,7 @@ mod tests {
"tokyo-night",
"dracula",
"gruvbox-dark",
"solarized-light",
] {
let mut prefs = TuiPrefs {
theme: theme.to_string(),
@@ -2079,17 +2085,16 @@ mod tests {
#[test]
fn tui_prefs_validate_rejects_unknown_theme() {
let mut prefs = TuiPrefs {
theme: "solarized".to_string(),
theme: "nord".to_string(),
..TuiPrefs::default()
};
let err = prefs
.validate()
.expect_err("solarized is not a valid theme");
let err = prefs.validate().expect_err("nord is not a valid theme");
assert!(err.to_string().contains("Invalid tui.toml theme"));
assert!(
err.to_string()
.contains("expected system, dark, light, grayscale")
);
assert!(err.to_string().contains("solarized-light"));
}
#[test]