-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(tui): add terminal-transparent theme (#2230) #2276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -825,6 +825,63 @@ pub const DRACULA_UI_THEME: UiTheme = UiTheme { | |||||||||||||||||||||
| tool_failed: Color::Rgb(0xff, 0x55, 0x55), // red | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /// "Terminal" theme: lets the host terminal's color scheme show through | ||||||||||||||||||||||
| /// instead of painting any RGB surface. Backgrounds use `Color::Reset` | ||||||||||||||||||||||
| /// (the terminal's own default bg) and most text uses `Color::Reset` | ||||||||||||||||||||||
| /// (terminal's own default fg). Accents are ANSI named colors so they | ||||||||||||||||||||||
| /// also inherit the user's terminal palette (Solarized, Nord, custom | ||||||||||||||||||||||
| /// schemes, etc.) rather than DeepSeek brand RGB. | ||||||||||||||||||||||
| pub const TERMINAL_UI_THEME: UiTheme = UiTheme { | ||||||||||||||||||||||
| name: "terminal", | ||||||||||||||||||||||
| // Mode is reported as Dark to avoid the dark→light cell remap kicking | ||||||||||||||||||||||
| // in; the terminal-theme cell remap already normalizes everything to | ||||||||||||||||||||||
| // `Color::Reset`, and we never want a second pass overwriting that. | ||||||||||||||||||||||
| mode: PaletteMode::Dark, | ||||||||||||||||||||||
| surface_bg: Color::Reset, | ||||||||||||||||||||||
| panel_bg: Color::Reset, | ||||||||||||||||||||||
| elevated_bg: Color::Reset, | ||||||||||||||||||||||
| composer_bg: Color::Reset, | ||||||||||||||||||||||
| selection_bg: Color::Reset, | ||||||||||||||||||||||
| header_bg: Color::Reset, | ||||||||||||||||||||||
| footer_bg: Color::Reset, | ||||||||||||||||||||||
| text_dim: Color::Reset, | ||||||||||||||||||||||
| text_hint: Color::Reset, | ||||||||||||||||||||||
| text_muted: Color::Reset, | ||||||||||||||||||||||
| text_body: Color::Reset, | ||||||||||||||||||||||
| text_soft: Color::Reset, | ||||||||||||||||||||||
|
Comment on lines
+847
to
+851
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting To preserve the visual hierarchy while inheriting the terminal's color scheme, consider using standard ANSI grayscale colors like
Suggested change
|
||||||||||||||||||||||
| border: Color::Reset, | ||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Consider using
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||||||||||||||
| accent_primary: Color::Blue, | ||||||||||||||||||||||
| accent_secondary: Color::Cyan, | ||||||||||||||||||||||
| accent_action: Color::Yellow, | ||||||||||||||||||||||
| error_fg: Color::Red, | ||||||||||||||||||||||
| error_hover: Color::Red, | ||||||||||||||||||||||
| error_surface: Color::Reset, | ||||||||||||||||||||||
| error_border: Color::Red, | ||||||||||||||||||||||
| error_text: Color::Red, | ||||||||||||||||||||||
| warning: Color::Yellow, | ||||||||||||||||||||||
| success: Color::Green, | ||||||||||||||||||||||
| info: Color::Cyan, | ||||||||||||||||||||||
| mode_agent: Color::Blue, | ||||||||||||||||||||||
| mode_yolo: Color::Red, | ||||||||||||||||||||||
| // Magenta keeps Plan visually distinct from `status_warning` (yellow) | ||||||||||||||||||||||
| // so the mode indicator and warning chip don't collide on themes that | ||||||||||||||||||||||
| // render both in the status row. | ||||||||||||||||||||||
| mode_plan: Color::Magenta, | ||||||||||||||||||||||
| mode_goal: Color::Green, | ||||||||||||||||||||||
| // DarkGray gives "Ready" a low-contrast but still distinguishable hue | ||||||||||||||||||||||
| // versus default body text (which is `Color::Reset` on this theme). | ||||||||||||||||||||||
| status_ready: Color::DarkGray, | ||||||||||||||||||||||
| status_working: Color::Cyan, | ||||||||||||||||||||||
| status_warning: Color::Yellow, | ||||||||||||||||||||||
| diff_added_fg: Color::Green, | ||||||||||||||||||||||
| diff_deleted_fg: Color::Red, | ||||||||||||||||||||||
| diff_added_bg: Color::Reset, | ||||||||||||||||||||||
| diff_deleted_bg: Color::Reset, | ||||||||||||||||||||||
| tool_running: Color::Cyan, | ||||||||||||||||||||||
| tool_success: Color::Green, | ||||||||||||||||||||||
| tool_failed: Color::Red, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| pub const GRUVBOX_DARK_UI_THEME: UiTheme = UiTheme { | ||||||||||||||||||||||
| name: "gruvbox-dark", | ||||||||||||||||||||||
| mode: PaletteMode::Dark, | ||||||||||||||||||||||
|
|
@@ -874,6 +931,7 @@ pub const GRUVBOX_DARK_UI_THEME: UiTheme = UiTheme { | |||||||||||||||||||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||||||||||||||||||||
| pub enum ThemeId { | ||||||||||||||||||||||
| System, | ||||||||||||||||||||||
| Terminal, | ||||||||||||||||||||||
| Whale, | ||||||||||||||||||||||
| WhaleLight, | ||||||||||||||||||||||
| Grayscale, | ||||||||||||||||||||||
|
|
@@ -891,6 +949,7 @@ impl ThemeId { | |||||||||||||||||||||
| pub fn from_name(value: &str) -> Option<Self> { | ||||||||||||||||||||||
| match normalize_theme_name(value)? { | ||||||||||||||||||||||
| "system" => Some(Self::System), | ||||||||||||||||||||||
| "terminal" => Some(Self::Terminal), | ||||||||||||||||||||||
| "dark" => Some(Self::Whale), | ||||||||||||||||||||||
| "light" => Some(Self::WhaleLight), | ||||||||||||||||||||||
| "grayscale" => Some(Self::Grayscale), | ||||||||||||||||||||||
|
|
@@ -908,6 +967,7 @@ impl ThemeId { | |||||||||||||||||||||
| pub const fn name(self) -> &'static str { | ||||||||||||||||||||||
| match self { | ||||||||||||||||||||||
| Self::System => "system", | ||||||||||||||||||||||
| Self::Terminal => "terminal", | ||||||||||||||||||||||
| Self::Whale => "dark", | ||||||||||||||||||||||
| Self::WhaleLight => "light", | ||||||||||||||||||||||
| Self::Grayscale => "grayscale", | ||||||||||||||||||||||
|
|
@@ -923,6 +983,7 @@ impl ThemeId { | |||||||||||||||||||||
| pub const fn display_name(self) -> &'static str { | ||||||||||||||||||||||
| match self { | ||||||||||||||||||||||
| Self::System => "System", | ||||||||||||||||||||||
| Self::Terminal => "Terminal", | ||||||||||||||||||||||
| Self::Whale => "Whale (Dark)", | ||||||||||||||||||||||
| Self::WhaleLight => "Whale Light", | ||||||||||||||||||||||
| Self::Grayscale => "Grayscale", | ||||||||||||||||||||||
|
|
@@ -938,6 +999,7 @@ impl ThemeId { | |||||||||||||||||||||
| pub const fn tagline(self) -> &'static str { | ||||||||||||||||||||||
| match self { | ||||||||||||||||||||||
| Self::System => "Follow terminal background (COLORFGBG / macOS appearance)", | ||||||||||||||||||||||
| Self::Terminal => "Inherit terminal colors fully (transparent surfaces, ANSI accents)", | ||||||||||||||||||||||
| Self::Whale => "Whale dark — deep navy & gold", | ||||||||||||||||||||||
| Self::WhaleLight => "DeepSeek light, paper-ish", | ||||||||||||||||||||||
| Self::Grayscale => "Color-minimal high contrast", | ||||||||||||||||||||||
|
|
@@ -956,6 +1018,7 @@ impl ThemeId { | |||||||||||||||||||||
| pub fn ui_theme(self) -> UiTheme { | ||||||||||||||||||||||
| match self { | ||||||||||||||||||||||
| Self::System => UiTheme::detect(), | ||||||||||||||||||||||
| Self::Terminal => TERMINAL_UI_THEME, | ||||||||||||||||||||||
| Self::Whale => UI_THEME, | ||||||||||||||||||||||
| Self::WhaleLight => LIGHT_UI_THEME, | ||||||||||||||||||||||
| Self::Grayscale => GRAYSCALE_UI_THEME, | ||||||||||||||||||||||
|
|
@@ -970,6 +1033,7 @@ impl ThemeId { | |||||||||||||||||||||
| /// Themes shown in the `/theme` picker, in display order. | ||||||||||||||||||||||
| pub const SELECTABLE_THEMES: &[ThemeId] = &[ | ||||||||||||||||||||||
| ThemeId::System, | ||||||||||||||||||||||
| ThemeId::Terminal, | ||||||||||||||||||||||
| ThemeId::Whale, | ||||||||||||||||||||||
| ThemeId::WhaleLight, | ||||||||||||||||||||||
| ThemeId::Grayscale, | ||||||||||||||||||||||
|
|
@@ -1012,6 +1076,7 @@ impl UiTheme { | |||||||||||||||||||||
| pub fn normalize_theme_name(value: &str) -> Option<&'static str> { | ||||||||||||||||||||||
| match value.trim().to_ascii_lowercase().as_str() { | ||||||||||||||||||||||
| "" | "auto" | "system" | "default" => Some("system"), | ||||||||||||||||||||||
| "terminal" | "term" | "transparent" | "follow-terminal" | "inherit" => Some("terminal"), | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
| "dark" | "whale" | "whale-dark" => Some("dark"), | ||||||||||||||||||||||
| "light" | "whale-light" => Some("light"), | ||||||||||||||||||||||
| "grayscale" | "greyscale" | "gray" | "grey" | "mono" | "monochrome" | "black-white" | ||||||||||||||||||||||
|
|
@@ -1189,7 +1254,11 @@ const fn theme_diff_deleted_bg(ui: &UiTheme) -> Color { | |||||||||||||||||||||
| pub const fn theme_remap_active(theme: ThemeId) -> bool { | ||||||||||||||||||||||
| matches!( | ||||||||||||||||||||||
| theme, | ||||||||||||||||||||||
| ThemeId::CatppuccinMocha | ThemeId::TokyoNight | ThemeId::Dracula | ThemeId::GruvboxDark | ||||||||||||||||||||||
| ThemeId::Terminal | ||||||||||||||||||||||
| | ThemeId::CatppuccinMocha | ||||||||||||||||||||||
| | ThemeId::TokyoNight | ||||||||||||||||||||||
| | ThemeId::Dracula | ||||||||||||||||||||||
| | ThemeId::GruvboxDark | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -1677,14 +1746,15 @@ fn rgb_to_ansi256(r: u8, g: u8, b: u8) -> u8 { | |||||||||||||||||||||
| mod tests { | ||||||||||||||||||||||
| use super::{ | ||||||||||||||||||||||
| ACCENT_REASONING_LIVE, ColorDepth, DEEPSEEK_INK, DEEPSEEK_RED, DEEPSEEK_SKY, | ||||||||||||||||||||||
| DEEPSEEK_SLATE, 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, TEXT_BODY, TEXT_HINT, TEXT_REASONING, | ||||||||||||||||||||||
| TEXT_TOOL_OUTPUT, UI_THEME, WHALE_REASONING_TEXT_RGB, WHALE_REASONING_TINT_RGB, | ||||||||||||||||||||||
| WHALE_TEXT_BODY_RGB, adapt_bg, adapt_bg_for_palette_mode, adapt_color, | ||||||||||||||||||||||
| adapt_fg_for_palette_mode, blend, luma, nearest_ansi16, normalize_hex_rgb_color, | ||||||||||||||||||||||
| 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, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
@@ -1770,12 +1840,39 @@ mod tests { | |||||||||||||||||||||
| assert_eq!(normalize_theme_name("system"), Some("system")); | ||||||||||||||||||||||
| assert_eq!(normalize_theme_name("default"), Some("system")); | ||||||||||||||||||||||
| assert_eq!(normalize_theme_name("whale"), Some("dark")); | ||||||||||||||||||||||
| assert_eq!(normalize_theme_name("transparent"), Some("terminal")); | ||||||||||||||||||||||
| 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); | ||||||||||||||||||||||
|
Comment on lines
1840
to
1847
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Terminal alias assertions ( Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||||||||||||||
| assert_eq!(theme_label_for_mode(PaletteMode::Grayscale), "grayscale"); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||
| fn terminal_theme_resets_surfaces_and_remaps_direct_palette_constants() { | ||||||||||||||||||||||
| assert_eq!(ThemeId::from_name("terminal"), Some(ThemeId::Terminal)); | ||||||||||||||||||||||
| assert_eq!(TERMINAL_UI_THEME.surface_bg, Color::Reset); | ||||||||||||||||||||||
| assert_eq!(TERMINAL_UI_THEME.footer_bg, Color::Reset); | ||||||||||||||||||||||
| assert_eq!(TERMINAL_UI_THEME.text_body, Color::Reset); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| assert_eq!( | ||||||||||||||||||||||
| adapt_bg_for_theme(DEEPSEEK_INK, ThemeId::Terminal, &TERMINAL_UI_THEME), | ||||||||||||||||||||||
| Color::Reset | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| assert_eq!( | ||||||||||||||||||||||
| adapt_bg_for_theme(DIFF_ADDED_BG, ThemeId::Terminal, &TERMINAL_UI_THEME), | ||||||||||||||||||||||
| Color::Reset | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| assert_eq!( | ||||||||||||||||||||||
| adapt_fg_for_theme(TEXT_BODY, ThemeId::Terminal, &TERMINAL_UI_THEME), | ||||||||||||||||||||||
| Color::Reset | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| assert_eq!( | ||||||||||||||||||||||
| adapt_fg_for_theme(DIFF_ADDED, ThemeId::Terminal, &TERMINAL_UI_THEME), | ||||||||||||||||||||||
| Color::Green | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||
| fn light_palette_has_quiet_layer_separation() { | ||||||||||||||||||||||
| assert_eq!(LIGHT_SURFACE, Color::Rgb(246, 248, 251)); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
Color::Resetforselection_bgmeans that selected text or list items will have the same background color as the default terminal background. This makes selections (such as text highlighted for copying or selected menu items) completely invisible or extremely difficult to distinguish.Consider using a standard ANSI color like
Color::DarkGrayorColor::Grayforselection_bgto ensure selections are visually distinct while still respecting the user's terminal palette.