diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-09-04 21:59:13 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-09-04 21:59:13 +0300 |
| commit | a2b128978a154c02311b90da778ca115758f75d7 (patch) | |
| tree | 429d5ea3f44c91b85ed54138a1b4a6ad7ea1e1a4 /src | |
| parent | 6045fe6a8735609507ec8600fa07205227546e8b (diff) | |
feat: enable kitty graphics by default
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/server/pane_graphics_stream.rs | 2 | ||||
| -rw-r--r-- | src/app/api/pane_graphics.rs | 2 | ||||
| -rw-r--r-- | src/app/mod.rs | 49 | ||||
| -rw-r--r-- | src/app/pane_graphics.rs | 1 | ||||
| -rw-r--r-- | src/client/mod.rs | 2 | ||||
| -rw-r--r-- | src/config.rs | 7 | ||||
| -rw-r--r-- | src/config/model.rs | 48 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/server/headless/tests/pane_graphics.rs | 1 |
9 files changed, 93 insertions, 25 deletions
diff --git a/src/api/server/pane_graphics_stream.rs b/src/api/server/pane_graphics_stream.rs index b6420f95..e4d3315d 100644 --- a/src/api/server/pane_graphics_stream.rs +++ b/src/api/server/pane_graphics_stream.rs @@ -798,7 +798,7 @@ mod tests { .send(super::super::error_response_json( open.request.id, "feature_disabled", - "pane graphics require experimental.kitty_graphics".into(), + "pane graphics are disabled by terminal.kitty_graphics".into(), )) .unwrap(); diff --git a/src/app/api/pane_graphics.rs b/src/app/api/pane_graphics.rs index 3dddccc9..649282b5 100644 --- a/src/app/api/pane_graphics.rs +++ b/src/app/api/pane_graphics.rs @@ -536,7 +536,7 @@ fn require_enabled(app: &App, id: &str) -> Result<(), String> { encode_error( id.to_owned(), "feature_disabled", - "pane graphics require experimental.kitty_graphics", + "pane graphics are disabled by terminal.kitty_graphics", ) }) } diff --git a/src/app/mod.rs b/src/app/mod.rs index 02175481..ed2c5fd9 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -359,7 +359,7 @@ impl App { event_hub: crate::api::EventHub, ) -> Self { let (prefix_code, prefix_mods) = config.prefix_key(); - crate::kitty_graphics::set_enabled(config.experimental.kitty_graphics); + crate::kitty_graphics::set_enabled(config.kitty_graphics_enabled()); let (event_tx, event_rx) = mpsc::channel::<AppEvent>(APP_EVENT_CHANNEL_CAPACITY); let render_notify = Arc::new(Notify::new()); let render_dirty = Arc::new(crate::render_signal::RenderSignal::new()); @@ -494,7 +494,7 @@ impl App { cjk_ime_agent_filter_configured: !config.experimental.cjk_ime_agents.is_empty(), cjk_ime_agents: parse_cjk_ime_agents(&config.experimental.cjk_ime_agents), cjk_ime_cursor_shape: config.experimental.cjk_ime_cursor_shape.to_decscusr(), - kitty_graphics_enabled: config.experimental.kitty_graphics, + kitty_graphics_enabled: config.kitty_graphics_enabled(), default_shell: config.terminal.default_shell.clone(), shell_mode: config.terminal.shell_mode, new_terminal_cwd: config.terminal.new_cwd.clone(), @@ -841,14 +841,18 @@ impl App { } } + let graphics_config_valid = !invalid_section("terminal") + && (config.terminal.kitty_graphics.is_some() || !invalid_section("experimental")); + if graphics_config_valid + && config.kitty_graphics_enabled() != self.state.kitty_graphics_enabled + { + diagnostics.push( + "terminal.kitty_graphics changes require restarting Herdr; kept current setting" + .into(), + ); + } + if !invalid_section("experimental") { - let was_kitty_graphics_enabled = self.state.kitty_graphics_enabled; - 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 { - self.pane_graphics.clear(); - self.state.host_cell_size = crate::kitty_graphics::HostCellSize::default(); - } self.state.reveal_hidden_cursor_for_cjk_ime = config.experimental.reveal_hidden_cursor_for_cjk_ime; self.state.cjk_ime_agent_filter_configured = @@ -1722,6 +1726,33 @@ mod tests { } #[test] + fn reload_config_keeps_kitty_graphics_until_restart() { + let _guard = config_env_lock().lock().unwrap(); + let path = temp_config_path("reload-config-kitty-graphics"); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + std::fs::write(&path, "[terminal]\nkitty_graphics = false\n").unwrap(); + std::env::set_var(crate::config::CONFIG_PATH_ENV_VAR, &path); + + let mut app = test_app(); + assert!(app.state.kitty_graphics_enabled); + + let report = app.reload_config(); + + assert_eq!(report.status, crate::config::ConfigReloadStatus::Partial); + assert!(app.state.kitty_graphics_enabled); + assert_eq!( + report.diagnostics, + vec![ + "terminal.kitty_graphics changes require restarting Herdr; kept current setting" + .to_owned() + ] + ); + + std::env::remove_var(crate::config::CONFIG_PATH_ENV_VAR); + let _ = std::fs::remove_dir_all(path.parent().unwrap()); + } + + #[test] fn reload_config_requests_client_reload_for_key_only_change() { let _guard = config_env_lock().lock().unwrap(); let path = temp_config_path("reload-config-key-only"); diff --git a/src/app/pane_graphics.rs b/src/app/pane_graphics.rs index e6c43fa1..40081cd3 100644 --- a/src/app/pane_graphics.rs +++ b/src/app/pane_graphics.rs @@ -108,6 +108,7 @@ impl Runtime { self.revision = self.revision.wrapping_add(1); } + #[cfg(test)] pub(crate) fn clear(&mut self) { self.slots.clear(); self.mark_changed(); diff --git a/src/client/mod.rs b/src/client/mod.rs index b20a6e85..ea789431 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -371,7 +371,7 @@ fn run_client_with_mode( let host_cursor = loaded_config.config.ui.host_cursor; let remote_image_paste_key = client_remote_image_paste_key(&loaded_config.config); let kitty_graphics_enabled = - loaded_config.config.experimental.kitty_graphics && client_rendered_shell; + loaded_config.config.kitty_graphics_enabled() && client_rendered_shell; let pixel_geometry_enabled = kitty_graphics_enabled || attach_escape.is_some(); let loop_config = ClientLoopConfig { sound_config: loaded_config.config.ui.sound, diff --git a/src/config.rs b/src/config.rs index 117374ee..4c966453 100644 --- a/src/config.rs +++ b/src/config.rs @@ -95,6 +95,13 @@ impl Config { self.onboarding.unwrap_or(true) } + pub fn kitty_graphics_enabled(&self) -> bool { + self.terminal + .kitty_graphics + .or(self.experimental.kitty_graphics) + .unwrap_or(true) + } + pub fn prefix_key(&self) -> (KeyCode, KeyModifiers) { self.validated_keybinds().1 } diff --git a/src/config/model.rs b/src/config/model.rs index 552c19de..a400382c 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -258,6 +258,8 @@ pub struct TerminalConfig { pub shell_mode: ShellModeConfig, /// CWD policy for new interactive panes, tabs, and workspaces. pub new_cwd: NewTerminalCwdConfig, + /// Render Kitty graphics in compatible outer terminals. Default: true. + pub kitty_graphics: Option<bool>, } #[derive(Debug, Deserialize)] @@ -978,8 +980,8 @@ impl Default for RemoteConfig { 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, + /// Deprecated compatibility key for `terminal.kitty_graphics`. + pub kitty_graphics: Option<bool>, /// Persist pane screen history to session-history.json. Default: false. pub pane_history: bool, /// Expose the focused pane's cursor anchor to the outer terminal even when @@ -1911,16 +1913,41 @@ pane_history = true } #[test] - fn kitty_graphics_default_off_and_parse() { - let config = Config::default(); - assert!(!config.experimental.kitty_graphics); + fn kitty_graphics_default_on_with_stable_opt_out() { + assert!(Config::default().kitty_graphics_enabled()); + + let config: Config = toml::from_str( + r#" +[terminal] +kitty_graphics = false +"#, + ) + .unwrap(); + assert!(!config.kitty_graphics_enabled()); + } + + #[test] + fn legacy_experimental_kitty_graphics_setting_remains_compatible() { + let disabled: Config = toml::from_str( + r#" +[experimental] +kitty_graphics = false +"#, + ) + .unwrap(); + assert!(!disabled.kitty_graphics_enabled()); + + let stable_setting_wins: Config = toml::from_str( + r#" +[terminal] +kitty_graphics = false - let toml = r#" [experimental] kitty_graphics = true -"#; - let config: Config = toml::from_str(toml).unwrap(); - assert!(config.experimental.kitty_graphics); +"#, + ) + .unwrap(); + assert!(!stable_setting_wins.kitty_graphics_enabled()); } #[test] @@ -1934,7 +1961,8 @@ switch_ascii_input_source_in_prefix = true "#; let config: Config = toml::from_str(toml).unwrap(); assert!(config.experimental.allow_nested); - assert!(config.experimental.kitty_graphics); + assert_eq!(config.experimental.kitty_graphics, Some(true)); + assert!(config.kitty_graphics_enabled()); assert!(config.experimental.pane_history); assert!(config.experimental.switch_ascii_input_source_in_prefix); } diff --git a/src/main.rs b/src/main.rs index 91a6b2e4..ee38e84b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -114,6 +114,9 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration # "current" for Herdr's process directory, or a fixed path such as "~/Projects". # new_cwd = "follow" +# Render pane images in Kitty graphics-compatible outer terminals. +# kitty_graphics = true + [update] # Update channel used by background version checks and `herdr update`. # Stable builds default to "stable". Windows preview builds default to "preview" @@ -400,9 +403,6 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration [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. -# kitty_graphics = false # Save recent pane screen history across full server restarts. pane_history = false # While prefix mode is active, temporarily switch the host input source to diff --git a/src/server/headless/tests/pane_graphics.rs b/src/server/headless/tests/pane_graphics.rs index 6c0cd8b0..d310003c 100644 --- a/src/server/headless/tests/pane_graphics.rs +++ b/src/server/headless/tests/pane_graphics.rs @@ -540,6 +540,7 @@ fn rejected_or_stale_requests_do_not_schedule_rendering() { server.app.state.workspaces = vec![workspace]; server.app.state.active = Some(0); server.app.state.selected = 0; + server.app.state.kitty_graphics_enabled = false; let (respond_to, response_rx) = std::sync::mpsc::channel(); let changed = server.handle_api_request_with_shutdown_check(api::ApiRequestMessage { |
