Merge pull request #2270 from AiurArtanis/feat/theme-solarized-light
feat(theme): add Solarized Light theme
This commit is contained in:
@@ -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
@@ -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!(
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user