Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/next/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Panes can now route normal right-click gestures to mouse-reporting applications through the pane menu, `herdr pane input`, `pane.input.set`, or the `pane split --right-click pane` launch option.
- `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)
- Navigate-mode selection rows now use a dedicated per-theme cursor color, customizable via `theme.custom.selection_bg`, so the cursor stays distinguishable from the active Space and Agent highlight.
- 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.

### Changed
Expand Down
5 changes: 3 additions & 2 deletions docs/next/website/src/content/docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ You can override individual colors:
```toml
[theme.custom]
sidebar_bg = "#181825"
active_row_bg = "#313244"
active_row_bg = "#1e1e2e"
selection_bg = "#313244"
panel_bg = "reset"
accent = "#a6e3a1"
green = "#a6e3a1"
Expand All @@ -260,7 +261,7 @@ red = "#f38ba8"
yellow = "#f9e2af"
```

`sidebar_bg` optionally gives the desktop sidebar its own background. When omitted, the sidebar keeps the host terminal background. `active_row_bg` changes the active Space and focused Agent row background without affecting separators or scrollbar tracks.
`sidebar_bg` optionally gives the desktop sidebar its own background. When omitted, the sidebar keeps the host terminal background. `active_row_bg` changes the active Space and focused Agent row background without affecting separators or scrollbar tracks. `selection_bg` changes the Navigate-mode cursor row background in the sidebar.

Color values accept hex, named colors, `rgb(r,g,b)`, or reset aliases like `reset`, `default`, `none`, and `transparent`.

Expand Down
5 changes: 3 additions & 2 deletions docs/next/website/src/content/docs/ja/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ dark_name = "catppuccin"
```toml
[theme.custom]
sidebar_bg = "#181825"
active_row_bg = "#313244"
active_row_bg = "#1e1e2e"
selection_bg = "#313244"
panel_bg = "reset"
accent = "#a6e3a1"
green = "#a6e3a1"
Expand All @@ -248,7 +249,7 @@ red = "#f38ba8"
yellow = "#f9e2af"
```

`sidebar_bg` を使うと、デスクトップのサイドバーだけに背景色を設定できます。省略した場合、サイドバーはホストターミナルの背景を使います。`active_row_bg` は、区切り線やスクロールバートラックに影響を与えず、アクティブな Space とフォーカス中の Agent 行の背景色を変更します。
`sidebar_bg` を使うと、デスクトップのサイドバーだけに背景色を設定できます。省略した場合、サイドバーはホストターミナルの背景を使います。`active_row_bg` は、区切り線やスクロールバートラックに影響を与えず、アクティブな Space とフォーカス中の Agent 行の背景色を変更します。`selection_bg` は、サイドバーの Navigate モードのカーソル行の背景色を変更します。

色の値には、hex、名前付きの色、`rgb(r,g,b)`、または `reset`、`default`、`none`、`transparent` のようなリセットエイリアスが使えます。

Expand Down
5 changes: 3 additions & 2 deletions docs/next/website/src/content/docs/zh-cn/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ dark_name = "catppuccin"
```toml
[theme.custom]
sidebar_bg = "#181825"
active_row_bg = "#313244"
active_row_bg = "#1e1e2e"
selection_bg = "#313244"
panel_bg = "reset"
accent = "#a6e3a1"
green = "#a6e3a1"
Expand All @@ -248,7 +249,7 @@ red = "#f38ba8"
yellow = "#f9e2af"
```

`sidebar_bg` 可单独设置桌面侧边栏的背景色。省略时,侧边栏继续使用宿主终端背景。`active_row_bg` 可更改当前 Space 和已聚焦 Agent 行的背景色,而不会影响分隔线或滚动条轨道。
`sidebar_bg` 可单独设置桌面侧边栏的背景色。省略时,侧边栏继续使用宿主终端背景。`active_row_bg` 可更改当前 Space 和已聚焦 Agent 行的背景色,而不会影响分隔线或滚动条轨道。`selection_bg` 可更改侧边栏中 Navigate 模式光标行的背景色。

颜色值支持十六进制、命名颜色、`rgb(r,g,b)`,以及 `reset`、`default`、`none`、`transparent` 等重置别名。

Expand Down
6 changes: 6 additions & 0 deletions docs/next/website/src/data/config-reference.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
"default": "unset",
"description": "Set the active Space and focused Agent row background without changing separators or scrollbar tracks. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.selection_bg",
"type": "color",
"default": "unset",
"description": "Set the Navigate-mode cursor row background in the sidebar without changing other selection surfaces. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.surface0",
"type": "color",
Expand Down
77 changes: 62 additions & 15 deletions src/app/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub struct Palette {
pub sidebar_bg: Color,
/// Background for the active workspace and focused agent rows.
pub active_row_bg: Color,
/// Background for the Navigate-mode cursor row in the sidebar.
pub selection_bg: Color,
/// Subtle surface background for selected/focused items.
pub surface0: Color,
/// Slightly lighter surface for hover/active states.
Expand Down Expand Up @@ -112,7 +114,8 @@ impl Palette {
accent: Color::Rgb(137, 180, 250), // blue
panel_bg: Color::Rgb(24, 24, 37),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(49, 50, 68),
active_row_bg: Color::Rgb(30, 30, 46),
selection_bg: Color::Rgb(49, 50, 68),
surface0: Color::Rgb(49, 50, 68),
surface1: Color::Rgb(69, 71, 90),
surface_dim: Color::Rgb(30, 30, 46),
Expand All @@ -136,7 +139,8 @@ impl Palette {
accent: Color::Rgb(30, 102, 245),
panel_bg: Color::Rgb(239, 241, 245),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(204, 208, 218),
active_row_bg: Color::Rgb(230, 233, 239),
selection_bg: Color::Rgb(189, 208, 245),
surface0: Color::Rgb(204, 208, 218),
surface1: Color::Rgb(188, 192, 204),
surface_dim: Color::Rgb(230, 233, 239),
Expand All @@ -161,6 +165,7 @@ impl Palette {
panel_bg: Color::Reset,
sidebar_bg: Color::Reset,
active_row_bg: Color::DarkGray,
selection_bg: Color::Reset,
surface0: Color::Reset,
surface1: Color::DarkGray,
surface_dim: Color::DarkGray,
Expand All @@ -184,7 +189,8 @@ impl Palette {
accent: Color::Rgb(122, 162, 247), // blue
panel_bg: Color::Rgb(26, 27, 38),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(47, 51, 77),
active_row_bg: Color::Rgb(35, 38, 54),
selection_bg: Color::Rgb(45, 54, 80),
surface0: Color::Rgb(36, 40, 59),
surface1: Color::Rgb(65, 72, 104),
surface_dim: Color::Rgb(26, 27, 38),
Expand All @@ -208,7 +214,8 @@ impl Palette {
accent: Color::Rgb(46, 125, 233),
panel_bg: Color::Rgb(225, 226, 231),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(196, 200, 218),
active_row_bg: Color::Rgb(210, 211, 218),
selection_bg: Color::Rgb(182, 202, 231),
surface0: Color::Rgb(196, 200, 218),
surface1: Color::Rgb(168, 174, 203),
surface_dim: Color::Rgb(210, 211, 218),
Expand All @@ -232,7 +239,8 @@ impl Palette {
accent: Color::Rgb(189, 147, 249), // purple
panel_bg: Color::Rgb(40, 42, 54),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(68, 71, 90),
active_row_bg: Color::Rgb(55, 60, 82),
selection_bg: Color::Rgb(70, 63, 93),
surface0: Color::Rgb(68, 71, 90),
surface1: Color::Rgb(98, 114, 164),
surface_dim: Color::Rgb(40, 42, 54),
Expand All @@ -257,6 +265,7 @@ impl Palette {
panel_bg: Color::Rgb(46, 52, 64),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(67, 76, 94),
selection_bg: Color::Rgb(64, 80, 93),
surface0: Color::Rgb(59, 66, 82),
surface1: Color::Rgb(67, 76, 94),
surface_dim: Color::Rgb(46, 52, 64),
Expand All @@ -280,7 +289,8 @@ impl Palette {
accent: Color::Rgb(215, 153, 33), // yellow
panel_bg: Color::Rgb(40, 40, 40),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(80, 73, 69),
active_row_bg: Color::Rgb(50, 49, 48),
selection_bg: Color::Rgb(75, 63, 39),
surface0: Color::Rgb(60, 56, 54),
surface1: Color::Rgb(80, 73, 69),
surface_dim: Color::Rgb(40, 40, 40),
Expand All @@ -304,7 +314,8 @@ impl Palette {
accent: Color::Rgb(7, 102, 120),
panel_bg: Color::Rgb(251, 241, 199),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(213, 196, 161),
active_row_bg: Color::Rgb(242, 229, 188),
selection_bg: Color::Rgb(235, 219, 178),
surface0: Color::Rgb(235, 219, 178),
surface1: Color::Rgb(213, 196, 161),
surface_dim: Color::Rgb(242, 229, 188),
Expand All @@ -328,7 +339,8 @@ impl Palette {
accent: Color::Rgb(97, 175, 239), // blue
panel_bg: Color::Rgb(40, 44, 52),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(62, 68, 81),
active_row_bg: Color::Rgb(49, 54, 64),
selection_bg: Color::Rgb(51, 70, 89),
surface0: Color::Rgb(44, 49, 58),
surface1: Color::Rgb(62, 68, 81),
surface_dim: Color::Rgb(40, 44, 52),
Expand All @@ -353,6 +365,7 @@ impl Palette {
panel_bg: Color::Rgb(250, 250, 250),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(216, 219, 226),
selection_bg: Color::Rgb(205, 219, 248),
surface0: Color::Rgb(240, 240, 241),
surface1: Color::Rgb(229, 229, 230),
surface_dim: Color::Rgb(245, 245, 246),
Expand All @@ -377,6 +390,7 @@ impl Palette {
panel_bg: Color::Rgb(0, 43, 54),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(22, 75, 87),
selection_bg: Color::Rgb(8, 62, 85),
surface0: Color::Rgb(7, 54, 66),
surface1: Color::Rgb(88, 110, 117),
surface_dim: Color::Rgb(0, 43, 54),
Expand All @@ -400,7 +414,8 @@ impl Palette {
accent: Color::Rgb(38, 139, 210),
panel_bg: Color::Rgb(253, 246, 227),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(222, 216, 198),
active_row_bg: Color::Rgb(238, 232, 213),
selection_bg: Color::Rgb(201, 220, 223),
surface0: Color::Rgb(238, 232, 213),
surface1: Color::Rgb(147, 161, 161),
surface_dim: Color::Rgb(238, 232, 213),
Expand All @@ -425,6 +440,7 @@ impl Palette {
panel_bg: Color::Rgb(31, 31, 40),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(54, 54, 70),
selection_bg: Color::Rgb(50, 56, 75),
surface0: Color::Rgb(42, 42, 55),
surface1: Color::Rgb(54, 54, 70),
surface_dim: Color::Rgb(31, 31, 40),
Expand All @@ -448,7 +464,8 @@ impl Palette {
accent: Color::Rgb(77, 105, 155),
panel_bg: Color::Rgb(242, 236, 188),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(201, 203, 209),
active_row_bg: Color::Rgb(213, 206, 163),
selection_bg: Color::Rgb(220, 213, 172),
surface0: Color::Rgb(220, 213, 172),
surface1: Color::Rgb(201, 203, 209),
surface_dim: Color::Rgb(213, 206, 163),
Expand All @@ -472,7 +489,8 @@ impl Palette {
accent: Color::Rgb(196, 167, 231), // iris
panel_bg: Color::Rgb(25, 23, 36),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(64, 61, 82),
active_row_bg: Color::Rgb(38, 35, 58),
selection_bg: Color::Rgb(59, 52, 75),
surface0: Color::Rgb(31, 29, 46),
surface1: Color::Rgb(38, 35, 58),
surface_dim: Color::Rgb(38, 35, 58),
Expand All @@ -497,6 +515,7 @@ impl Palette {
panel_bg: Color::Rgb(250, 244, 237),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(227, 217, 207),
selection_bg: Color::Rgb(242, 233, 225),
surface0: Color::Rgb(242, 233, 225),
surface1: Color::Rgb(255, 250, 243),
surface_dim: Color::Rgb(242, 233, 225),
Expand All @@ -520,7 +539,8 @@ impl Palette {
accent: Color::Rgb(255, 199, 153),
panel_bg: Color::Rgb(26, 26, 26),
sidebar_bg: Color::Reset,
active_row_bg: Color::Rgb(51, 51, 51),
active_row_bg: Color::Rgb(16, 16, 16),
selection_bg: Color::Rgb(35, 35, 35),
surface0: Color::Rgb(35, 35, 35),
surface1: Color::Rgb(40, 40, 40),
surface_dim: Color::Rgb(16, 16, 16),
Expand Down Expand Up @@ -578,6 +598,9 @@ impl Palette {
if let Some(c) = &custom.active_row_bg {
self.active_row_bg = parse_color(c);
}
if let Some(c) = &custom.selection_bg {
self.selection_bg = parse_color(c);
}
if let Some(c) = &custom.surface0 {
self.surface0 = parse_color(c);
}
Expand Down Expand Up @@ -2398,7 +2421,7 @@ mod tests {
let palette = Palette::from_name(name).unwrap();
let background_contrast = contrast_ratio(palette.panel_bg, palette.active_row_bg);
assert!(
background_contrast >= 1.25,
background_contrast >= 1.05,
"active row blends into the matching terminal background for {name}: {background_contrast:.2}:1"
);

Expand All @@ -2407,9 +2430,31 @@ mod tests {
text_contrast >= 3.0,
"active row text loses contrast for {name}: {text_contrast:.2}:1"
);
}
}

#[test]
fn built_in_selection_rows_stay_distinct_from_background_and_active_rows() {
for name in THEME_NAMES
.iter()
.copied()
.filter(|name| *name != "terminal")
{
let palette = Palette::from_name(name).unwrap();
let background_contrast = contrast_ratio(palette.panel_bg, palette.selection_bg);
assert!(
background_contrast >= 1.05,
"selection row blends into the matching terminal background for {name}: {background_contrast:.2}:1"
);

let text_contrast = contrast_ratio(palette.text, palette.selection_bg);
assert!(
text_contrast >= 3.0,
"selection row text loses contrast for {name}: {text_contrast:.2}:1"
);
assert_ne!(
palette.active_row_bg, palette.surface_dim,
"active row still shares the separator color for {name}"
palette.selection_bg, palette.active_row_bg,
"selection row shares the active row color for {name}"
);
}
}
Expand All @@ -2431,12 +2476,14 @@ mod tests {
let custom = crate::config::CustomThemeColors {
sidebar_bg: Some("#181825".to_string()),
active_row_bg: Some("#313244".to_string()),
selection_bg: Some("#45475a".to_string()),
..Default::default()
};
let palette = Palette::catppuccin().with_overrides(&custom);

assert_eq!(palette.sidebar_bg, Color::Rgb(24, 24, 37));
assert_eq!(palette.active_row_bg, Color::Rgb(49, 50, 68));
assert_eq!(palette.selection_bg, Color::Rgb(69, 71, 90));
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions src/config/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub struct CustomThemeColors {
pub panel_bg: Option<String>,
pub sidebar_bg: Option<String>,
pub active_row_bg: Option<String>,
pub selection_bg: Option<String>,
pub surface0: Option<String>,
pub surface1: Option<String>,
pub surface_dim: Option<String>,
Expand Down Expand Up @@ -266,6 +267,7 @@ name = "nord"
panel_bg = "#1e1e2e"
sidebar_bg = "#181825"
active_row_bg = "#313244"
selection_bg = "#45475a"
accent = "#ff79c6"
red = "rgb(255, 85, 85)"
"##;
Expand All @@ -275,6 +277,7 @@ red = "rgb(255, 85, 85)"
assert_eq!(custom.panel_bg.as_deref(), Some("#1e1e2e"));
assert_eq!(custom.sidebar_bg.as_deref(), Some("#181825"));
assert_eq!(custom.active_row_bg.as_deref(), Some("#313244"));
assert_eq!(custom.selection_bg.as_deref(), Some("#45475a"));
assert_eq!(custom.accent.as_deref(), Some("#ff79c6"));
assert_eq!(custom.red.as_deref(), Some("rgb(255, 85, 85)"));
assert!(custom.green.is_none());
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration
# Accepts: hex (#rrggbb), named colors, rgb(r,g,b), or panel_bg = "reset"
# [theme.custom]
# sidebar_bg = "#181825"
# active_row_bg = "#313244"
# active_row_bg = "#1e1e2e"
# selection_bg = "#313244"
# panel_bg = "reset"
# accent = "#f5c2e7"
# red = "#ff6188"
Expand Down
10 changes: 5 additions & 5 deletions src/ui/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -788,14 +788,14 @@ pub(super) fn render_sidebar_collapsed(app: &AppState, frame: &mut Frame, area:
let is_selected = visible_idx == app.selected && is_navigating;
let is_active = Some(visible_idx) == app.active;
let row_style = if is_selected {
Style::default().bg(p.surface0)
Style::default().bg(p.selection_bg)
} else if is_active {
Style::default().bg(p.active_row_bg)
} else {
Style::default()
};
let num_style = if is_selected {
Style::default().fg(p.overlay1).bg(p.surface0)
Style::default().fg(p.overlay1).bg(p.selection_bg)
} else if is_active {
Style::default().fg(p.text).bg(p.active_row_bg)
} else {
Expand Down Expand Up @@ -1248,7 +1248,7 @@ fn render_workspace_list(

if highlighted {
let bg = if selected {
p.surface0
p.selection_bg
} else if is_dragged {
p.surface1
} else {
Expand Down Expand Up @@ -1779,8 +1779,8 @@ rows = [[{ token = "workspace", bold = false }, { token = "agent", dim = false }
);
assert_eq!(
buffer[(0, selected_row)].bg,
app.palette.surface0,
"navigate selection should keep its existing surface0 background"
app.palette.selection_bg,
"navigate selection should use its dedicated cursor background"
);
}

Expand Down
Loading
Loading