feat(theme): add optional sidebar background
This commit is contained in:
parent
1f1e434268
commit
add6c14f1e
|
|
@ -3,6 +3,7 @@
|
|||
## Unreleased
|
||||
|
||||
### Added
|
||||
- `theme.custom.sidebar_bg` can now give the desktop sidebar its own background without changing built-in theme defaults.
|
||||
- Settings and `ui.status_indicators = "symbols"` can now use distinct static shapes for blocked, working, done, idle, and unknown agent states. (#2260)
|
||||
- The plugin marketplace now discovers valid manifests at repository roots and subdirectories, groups multiple plugins under each repository, and publishes their versions and exact default-branch commits.
|
||||
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ You can override individual colors:
|
|||
|
||||
```toml
|
||||
[theme.custom]
|
||||
sidebar_bg = "#181825"
|
||||
panel_bg = "reset"
|
||||
accent = "#a6e3a1"
|
||||
green = "#a6e3a1"
|
||||
|
|
@ -250,6 +251,8 @@ red = "#f38ba8"
|
|||
yellow = "#f9e2af"
|
||||
```
|
||||
|
||||
`sidebar_bg` optionally gives the desktop sidebar its own background. When omitted, the sidebar keeps the host terminal background.
|
||||
|
||||
Color values accept hex, named colors, `rgb(r,g,b)`, or reset aliases like `reset`, `default`, `none`, and `transparent`.
|
||||
|
||||
## UI and sidebar
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ dark_name = "catppuccin"
|
|||
|
||||
```toml
|
||||
[theme.custom]
|
||||
sidebar_bg = "#181825"
|
||||
panel_bg = "reset"
|
||||
accent = "#a6e3a1"
|
||||
green = "#a6e3a1"
|
||||
|
|
@ -246,6 +247,8 @@ red = "#f38ba8"
|
|||
yellow = "#f9e2af"
|
||||
```
|
||||
|
||||
`sidebar_bg` を使うと、デスクトップのサイドバーだけに背景色を設定できます。省略した場合、サイドバーはホストターミナルの背景を使います。
|
||||
|
||||
色の値には、hex、名前付きの色、`rgb(r,g,b)`、または `reset`、`default`、`none`、`transparent` のようなリセットエイリアスが使えます。
|
||||
|
||||
## UI とサイドバー
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ dark_name = "catppuccin"
|
|||
|
||||
```toml
|
||||
[theme.custom]
|
||||
sidebar_bg = "#181825"
|
||||
panel_bg = "reset"
|
||||
accent = "#a6e3a1"
|
||||
green = "#a6e3a1"
|
||||
|
|
@ -246,6 +247,8 @@ red = "#f38ba8"
|
|||
yellow = "#f9e2af"
|
||||
```
|
||||
|
||||
`sidebar_bg` 可单独设置桌面侧边栏的背景色。省略时,侧边栏继续使用宿主终端背景。
|
||||
|
||||
颜色值支持十六进制、命名颜色、`rgb(r,g,b)`,以及 `reset`、`default`、`none`、`transparent` 等重置别名。
|
||||
|
||||
## UI 与侧边栏
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@
|
|||
"default": "unset",
|
||||
"description": "Override the panel_bg color token on top of the base theme. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
|
||||
},
|
||||
{
|
||||
"key": "theme.custom.sidebar_bg",
|
||||
"type": "color",
|
||||
"default": "unset",
|
||||
"description": "Set the desktop sidebar background without changing other panel surfaces. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
|
||||
},
|
||||
{
|
||||
"key": "theme.custom.surface0",
|
||||
"type": "color",
|
||||
|
|
|
|||
|
|
@ -107,8 +107,10 @@ use crate::workspace::Workspace;
|
|||
pub struct Palette {
|
||||
/// Primary accent (highlight, active borders).
|
||||
pub accent: Color,
|
||||
/// Background for floating panels, overlays, and modals.
|
||||
/// Background for the tab bar, floating panels, overlays, and modals.
|
||||
pub panel_bg: Color,
|
||||
/// Optional desktop sidebar background. Reset preserves the terminal background.
|
||||
pub sidebar_bg: Color,
|
||||
/// Subtle surface background for selected/focused items.
|
||||
pub surface0: Color,
|
||||
/// Slightly lighter surface for hover/active states.
|
||||
|
|
@ -145,6 +147,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(137, 180, 250), // blue
|
||||
panel_bg: Color::Rgb(24, 24, 37),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(49, 50, 68),
|
||||
surface1: Color::Rgb(69, 71, 90),
|
||||
surface_dim: Color::Rgb(30, 30, 46),
|
||||
|
|
@ -167,6 +170,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(30, 102, 245),
|
||||
panel_bg: Color::Rgb(239, 241, 245),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(204, 208, 218),
|
||||
surface1: Color::Rgb(188, 192, 204),
|
||||
surface_dim: Color::Rgb(230, 233, 239),
|
||||
|
|
@ -189,6 +193,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Blue,
|
||||
panel_bg: Color::Reset,
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Reset,
|
||||
surface1: Color::DarkGray,
|
||||
surface_dim: Color::DarkGray,
|
||||
|
|
@ -211,6 +216,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(122, 162, 247), // blue
|
||||
panel_bg: Color::Rgb(26, 27, 38),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(36, 40, 59),
|
||||
surface1: Color::Rgb(65, 72, 104),
|
||||
surface_dim: Color::Rgb(26, 27, 38),
|
||||
|
|
@ -233,6 +239,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(46, 125, 233),
|
||||
panel_bg: Color::Rgb(225, 226, 231),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(196, 200, 218),
|
||||
surface1: Color::Rgb(168, 174, 203),
|
||||
surface_dim: Color::Rgb(210, 211, 218),
|
||||
|
|
@ -255,6 +262,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(189, 147, 249), // purple
|
||||
panel_bg: Color::Rgb(40, 42, 54),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(68, 71, 90),
|
||||
surface1: Color::Rgb(98, 114, 164),
|
||||
surface_dim: Color::Rgb(40, 42, 54),
|
||||
|
|
@ -277,6 +285,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(136, 192, 208), // frost
|
||||
panel_bg: Color::Rgb(46, 52, 64),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(59, 66, 82),
|
||||
surface1: Color::Rgb(67, 76, 94),
|
||||
surface_dim: Color::Rgb(46, 52, 64),
|
||||
|
|
@ -299,6 +308,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(215, 153, 33), // yellow
|
||||
panel_bg: Color::Rgb(40, 40, 40),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(60, 56, 54),
|
||||
surface1: Color::Rgb(80, 73, 69),
|
||||
surface_dim: Color::Rgb(40, 40, 40),
|
||||
|
|
@ -321,6 +331,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(7, 102, 120),
|
||||
panel_bg: Color::Rgb(251, 241, 199),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(235, 219, 178),
|
||||
surface1: Color::Rgb(213, 196, 161),
|
||||
surface_dim: Color::Rgb(242, 229, 188),
|
||||
|
|
@ -343,6 +354,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(97, 175, 239), // blue
|
||||
panel_bg: Color::Rgb(40, 44, 52),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(44, 49, 58),
|
||||
surface1: Color::Rgb(62, 68, 81),
|
||||
surface_dim: Color::Rgb(40, 44, 52),
|
||||
|
|
@ -365,6 +377,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(64, 120, 242),
|
||||
panel_bg: Color::Rgb(250, 250, 250),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(240, 240, 241),
|
||||
surface1: Color::Rgb(229, 229, 230),
|
||||
surface_dim: Color::Rgb(245, 245, 246),
|
||||
|
|
@ -387,6 +400,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(38, 139, 210), // blue
|
||||
panel_bg: Color::Rgb(0, 43, 54),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(7, 54, 66),
|
||||
surface1: Color::Rgb(88, 110, 117),
|
||||
surface_dim: Color::Rgb(0, 43, 54),
|
||||
|
|
@ -409,6 +423,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(38, 139, 210),
|
||||
panel_bg: Color::Rgb(253, 246, 227),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(238, 232, 213),
|
||||
surface1: Color::Rgb(147, 161, 161),
|
||||
surface_dim: Color::Rgb(238, 232, 213),
|
||||
|
|
@ -431,6 +446,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(126, 156, 216), // blue
|
||||
panel_bg: Color::Rgb(31, 31, 40),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(42, 42, 55),
|
||||
surface1: Color::Rgb(54, 54, 70),
|
||||
surface_dim: Color::Rgb(31, 31, 40),
|
||||
|
|
@ -453,6 +469,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(77, 105, 155),
|
||||
panel_bg: Color::Rgb(242, 236, 188),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(220, 213, 172),
|
||||
surface1: Color::Rgb(201, 203, 209),
|
||||
surface_dim: Color::Rgb(213, 206, 163),
|
||||
|
|
@ -475,6 +492,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(196, 167, 231), // iris
|
||||
panel_bg: Color::Rgb(25, 23, 36),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(31, 29, 46),
|
||||
surface1: Color::Rgb(38, 35, 58),
|
||||
surface_dim: Color::Rgb(38, 35, 58),
|
||||
|
|
@ -497,6 +515,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(144, 122, 169),
|
||||
panel_bg: Color::Rgb(250, 244, 237),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(242, 233, 225),
|
||||
surface1: Color::Rgb(255, 250, 243),
|
||||
surface_dim: Color::Rgb(242, 233, 225),
|
||||
|
|
@ -519,6 +538,7 @@ impl Palette {
|
|||
Self {
|
||||
accent: Color::Rgb(255, 199, 153),
|
||||
panel_bg: Color::Rgb(26, 26, 26),
|
||||
sidebar_bg: Color::Reset,
|
||||
surface0: Color::Rgb(35, 35, 35),
|
||||
surface1: Color::Rgb(40, 40, 40),
|
||||
surface_dim: Color::Rgb(16, 16, 16),
|
||||
|
|
@ -570,6 +590,9 @@ impl Palette {
|
|||
if let Some(c) = &custom.panel_bg {
|
||||
self.panel_bg = parse_color(c);
|
||||
}
|
||||
if let Some(c) = &custom.sidebar_bg {
|
||||
self.sidebar_bg = parse_color(c);
|
||||
}
|
||||
if let Some(c) = &custom.surface0 {
|
||||
self.surface0 = parse_color(c);
|
||||
}
|
||||
|
|
@ -2397,6 +2420,31 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn built_in_themes_leave_sidebar_background_unset() {
|
||||
for name in THEME_NAMES {
|
||||
let palette = Palette::from_name(name).unwrap();
|
||||
assert_eq!(
|
||||
palette.sidebar_bg,
|
||||
Color::Reset,
|
||||
"built-in theme changed the sidebar background: {name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_sidebar_background_overrides_the_default() {
|
||||
let custom = crate::config::CustomThemeColors {
|
||||
sidebar_bg: Some("#181825".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
Palette::catppuccin().with_overrides(&custom).sidebar_bg,
|
||||
Color::Rgb(24, 24, 37)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_theme_aliases_resolve() {
|
||||
for name in ["light", "latte", "tokyo-day", "onelight", "lotus", "dawn"] {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ pub struct ThemeConfig {
|
|||
pub struct CustomThemeColors {
|
||||
pub accent: Option<String>,
|
||||
pub panel_bg: Option<String>,
|
||||
pub sidebar_bg: Option<String>,
|
||||
pub surface0: Option<String>,
|
||||
pub surface1: Option<String>,
|
||||
pub surface_dim: Option<String>,
|
||||
|
|
@ -164,6 +165,7 @@ name = "nord"
|
|||
|
||||
[theme.custom]
|
||||
panel_bg = "#1e1e2e"
|
||||
sidebar_bg = "#181825"
|
||||
accent = "#ff79c6"
|
||||
red = "rgb(255, 85, 85)"
|
||||
"##;
|
||||
|
|
@ -171,6 +173,7 @@ red = "rgb(255, 85, 85)"
|
|||
assert_eq!(config.theme.name.as_deref(), Some("nord"));
|
||||
let custom = config.theme.custom.as_ref().unwrap();
|
||||
assert_eq!(custom.panel_bg.as_deref(), Some("#1e1e2e"));
|
||||
assert_eq!(custom.sidebar_bg.as_deref(), Some("#181825"));
|
||||
assert_eq!(custom.accent.as_deref(), Some("#ff79c6"));
|
||||
assert_eq!(custom.red.as_deref(), Some("rgb(255, 85, 85)"));
|
||||
assert!(custom.green.is_none());
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration
|
|||
# Override individual color tokens on top of the base theme.
|
||||
# Accepts: hex (#rrggbb), named colors, rgb(r,g,b), or panel_bg = "reset"
|
||||
# [theme.custom]
|
||||
# sidebar_bg = "#181825"
|
||||
# panel_bg = "reset"
|
||||
# accent = "#f5c2e7"
|
||||
# red = "#ff6188"
|
||||
|
|
|
|||
|
|
@ -757,6 +757,9 @@ pub(super) fn render_sidebar_collapsed(app: &AppState, frame: &mut Frame, area:
|
|||
let is_navigating = matches!(app.mode, Mode::Navigate);
|
||||
|
||||
let p = &app.palette;
|
||||
frame
|
||||
.buffer_mut()
|
||||
.set_style(area, Style::default().bg(p.sidebar_bg));
|
||||
let sep_style = if is_navigating {
|
||||
Style::default().fg(p.accent)
|
||||
} else {
|
||||
|
|
@ -960,6 +963,9 @@ pub(super) fn render_sidebar(
|
|||
area: Rect,
|
||||
) {
|
||||
let p = &app.palette;
|
||||
frame
|
||||
.buffer_mut()
|
||||
.set_style(area, Style::default().bg(p.sidebar_bg));
|
||||
let is_navigating = matches!(app.mode, Mode::Navigate);
|
||||
let sep_style = if is_navigating {
|
||||
Style::default().fg(p.accent)
|
||||
|
|
@ -1591,6 +1597,37 @@ mod tests {
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expanded_and_collapsed_sidebars_use_custom_background() {
|
||||
let mut app = crate::app::state::AppState::test_new();
|
||||
app.workspaces.clear();
|
||||
app.active = None;
|
||||
app.palette.sidebar_bg = ratatui::style::Color::Rgb(12, 34, 56);
|
||||
let area = Rect::new(0, 0, 26, 20);
|
||||
|
||||
let mut expanded = Terminal::new(TestBackend::new(26, 20)).unwrap();
|
||||
expanded
|
||||
.draw(|frame| render_sidebar(&app, &TerminalRuntimeRegistry::new(), frame, area))
|
||||
.unwrap();
|
||||
assert!(expanded
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.all(|cell| cell.bg == app.palette.sidebar_bg));
|
||||
|
||||
let mut collapsed = Terminal::new(TestBackend::new(26, 20)).unwrap();
|
||||
collapsed
|
||||
.draw(|frame| render_sidebar_collapsed(&app, frame, area))
|
||||
.unwrap();
|
||||
assert!(collapsed
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.all(|cell| cell.bg == app.palette.sidebar_bg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_agent_rows_remove_redundant_state_text() {
|
||||
let mut app = crate::app::state::AppState::test_new();
|
||||
|
|
|
|||
Loading…
Reference in New Issue