Skip to content
Open
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
17 changes: 17 additions & 0 deletions docs/next/website/src/content/docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,23 @@ yellow = "#f9e2af"

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

When `auto_switch` is enabled, optional light and dark subtables layer on top of the shared custom colors:

```toml
[theme.custom]
accent = "#89b4fa"

[theme.custom.light]
panel_bg = "#eff1f5"
text = "#4c4f69"

[theme.custom.dark]
panel_bg = "#1e1e2e"
text = "#cdd6f4"
```

The active palette is applied in this order: built-in theme, `[theme.custom]`, then `[theme.custom.light]` or `[theme.custom.dark]`. Omitting the mode subtables preserves the existing shared override behavior.

## UI and sidebar

The sidebar is the main Herdr dashboard. Search `ui.` in the [Config reference](/docs/config-reference/) for sizing, collapsed mode, Agent panel ordering, mouse behavior, pane borders, and other presentation settings.
Expand Down
204 changes: 204 additions & 0 deletions docs/next/website/src/data/config-reference.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,210 @@
"type": "color",
"default": "unset",
"description": "Override the peach color token on top of the base theme. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.accent",
"type": "color",
"default": "unset",
"description": "Override the accent color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.panel_bg",
"type": "color",
"default": "unset",
"description": "Override the panel_bg color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.sidebar_bg",
"type": "color",
"default": "unset",
"description": "Override the sidebar_bg color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.surface0",
"type": "color",
"default": "unset",
"description": "Override the surface0 color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.surface1",
"type": "color",
"default": "unset",
"description": "Override the surface1 color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.surface_dim",
"type": "color",
"default": "unset",
"description": "Override the surface_dim color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.overlay0",
"type": "color",
"default": "unset",
"description": "Override the overlay0 color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.overlay1",
"type": "color",
"default": "unset",
"description": "Override the overlay1 color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.text",
"type": "color",
"default": "unset",
"description": "Override the text color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.subtext0",
"type": "color",
"default": "unset",
"description": "Override the subtext0 color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.mauve",
"type": "color",
"default": "unset",
"description": "Override the mauve color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.green",
"type": "color",
"default": "unset",
"description": "Override the green color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.yellow",
"type": "color",
"default": "unset",
"description": "Override the yellow color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.red",
"type": "color",
"default": "unset",
"description": "Override the red color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.blue",
"type": "color",
"default": "unset",
"description": "Override the blue color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.teal",
"type": "color",
"default": "unset",
"description": "Override the teal color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.light.peach",
"type": "color",
"default": "unset",
"description": "Override the peach color token when auto_switch selects a light appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.accent",
"type": "color",
"default": "unset",
"description": "Override the accent color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.panel_bg",
"type": "color",
"default": "unset",
"description": "Override the panel_bg color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.sidebar_bg",
"type": "color",
"default": "unset",
"description": "Override the sidebar_bg color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.surface0",
"type": "color",
"default": "unset",
"description": "Override the surface0 color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.surface1",
"type": "color",
"default": "unset",
"description": "Override the surface1 color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.surface_dim",
"type": "color",
"default": "unset",
"description": "Override the surface_dim color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.overlay0",
"type": "color",
"default": "unset",
"description": "Override the overlay0 color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.overlay1",
"type": "color",
"default": "unset",
"description": "Override the overlay1 color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.text",
"type": "color",
"default": "unset",
"description": "Override the text color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.subtext0",
"type": "color",
"default": "unset",
"description": "Override the subtext0 color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.mauve",
"type": "color",
"default": "unset",
"description": "Override the mauve color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.green",
"type": "color",
"default": "unset",
"description": "Override the green color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.yellow",
"type": "color",
"default": "unset",
"description": "Override the yellow color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.red",
"type": "color",
"default": "unset",
"description": "Override the red color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.blue",
"type": "color",
"default": "unset",
"description": "Override the blue color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.teal",
"type": "color",
"default": "unset",
"description": "Override the teal color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
},
{
"key": "theme.custom.dark.peach",
"type": "color",
"default": "unset",
"description": "Override the peach color token when auto_switch selects a dark appearance. Applied after theme.custom. Accepts hex, named colors, rgb(r,g,b), or reset aliases."
}
]
},
Expand Down
74 changes: 67 additions & 7 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ fn resolve_palette_for_theme_name(
name: &str,
fallback_name: &str,
runtime: &state::ThemeRuntimeConfig,
mode_custom: Option<&crate::config::ModeThemeColors>,
) -> state::Palette {
let mut palette = state::Palette::from_name(name).unwrap_or_else(|| {
tracing::warn!(
Expand All @@ -336,6 +337,9 @@ fn resolve_palette_for_theme_name(
if let Some(accent) = &runtime.legacy_accent {
palette.accent = crate::config::parse_color(accent);
}
if let Some(custom) = mode_custom {
palette = palette.with_mode_overrides(custom);
}

palette
}
Expand All @@ -344,18 +348,30 @@ fn resolve_effective_theme(
runtime: &state::ThemeRuntimeConfig,
appearance: Option<crate::terminal_theme::HostAppearance>,
) -> (state::Palette, String) {
let (name, fallback) = if runtime.auto_switch {
let (name, fallback, mode_custom) = if runtime.auto_switch {
match appearance.unwrap_or(crate::terminal_theme::HostAppearance::Dark) {
crate::terminal_theme::HostAppearance::Dark => (&runtime.dark_name, "catppuccin"),
crate::terminal_theme::HostAppearance::Light => {
(&runtime.light_name, "catppuccin-latte")
}
crate::terminal_theme::HostAppearance::Dark => (
&runtime.dark_name,
"catppuccin",
runtime
.custom
.as_ref()
.and_then(|custom| custom.dark.as_ref()),
),
crate::terminal_theme::HostAppearance::Light => (
&runtime.light_name,
"catppuccin-latte",
runtime
.custom
.as_ref()
.and_then(|custom| custom.light.as_ref()),
),
}
} else {
(&runtime.manual_name, "catppuccin")
(&runtime.manual_name, "catppuccin", None)
};
(
resolve_palette_for_theme_name(name, fallback, runtime),
resolve_palette_for_theme_name(name, fallback, runtime, mode_custom),
name.clone(),
)
}
Expand Down Expand Up @@ -2730,6 +2746,50 @@ mod tests {
);
}

#[test]
fn theme_auto_switch_layers_active_mode_overrides_last() {
let mut config = Config::default();
config.theme.name = Some("gruvbox".to_string());
config.theme.auto_switch = true;
config.theme.custom = Some(crate::config::CustomThemeColors {
accent: Some("#010203".to_string()),
text: Some("#040506".to_string()),
light: Some(crate::config::ModeThemeColors {
accent: Some("#070809".to_string()),
..Default::default()
}),
dark: Some(crate::config::ModeThemeColors {
text: Some("#0a0b0c".to_string()),
sidebar_bg: Some("#0d0e0f".to_string()),
..Default::default()
}),
..Default::default()
});
let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel();
let mut app = App::new(&config, true, None, api_rx, crate::api::EventHub::default());

assert_eq!(
app.state.palette.accent,
ratatui::style::Color::Rgb(1, 2, 3)
);
assert_eq!(
app.state.palette.text,
ratatui::style::Color::Rgb(10, 11, 12)
);
assert_eq!(
app.state.palette.sidebar_bg,
ratatui::style::Color::Rgb(13, 14, 15)
);

app.set_host_terminal_appearance(crate::terminal_theme::HostAppearance::Light, true);

assert_eq!(
app.state.palette.accent,
ratatui::style::Color::Rgb(7, 8, 9)
);
assert_eq!(app.state.palette.text, ratatui::style::Color::Rgb(4, 5, 6));
}

#[test]
fn inferred_background_appearance_does_not_override_explicit_report() {
let mut config = Config::default();
Expand Down
Loading
Loading