aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-15 15:14:19 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-15 15:14:19 +0300
commit4bfc3a89c570aea8c9e4c91a7b249b50c112f1d2 (patch)
tree6141328f8d71220f26ad2c70967df368b8940763
parent7c636e97f4568583651de3bc8c8550a3bab54743 (diff)
config: split experimental settings from advanced
-rw-r--r--.pi/docs/CONFIGURATION.md34
-rw-r--r--src/app/mod.rs15
-rw-r--r--src/client/mod.rs2
-rw-r--r--src/config/io.rs8
-rw-r--r--src/config/model.rs36
-rw-r--r--src/main.rs11
6 files changed, 76 insertions, 30 deletions
diff --git a/.pi/docs/CONFIGURATION.md b/.pi/docs/CONFIGURATION.md
index 24bbe8c5..4336185e 100644
--- a/.pi/docs/CONFIGURATION.md
+++ b/.pi/docs/CONFIGURATION.md
@@ -44,12 +44,13 @@ Reloadable now:
- `ui.agent_panel_scope`
- `ui.toast.delivery`
- server-side `ui.sound` policy; attached thin clients refresh local sound config after a successful sound-policy change
+- `experimental.kitty_graphics`
- `advanced.scrollback_limit_bytes` for panes created after reload
- `ui.sidebar_width` as the default width; current width updates only while it is still config-owned
Startup-only or special-case:
- `onboarding` does not reopen onboarding during reload
-- `advanced.allow_nested` is checked before launch and needs a restart
+- `experimental.allow_nested` is checked before launch and needs a restart
- existing pane scrollback buffers are not resized during reload
- terminal notifications and sounds are client-local side effects and are sent to the foreground attached client
@@ -365,20 +366,20 @@ available agent keys:
- `droid`
- `amp`
-## advanced
+## experimental
```toml
-[advanced]
+[experimental]
allow_nested = false
-scrollback_limit_bytes = 10000000
+kitty_graphics = false
```
### options
| option | default | description |
|--------|---------|-------------|
-| `advanced.allow_nested` | `false` | allow launching herdr from inside a herdr-managed pane |
-| `advanced.scrollback_limit_bytes` | `10000000` | maximum scrollback buffer size in bytes retained per pane terminal |
+| `experimental.allow_nested` | `false` | allow launching herdr from inside a herdr-managed pane |
+| `experimental.kitty_graphics` | `false` | enable experimental local Kitty graphics rendering for attached clients |
### nested launches
@@ -386,6 +387,25 @@ By default, herdr blocks nested launches when `HERDR_ENV=1` is already present.
Set `allow_nested = true` only for debugging or intentionally nested setups.
+### Kitty graphics
+
+`kitty_graphics` enables experimental local Kitty graphics rendering for attached clients.
+
+It requires a Kitty graphics-compatible outer terminal.
+
+## advanced
+
+```toml
+[advanced]
+scrollback_limit_bytes = 10000000
+```
+
+### options
+
+| option | default | description |
+|--------|---------|-------------|
+| `advanced.scrollback_limit_bytes` | `10000000` | maximum scrollback buffer size in bytes retained per pane terminal |
+
### scrollback
`scrollback_limit_bytes` limits retained terminal scrollback per pane.
@@ -394,7 +414,7 @@ The default matches Ghostty's `scrollback-limit` value.
Set `scrollback_limit_bytes = 0` to disable pane scrollback entirely.
-The old `advanced.scrollback_lines` key is still accepted as a compatibility alias, but it uses the same byte-based value.
+The legacy `scrollback_lines` key is still accepted inside `[advanced]`, but it uses the same byte-based value.
## environment variables
diff --git a/src/app/mod.rs b/src/app/mod.rs
index b56e8fc9..7c3bae76 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -197,7 +197,7 @@ impl App {
event_hub: crate::api::EventHub,
) -> Self {
let (prefix_code, prefix_mods) = config.prefix_key();
- crate::kitty_graphics::set_enabled(config.advanced.kitty_graphics);
+ crate::kitty_graphics::set_enabled(config.experimental.kitty_graphics);
let (event_tx, event_rx) = mpsc::channel::<AppEvent>(64);
let render_notify = Arc::new(Notify::new());
let render_dirty = Arc::new(AtomicBool::new(false));
@@ -370,7 +370,7 @@ impl App {
mouse_capture: config.ui.mouse_capture,
confirm_close: config.ui.confirm_close,
show_agent_labels_on_pane_borders: config.ui.show_agent_labels_on_pane_borders,
- kitty_graphics_enabled: config.advanced.kitty_graphics,
+ kitty_graphics_enabled: config.experimental.kitty_graphics,
pane_scrollback_limit_bytes: config.advanced.scrollback_limit_bytes,
accent: crate::config::parse_color(&config.ui.accent),
sound: config.ui.sound.clone(),
@@ -718,13 +718,16 @@ impl App {
self.state.toast_config = config.ui.toast.clone();
}
- if !invalid_section("advanced") {
+ if !invalid_section("experimental") {
let was_kitty_graphics_enabled = self.state.kitty_graphics_enabled;
- self.state.kitty_graphics_enabled = config.advanced.kitty_graphics;
- crate::kitty_graphics::set_enabled(config.advanced.kitty_graphics);
- if was_kitty_graphics_enabled && !config.advanced.kitty_graphics {
+ self.state.kitty_graphics_enabled = config.experimental.kitty_graphics;
+ crate::kitty_graphics::set_enabled(config.experimental.kitty_graphics);
+ if was_kitty_graphics_enabled && !config.experimental.kitty_graphics {
let _ = crate::kitty_graphics::clear_all_host_graphics();
}
+ }
+
+ if !invalid_section("advanced") {
self.state.pane_scrollback_limit_bytes = config.advanced.scrollback_limit_bytes;
}
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 50a54ef3..1758ffdc 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -309,7 +309,7 @@ pub fn run_client() -> io::Result<()> {
let loaded_config = crate::config::Config::load();
let sound_config = loaded_config.config.ui.sound;
- let kitty_graphics_enabled = loaded_config.config.advanced.kitty_graphics;
+ let kitty_graphics_enabled = loaded_config.config.experimental.kitty_graphics;
let socket_path = client_socket_path();
crate::logging::startup("client");
diff --git a/src/config/io.rs b/src/config/io.rs
index 16602972..60eb54d8 100644
--- a/src/config/io.rs
+++ b/src/config/io.rs
@@ -166,6 +166,14 @@ fn load_live_config_from_str(content: &str) -> Result<LoadedConfig, Vec<String>>
&mut invalid_sections,
|section| config.advanced = section,
);
+ load_live_section(
+ table,
+ "experimental",
+ "experimental config",
+ &mut diagnostics,
+ &mut invalid_sections,
+ |section| config.experimental = section,
+ );
Ok(LoadedConfig {
config,
diff --git a/src/config/model.rs b/src/config/model.rs
index a8485fb0..8ea52724 100644
--- a/src/config/model.rs
+++ b/src/config/model.rs
@@ -56,6 +56,7 @@ pub struct Config {
pub keys: KeysConfig,
pub ui: UiConfig,
pub advanced: AdvancedConfig,
+ pub experimental: ExperimentalConfig,
}
#[derive(Debug)]
@@ -150,13 +151,18 @@ pub struct UiConfig {
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct AdvancedConfig {
+ /// Maximum scrollback buffer size in bytes retained per pane terminal. Default: 10000000.
+ #[serde(alias = "scrollback_lines")]
+ pub scrollback_limit_bytes: usize,
+}
+
+#[derive(Debug, Default, Deserialize)]
+#[serde(default)]
+pub struct ExperimentalConfig {
/// Allow launching herdr inside an existing herdr pane. Default: false.
pub allow_nested: bool,
/// Experimental local Kitty graphics rendering for attached clients. Default: false.
pub kitty_graphics: bool,
- /// Maximum scrollback buffer size in bytes retained per pane terminal. Default: 10000000.
- #[serde(alias = "scrollback_lines")]
- pub scrollback_limit_bytes: usize,
}
impl Default for KeysConfig {
@@ -242,8 +248,6 @@ impl<'de> Deserialize<'de> for ToastConfig {
impl Default for AdvancedConfig {
fn default() -> Self {
Self {
- allow_nested: false,
- kitty_graphics: false,
scrollback_limit_bytes: DEFAULT_SCROLLBACK_LIMIT_BYTES,
}
}
@@ -364,27 +368,35 @@ delivery = "terminal"
#[test]
fn kitty_graphics_default_off_and_parse() {
let config = Config::default();
- assert!(!config.advanced.kitty_graphics);
+ assert!(!config.experimental.kitty_graphics);
let toml = r#"
-[advanced]
+[experimental]
kitty_graphics = true
"#;
let config: Config = toml::from_str(toml).unwrap();
- assert!(config.advanced.kitty_graphics);
+ assert!(config.experimental.kitty_graphics);
}
#[test]
- fn advanced_config_parses() {
+ fn experimental_config_parses() {
let toml = r#"
-[advanced]
+[experimental]
allow_nested = true
kitty_graphics = true
+"#;
+ let config: Config = toml::from_str(toml).unwrap();
+ assert!(config.experimental.allow_nested);
+ assert!(config.experimental.kitty_graphics);
+ }
+
+ #[test]
+ fn advanced_config_parses() {
+ let toml = r#"
+[advanced]
scrollback_limit_bytes = 12345
"#;
let config: Config = toml::from_str(toml).unwrap();
- assert!(config.advanced.allow_nested);
- assert!(config.advanced.kitty_graphics);
assert_eq!(config.advanced.scrollback_limit_bytes, 12345);
}
diff --git a/src/main.rs b/src/main.rs
index 92e6220d..ecfb9f07 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -159,12 +159,14 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration
# [ui.sound.agents]
# droid = "off"
-[advanced]
+[experimental]
# Allow launching herdr from inside a herdr-managed pane.
# allow_nested = false
# Experimental local Kitty graphics rendering for attached clients.
-# Requires a Kitty graphics-compatible outer terminal. Detach/headless replay is not supported yet.
+# Requires a Kitty graphics-compatible outer terminal.
# kitty_graphics = false
+
+[advanced]
# Maximum scrollback buffer size in bytes retained per pane terminal.
# Matches Ghostty's default scrollback-limit behavior.
# scrollback_limit_bytes = 10000000
@@ -175,7 +177,7 @@ fn should_block_nested(config: &config::Config) -> bool {
}
fn should_block_nested_for_env(config: &config::Config, herdr_env: Option<&str>) -> bool {
- !config.advanced.allow_nested && herdr_env == Some(HERDR_ENV_VALUE)
+ !config.experimental.allow_nested && herdr_env == Some(HERDR_ENV_VALUE)
}
fn random_nested_message() -> &'static str {
@@ -539,7 +541,8 @@ mod tests {
#[test]
fn nested_herdr_does_not_block_when_allowed() {
- let config: config::Config = toml::from_str("[advanced]\nallow_nested = true\n").unwrap();
+ let config: config::Config =
+ toml::from_str("[experimental]\nallow_nested = true\n").unwrap();
assert!(!should_block_nested_for_env(&config, Some(HERDR_ENV_VALUE)));
}