From 9c9490d764d306b6cc093b5b3de1ccd4e6467c94 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Thu, 16 Jul 2026 03:28:57 +0300 Subject: feat: configure collapsed sidebar startup state refs #1463 --- src/app/mod.rs | 32 +++++++++++++++++++++++++++++++- src/config/model.rs | 16 ++++++++++++++++ src/main.rs | 3 +++ 3 files changed, 50 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/app/mod.rs b/src/app/mod.rs index 6ea84746..8909fb1f 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -602,7 +602,7 @@ impl App { mobile_width_threshold: config.ui.mobile_width_threshold, sidebar_width_source, sidebar_width_auto: false, - sidebar_collapsed: false, + sidebar_collapsed: config.ui.sidebar_start_collapsed, sidebar_collapsed_mode: config.ui.sidebar_collapsed_mode, sidebar_section_split, agent_panel_sort, @@ -2373,6 +2373,17 @@ mod tests { assert_eq!(app.state.agent_panel_sort, state::AgentPanelSort::Priority); } + #[test] + fn startup_uses_configured_sidebar_state() { + let mut config = Config::default(); + config.ui.sidebar_start_collapsed = true; + let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); + + let app = App::new(&config, true, None, api_rx, crate::api::EventHub::default()); + + assert!(app.state.sidebar_collapsed); + } + #[test] fn startup_uses_redraw_on_focus_gained_config() { let mut config = Config::default(); @@ -2774,6 +2785,25 @@ mod tests { let _ = std::fs::remove_dir_all(path.parent().unwrap()); } + #[test] + fn reload_config_does_not_reset_sidebar_to_startup_state() { + let _guard = config_env_lock().lock().unwrap(); + let path = temp_config_path("reload-config-sidebar-start-collapsed"); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + std::env::set_var(crate::config::CONFIG_PATH_ENV_VAR, &path); + + let mut app = test_app(); + assert!(!app.state.sidebar_collapsed); + + std::fs::write(&path, "[ui]\nsidebar_start_collapsed = true\n").unwrap(); + let report = app.reload_config(); + assert_eq!(report.status, crate::config::ConfigReloadStatus::Applied); + assert!(!app.state.sidebar_collapsed); + + std::env::remove_var(crate::config::CONFIG_PATH_ENV_VAR); + let _ = std::fs::remove_dir_all(path.parent().unwrap()); + } + #[test] fn reload_config_updates_sidebar_collapsed_mode() { let _guard = config_env_lock().lock().unwrap(); diff --git a/src/config/model.rs b/src/config/model.rs index 60dd2471..b32dc8a9 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -780,6 +780,8 @@ pub struct UiConfig { pub sidebar_min_width: u16, /// Maximum sidebar width (columns) when expanded. Default: 36. pub sidebar_max_width: u16, + /// Start with the sidebar collapsed. Default: false. + pub sidebar_start_collapsed: bool, /// Collapsed sidebar presentation. Default: compact. pub sidebar_collapsed_mode: SidebarCollapsedModeConfig, /// Terminal width at or below which Herdr uses the mobile single-column layout. Default: 64. @@ -990,6 +992,7 @@ impl Default for UiConfig { sidebar_width: 26, sidebar_min_width: 18, sidebar_max_width: 36, + sidebar_start_collapsed: false, sidebar_collapsed_mode: SidebarCollapsedModeConfig::Compact, mobile_width_threshold: DEFAULT_MOBILE_WIDTH_THRESHOLD, mouse_capture: true, @@ -1363,6 +1366,19 @@ mobile_width_threshold = 96 assert_eq!(config.ui.mobile_width_threshold, 96); } + #[test] + fn sidebar_start_collapsed_defaults_off_and_parses_on() { + let default_config = Config::default(); + assert!(!default_config.ui.sidebar_start_collapsed); + + let toml = r#" +[ui] +sidebar_start_collapsed = true +"#; + let config: Config = toml::from_str(toml).unwrap(); + assert!(config.ui.sidebar_start_collapsed); + } + #[test] fn sidebar_collapsed_mode_defaults_compact_and_parses_hidden() { let default_config = Config::default(); diff --git a/src/main.rs b/src/main.rs index e1d9d87f..c48951c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -253,6 +253,9 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration # Maximum sidebar width when expanded (columns) # sidebar_max_width = 36 +# Start with the sidebar collapsed. Changes take effect on the next launch. +# sidebar_start_collapsed = false + # Collapsed sidebar presentation: "compact" keeps the narrow status rail, "hidden" uses zero width. # sidebar_collapsed_mode = "compact" -- cgit v1.2.3