From 34bf4f0ffec55abdc09a808e42008ce7c8d887be Mon Sep 17 00:00:00 2001 From: Kang Date: Wed, 29 Jul 2026 18:15:31 +0800 Subject: [PATCH] refactor(desktop): encapsulate settings compatibility state --- .../src/features/ai/ai_agent_runtime.rs | 2 +- .../commands/command_runtime/suggestions.rs | 16 +- .../commands/quick_command_runtime/catalog.rs | 10 +- .../src/features/connection_editor_window.rs | 2 +- .../connections/connection_runtime/actions.rs | 4 +- .../src/features/formatting/mod.rs | 2 +- .../features/layout/security_panel/chrome.rs | 4 +- .../src/features/layout/sidebar/shell.rs | 6 +- .../src/features/layout/title_bar/bar.rs | 28 +- .../src/features/layout/title_bar/menu.rs | 6 +- .../connections/editor/connection/mod.rs | 2 +- .../src/features/pages/settings/security.rs | 19 +- .../pages/settings/terminal/general.rs | 61 ++- .../pages/settings/terminal/keywords.rs | 8 +- .../pages/settings/terminal/search.rs | 2 +- .../pages/settings/transfer/advanced.rs | 19 +- .../pages/settings/transfer/editor.rs | 4 +- .../features/pages/settings/transfer/files.rs | 4 +- .../pages/settings/transfer/recording.rs | 10 +- .../pages/settings/workspace/appearance.rs | 32 +- .../pages/settings/workspace/general.rs | 20 +- .../pages/settings/workspace/interaction.rs | 26 +- .../pages/settings/workspace/keybindings.rs | 12 +- .../features/pages/transfers/browser/view.rs | 2 +- .../pages/transfers/browser_filter.rs | 2 +- .../features/pages/transfers/browser_keys.rs | 2 +- .../pages/transfers/browser_navigation.rs | 51 +- .../pages/transfers/browser_selection.rs | 2 +- .../features/pages/transfers/editor/open.rs | 8 +- .../src/features/pages/transfers/path_bar.rs | 2 +- .../panels/connection_import_overlay.rs | 2 +- .../features/panels/lock_screen_overlay.rs | 8 +- .../panels/quick_command_import_overlay.rs | 2 +- .../src/features/quick_command_window.rs | 2 +- .../src/features/remote_editor_window.rs | 2 +- crates/nyaterm-desktop/src/features/root.rs | 8 +- .../src/features/session/recording_runtime.rs | 18 +- .../session/session_dialog_runtime.rs | 2 +- .../src/features/session/session_lifecycle.rs | 2 +- .../src/features/session/session_order.rs | 2 +- .../session/session_runtime/background.rs | 4 +- .../features/session/session_runtime/start.rs | 6 +- .../session/startup_restore_runtime.rs | 14 +- .../features/session/temporary_ssh_link.rs | 4 +- .../src/features/settings/catalog.rs | 6 +- .../src/features/settings/mod.rs | 4 +- .../settings/settings_runtime/draft.rs | 50 +- .../src/features/settings/state.rs | 467 +++++++++++++++++- .../src/features/settings_window.rs | 2 +- .../features/shell/activity_bar_runtime.rs | 23 +- .../src/features/shell/appearance.rs | 129 +++-- .../src/features/shell/event_pump/bridge.rs | 2 +- .../src/features/shell/event_pump/mod.rs | 26 +- .../src/features/shell/event_pump/planes.rs | 4 +- .../shell/event_pump/session_events.rs | 2 +- .../features/shell/global_shortcut_runtime.rs | 2 +- .../shell/keybinding_runtime/keybindings.rs | 10 +- .../keybinding_runtime/keyword_highlights.rs | 72 +-- .../features/shell/panel_resize_runtime.rs | 116 +++-- .../src/features/shell/panel_stack_runtime.rs | 24 +- .../src/features/shell/tab_mouse.rs | 20 +- .../src/features/shell/tab_windows_runtime.rs | 8 +- .../src/features/shell/workspace_runtime.rs | 8 +- .../features/sync/cloud_sync_runtime/jobs.rs | 2 +- .../sync/cloud_sync_runtime/settings.rs | 12 +- .../terminal_context_menu_runtime/menu.rs | 6 +- .../terminal/terminal_runtime/buffer.rs | 68 ++- .../terminal/terminal_runtime/paste.rs | 6 +- .../terminal/terminal_runtime/sessions.rs | 12 +- .../terminal/terminal_runtime/view_io.rs | 70 +-- .../action_links.rs | 16 +- .../terminal_selection_runtime/helpers.rs | 2 +- .../terminal_selection_runtime/metrics.rs | 16 +- .../terminal_selection_runtime/selection.rs | 6 +- .../terminal/terminal_surface/canvas.rs | 50 +- .../features/transfer_external_sync_window.rs | 2 +- .../src/features/transfers/transfer_events.rs | 2 +- .../features/transfers/transfer_options.rs | 12 +- .../src/features/transfers/transfer_paths.rs | 12 +- docs/architecture/gpui-migration-status.md | 16 +- scripts/check-architecture-boundaries.sh | 21 + 81 files changed, 1105 insertions(+), 645 deletions(-) diff --git a/crates/nyaterm-desktop/src/features/ai/ai_agent_runtime.rs b/crates/nyaterm-desktop/src/features/ai/ai_agent_runtime.rs index 1f633b424..fcb124bfa 100644 --- a/crates/nyaterm-desktop/src/features/ai/ai_agent_runtime.rs +++ b/crates/nyaterm-desktop/src/features/ai/ai_agent_runtime.rs @@ -624,7 +624,7 @@ impl NyaTermApp { let observation_message = build_observation_message( &observation, &state.command, - &self.settings.summary.language, + &self.settings.summary().language, ); let settings = self.ai.settings.config.clone(); let terminal_session_id = state.terminal_session_id.clone(); diff --git a/crates/nyaterm-desktop/src/features/commands/command_runtime/suggestions.rs b/crates/nyaterm-desktop/src/features/commands/command_runtime/suggestions.rs index bfac00933..49a5501e9 100644 --- a/crates/nyaterm-desktop/src/features/commands/command_runtime/suggestions.rs +++ b/crates/nyaterm-desktop/src/features/commands/command_runtime/suggestions.rs @@ -199,13 +199,13 @@ impl NyaTermApp { { return; } - if self.settings.summary.terminal_low_latency_mode { + if self.settings.summary().terminal_low_latency_mode { self.clear_command_suggestion_draft(cx); finish!("low_latency_mode", 0); } if !self .settings - .summary + .summary() .interaction_command_suggestions_enabled { self.clear_command_suggestion_draft(cx); @@ -294,7 +294,7 @@ impl NyaTermApp { let min_chars_started_at = Instant::now(); let min_chars = self .settings - .summary + .summary() .interaction_command_suggestion_min_chars .max(1) as usize; let below_min_chars = terminal_input_tracker_below_min_chars( @@ -421,7 +421,7 @@ impl NyaTermApp { if this.terminal.assist.command_suggestion_search_gen != request_id { return None; } - if this.settings.summary.terminal_low_latency_mode { + if this.settings.summary().terminal_low_latency_mode { this.hide_command_suggestions_if_present(cx); return None; } @@ -528,7 +528,7 @@ impl NyaTermApp { } if !self .settings - .summary + .summary() .interaction_command_suggestions_enabled { let hide_started_at = Instant::now(); @@ -538,12 +538,12 @@ impl NyaTermApp { } let min_chars = self .settings - .summary + .summary() .interaction_command_suggestion_min_chars .max(1) as usize; let max_chars = self .settings - .summary + .summary() .interaction_command_suggestion_max_chars .max(min_chars as u32) as usize; let pattern_started_at = Instant::now(); @@ -599,7 +599,7 @@ impl NyaTermApp { || self.terminal.assist.command_suggestions_suppressed || !self .settings - .summary + .summary() .interaction_command_suggestions_enabled || get_tracked_command(&self.terminal.assist.command_input_tracker) != request.pattern { diff --git a/crates/nyaterm-desktop/src/features/commands/quick_command_runtime/catalog.rs b/crates/nyaterm-desktop/src/features/commands/quick_command_runtime/catalog.rs index 0057864cb..4ffc335ca 100644 --- a/crates/nyaterm-desktop/src/features/commands/quick_command_runtime/catalog.rs +++ b/crates/nyaterm-desktop/src/features/commands/quick_command_runtime/catalog.rs @@ -36,8 +36,8 @@ impl NyaTermApp { cx: &mut Context, ) { self.commands.set_quick_view_mode(mode); - self.settings.summary.ui_quick_cmd_view_mode = - quick_command_view_mode_setting(mode).to_string(); + self.settings + .set_quick_command_view_mode(quick_command_view_mode_setting(mode).to_string()); self.save_quick_command_ui_settings(cx); } @@ -47,8 +47,8 @@ impl NyaTermApp { cx: &mut Context, ) { self.commands.set_quick_sort_mode(mode); - self.settings.summary.ui_quick_cmd_sort_mode = - quick_command_sort_mode_setting(mode).to_string(); + self.settings + .set_quick_command_sort_mode(quick_command_sort_mode_setting(mode).to_string()); self.save_quick_command_ui_settings(cx); } @@ -57,7 +57,7 @@ impl NyaTermApp { self.runtime.config_dir(), self.runtime.portable_key_path().map(ToOwned::to_owned), ) - .and_then(|store| store.save_quick_command_ui_settings(&self.settings.summary)) + .and_then(|store| store.save_quick_command_ui_settings(self.settings.summary())) { Ok(settings) => { self.apply_gpui_settings(settings); diff --git a/crates/nyaterm-desktop/src/features/connection_editor_window.rs b/crates/nyaterm-desktop/src/features/connection_editor_window.rs index d67cb5538..5571b1591 100644 --- a/crates/nyaterm-desktop/src/features/connection_editor_window.rs +++ b/crates/nyaterm-desktop/src/features/connection_editor_window.rs @@ -40,7 +40,7 @@ impl Render for ConnectionEditorWindow { ( app.theme_palette(), app.gpui_ui_font_family(), - app.settings.summary.ui_font_size.clamp(12, 24) as f32, + app.settings.summary().ui_font_size.clamp(12, 24) as f32, app.connection_editor_title().to_string(), ) }); diff --git a/crates/nyaterm-desktop/src/features/connections/connection_runtime/actions.rs b/crates/nyaterm-desktop/src/features/connections/connection_runtime/actions.rs index 94259b529..0d403f7df 100644 --- a/crates/nyaterm-desktop/src/features/connections/connection_runtime/actions.rs +++ b/crates/nyaterm-desktop/src/features/connections/connection_runtime/actions.rs @@ -168,8 +168,8 @@ impl NyaTermApp { pub(in crate::features) fn cycle_connection_sort_mode(&mut self, cx: &mut Context) { let sort_mode = self.connection_state.cycle_list_sort_mode(); - self.settings.summary.ui_saved_connections_sort_mode = - sort_mode.persistence_id().to_string(); + self.settings + .set_saved_connections_sort_mode(sort_mode.persistence_id().to_string()); self.persist_ui_layout(); self.terminal.view.status = format!("connections sorted by {}", sort_mode.label()); cx.notify(); diff --git a/crates/nyaterm-desktop/src/features/formatting/mod.rs b/crates/nyaterm-desktop/src/features/formatting/mod.rs index 58969cc34..2fdef04e2 100644 --- a/crates/nyaterm-desktop/src/features/formatting/mod.rs +++ b/crates/nyaterm-desktop/src/features/formatting/mod.rs @@ -25,6 +25,6 @@ pub(in crate::features) use markdown::{ impl NyaTermApp { pub(in crate::features) fn tr(&self, key: &'static str) -> &'static str { - crate::i18n::text(&self.settings.summary.language, key) + crate::i18n::text(&self.settings.summary().language, key) } } diff --git a/crates/nyaterm-desktop/src/features/layout/security_panel/chrome.rs b/crates/nyaterm-desktop/src/features/layout/security_panel/chrome.rs index 8a1d11715..0f02a0004 100644 --- a/crates/nyaterm-desktop/src/features/layout/security_panel/chrome.rs +++ b/crates/nyaterm-desktop/src/features/layout/security_panel/chrome.rs @@ -13,7 +13,7 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let unlocked = - self.settings.summary.has_master_password && self.security.secrets_unlocked(); + self.settings.summary().has_master_password && self.security.secrets_unlocked(); let palette = self.theme_palette(); div() .id(SharedString::from("security-secrets-toggle")) @@ -81,7 +81,7 @@ impl NyaTermApp { .on_click(cx.listener(|this, _, window, cx| { if this.security_secrets_locked() { this.open_security_unlock_prompt(window, cx); - } else if this.settings.summary.has_master_password { + } else if this.settings.summary().has_master_password { this.lock_security_secrets(cx); } else { this.open_security_unlock_prompt(window, cx); diff --git a/crates/nyaterm-desktop/src/features/layout/sidebar/shell.rs b/crates/nyaterm-desktop/src/features/layout/sidebar/shell.rs index 8159517b5..3578e2662 100644 --- a/crates/nyaterm-desktop/src/features/layout/sidebar/shell.rs +++ b/crates/nyaterm-desktop/src/features/layout/sidebar/shell.rs @@ -50,7 +50,7 @@ impl NyaTermApp { NavItem::AiAssistant => self.ai_assistant_panel(cx).into_any_element(), NavItem::ActiveSessions => self.active_sessions_panel(cx).into_any_element(), NavItem::CommandHistory => self.command_history_panel(cx).into_any_element(), - NavItem::Stats if self.settings.summary.ui_show_remote_stats => { + NavItem::Stats if self.settings.summary().ui_show_remote_stats => { self.stats_view(cx).into_any_element() } NavItem::Stats => crate::features::inspector::disabled_inspector_panel( @@ -58,7 +58,7 @@ impl NyaTermApp { self.tr("panel.resourceMonitorDisabled"), ) .into_any_element(), - NavItem::Processes if self.settings.summary.ui_show_process_manager => { + NavItem::Processes if self.settings.summary().ui_show_process_manager => { self.processes_view(cx).into_any_element() } NavItem::Processes => crate::features::inspector::disabled_inspector_panel( @@ -66,7 +66,7 @@ impl NyaTermApp { self.tr("processManager.disabled"), ) .into_any_element(), - NavItem::Docker if self.settings.summary.ui_show_docker_manager => { + NavItem::Docker if self.settings.summary().ui_show_docker_manager => { self.docker_view(cx).into_any_element() } NavItem::Docker => crate::features::inspector::disabled_inspector_panel( diff --git a/crates/nyaterm-desktop/src/features/layout/title_bar/bar.rs b/crates/nyaterm-desktop/src/features/layout/title_bar/bar.rs index 5deaee7a2..aa477ae9e 100644 --- a/crates/nyaterm-desktop/src/features/layout/title_bar/bar.rs +++ b/crates/nyaterm-desktop/src/features/layout/title_bar/bar.rs @@ -30,7 +30,7 @@ impl NyaTermApp { let compact_layout = !cfg!(target_os = "macos"); let narrow_left = compact_layout && self.shell.viewport_size().0 < 1024.; let narrow_right = compact_layout && self.shell.viewport_size().0 < 768.; - let header_status_visible = self.settings.summary.ui_header_status_visible; + let header_status_visible = self.settings.summary().ui_header_status_visible; let header_status = self.header_status_content(); // Match Tauri Header: h-10. div() @@ -279,7 +279,8 @@ impl NyaTermApp { fn header_status_dropdown(&self, cx: &mut Context) -> impl IntoElement { let palette = self.theme_palette(); - let selected = HeaderStatusMode::from_setting(&self.settings.summary.ui_header_status_mode); + let selected = + HeaderStatusMode::from_setting(&self.settings.summary().ui_header_status_mode); let mut menu = div() .id("header-status-menu") .absolute() @@ -328,7 +329,7 @@ impl NyaTermApp { } fn header_status_content(&self) -> HeaderStatusContent { - let mode = HeaderStatusMode::from_setting(&self.settings.summary.ui_header_status_mode); + let mode = HeaderStatusMode::from_setting(&self.settings.summary().ui_header_status_mode); match mode { HeaderStatusMode::Session => HeaderStatusContent { icon_path: self.title_context_icon().unwrap_or(mode.icon_path()), @@ -336,7 +337,7 @@ impl NyaTermApp { }, HeaderStatusMode::DateTime => HeaderStatusContent { icon_path: mode.icon_path(), - label: format_header_datetime(local_now(), &self.settings.summary.language), + label: format_header_datetime(local_now(), &self.settings.summary().language), }, HeaderStatusMode::Resources | HeaderStatusMode::Host => { let label = self @@ -351,7 +352,8 @@ impl NyaTermApp { } fn remote_stats_header_label(&self, mode: HeaderStatusMode) -> Option { - if self.session.active_ssh_config().is_none() || !self.settings.summary.ui_show_remote_stats + if self.session.active_ssh_config().is_none() + || !self.settings.summary().ui_show_remote_stats { return None; } @@ -396,7 +398,7 @@ impl NyaTermApp { fn remote_stats_header_fallback(&self) -> String { if self.session.active_ssh_config().is_none() { self.tr("panel.resourceMonitorNoSession").to_string() - } else if !self.settings.summary.ui_show_remote_stats { + } else if !self.settings.summary().ui_show_remote_stats { self.tr("panel.resourceMonitorDisabled").to_string() } else { let stats = self.remote_ops.stats_presentation(); @@ -418,8 +420,8 @@ impl NyaTermApp { mode: HeaderStatusMode, cx: &mut Context, ) { - self.settings.summary.ui_header_status_mode = mode.persistence_id().to_string(); - self.settings.summary.ui_header_status_visible = true; + self.settings + .set_header_status_mode(mode.persistence_id().to_string()); self.shell.close_header_status_menu(); self.shell .set_header_status_rendered_minute(current_unix_minute()); @@ -432,7 +434,7 @@ impl NyaTermApp { visible: bool, cx: &mut Context, ) { - self.settings.summary.ui_header_status_visible = visible; + self.settings.set_header_status_visible(visible); self.shell.close_header_status_menu(); self.persist_header_status_settings(); cx.notify(); @@ -448,14 +450,14 @@ impl NyaTermApp { } pub(in crate::features) fn header_status_needs_remote_stats(&self) -> bool { - self.settings.summary.ui_header_status_visible - && HeaderStatusMode::from_setting(&self.settings.summary.ui_header_status_mode) + self.settings.summary().ui_header_status_visible + && HeaderStatusMode::from_setting(&self.settings.summary().ui_header_status_mode) .needs_remote_stats() } pub(in crate::features) fn header_status_clock_refresh_due(&self) -> bool { - self.settings.summary.ui_header_status_visible - && HeaderStatusMode::from_setting(&self.settings.summary.ui_header_status_mode) + self.settings.summary().ui_header_status_visible + && HeaderStatusMode::from_setting(&self.settings.summary().ui_header_status_mode) == HeaderStatusMode::DateTime && self.shell.header_status_rendered_minute() != current_unix_minute() } diff --git a/crates/nyaterm-desktop/src/features/layout/title_bar/menu.rs b/crates/nyaterm-desktop/src/features/layout/title_bar/menu.rs index 80b53459a..936fa51a1 100644 --- a/crates/nyaterm-desktop/src/features/layout/title_bar/menu.rs +++ b/crates/nyaterm-desktop/src/features/layout/title_bar/menu.rs @@ -327,7 +327,7 @@ impl NyaTermApp { match submenu { TitleMenuSubmenu::Theme => { for &theme in crate::theme::APPEARANCE_THEME_IDS { - let current = self.settings.summary.theme.as_str(); + let current = self.settings.summary().theme.as_str(); let selected = current == theme || (current == "catppuccin" && theme == "catppuccin-mocha"); let label = crate::theme::appearance_theme_label(theme); @@ -346,8 +346,8 @@ impl NyaTermApp { } } TitleMenuSubmenu::Language => { - let english = matches!(self.settings.summary.language.as_str(), "en" | "en-US"); - let chinese = matches!(self.settings.summary.language.as_str(), "zh" | "zh-CN"); + let english = matches!(self.settings.summary().language.as_str(), "en" | "en-US"); + let chinese = matches!(self.settings.summary().language.as_str(), "zh" | "zh-CN"); menu = menu .child(title_menu_item( palette, diff --git a/crates/nyaterm-desktop/src/features/pages/connections/editor/connection/mod.rs b/crates/nyaterm-desktop/src/features/pages/connections/editor/connection/mod.rs index a5e14caed..cd60a556b 100644 --- a/crates/nyaterm-desktop/src/features/pages/connections/editor/connection/mod.rs +++ b/crates/nyaterm-desktop/src/features/pages/connections/editor/connection/mod.rs @@ -53,7 +53,7 @@ impl NyaTermApp { cx: &mut Context, ) -> AnyElement { let palette = self.theme_palette(); - let language = self.settings.summary.language.clone(); + let language = self.settings.summary().language.clone(); let fields = ConnectionEditorFields::new( self.connection_state.editor_fields().clone(), self.connection_state.editor_menu_focus_handle(), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/security.rs b/crates/nyaterm-desktop/src/features/pages/settings/security.rs index b7167878a..c22d39a09 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/security.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/security.rs @@ -14,19 +14,21 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let palette = self.theme_palette(); + let master_password = self.settings.master_password(); + let master_password_draft = master_password.draft.to_string(); + let master_password_enabled = master_password.enabled; let master_password_input = self .text_input_box( "settings.security.master-password", - &self.settings.master_password.draft.clone(), + &master_password_draft, TextInputSetup::masked(), cx, ) .into_any_element(); - let master_password_enabled = self.settings.master_password.enabled; let master_password_switch_enabled = !self.cloud_sync.settings().enabled; - let has_stored_master_password = self.settings.summary.has_master_password; - let idle_minutes = self.settings.summary.idle_lock_minutes; - let host_key_policy = match self.settings.summary.host_key_policy.as_str() { + let has_stored_master_password = self.settings.summary().has_master_password; + let idle_minutes = self.settings.summary().idle_lock_minutes; + let host_key_policy = match self.settings.summary().host_key_policy.as_str() { "strict" | "reject" => "strict", "accept" | "accept_new" => "accept", _ => "prompt", @@ -91,8 +93,7 @@ impl NyaTermApp { )), )) .when( - has_stored_master_password - && self.settings.master_password.draft.is_empty(), + has_stored_master_password && master_password_draft.is_empty(), |this| { this.child( div() @@ -130,13 +131,13 @@ impl NyaTermApp { settings_switch( palette, "settings-screen-lock-enabled", - self.settings.summary.enable_screen_lock, + self.settings.summary().enable_screen_lock, cx.listener(|this, _, _, cx| { this.toggle_screen_lock_enabled(cx); }), ), )) - .when(self.settings.summary.enable_screen_lock, |this| { + .when(self.settings.summary().enable_screen_lock, |this| { this.child(settings_form_row( palette, idle_lock_label, diff --git a/crates/nyaterm-desktop/src/features/pages/settings/terminal/general.rs b/crates/nyaterm-desktop/src/features/pages/settings/terminal/general.rs index e112e5ec0..48317414f 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/terminal/general.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/terminal/general.rs @@ -20,12 +20,12 @@ impl NyaTermApp { let x11_display_input = self .text_input_box( "settings.terminal.x11-display", - &self.settings.summary.x11_display.clone(), + &self.settings.summary().x11_display.clone(), TextInputSetup::placeholder(self.tr("settings.x11DisplayPlaceholder")), cx, ) .into_any_element(); - let action_links_enabled = self.settings.summary.terminal_action_links_enabled; + let action_links_enabled = self.settings.summary().terminal_action_links_enabled; div() .flex() @@ -47,7 +47,10 @@ impl NyaTermApp { palette, "terminal-scrollback-minus", "terminal-scrollback-plus", - self.settings.summary.terminal_scrollback_lines.to_string(), + self.settings + .summary() + .terminal_scrollback_lines + .to_string(), cx.listener(|this, _, _, cx| { this.adjust_terminal_scrollback_lines(-100, cx); }), @@ -67,7 +70,7 @@ impl NyaTermApp { "terminal-keepalive-minus", "terminal-keepalive-plus", self.settings - .summary + .summary() .terminal_keep_alive_interval .to_string(), cx.listener(|this, _, _, cx| { @@ -99,7 +102,7 @@ impl NyaTermApp { settings_switch( palette, "terminal-hardware-acceleration", - self.settings.summary.terminal_hardware_acceleration, + self.settings.summary().terminal_hardware_acceleration, cx.listener(|this, _, _, cx| { this.toggle_terminal_hardware_acceleration(cx); }), @@ -112,7 +115,7 @@ impl NyaTermApp { settings_switch( palette, "terminal-low-latency-mode", - self.settings.summary.terminal_low_latency_mode, + self.settings.summary().terminal_low_latency_mode, cx.listener(|this, _, _, cx| { this.toggle_terminal_low_latency_mode(cx); }), @@ -127,7 +130,7 @@ impl NyaTermApp { settings_switch( palette, "terminal-workspace-padding", - self.settings.summary.terminal_show_workspace_padding, + self.settings.summary().terminal_show_workspace_padding, cx.listener(|this, _, _, cx| { this.toggle_terminal_workspace_padding(cx); }), @@ -140,7 +143,7 @@ impl NyaTermApp { settings_switch( palette, "terminal-line-numbers", - self.settings.summary.terminal_show_line_numbers, + self.settings.summary().terminal_show_line_numbers, cx.listener(|this, _, _, cx| { this.toggle_terminal_line_numbers(cx); }), @@ -153,13 +156,13 @@ impl NyaTermApp { settings_switch( palette, "terminal-timestamps", - self.settings.summary.terminal_show_timestamps, + self.settings.summary().terminal_show_timestamps, cx.listener(|this, _, _, cx| { this.toggle_terminal_timestamps(cx); }), ), )) - .when(self.settings.summary.terminal_show_timestamps, |this| { + .when(self.settings.summary().terminal_show_timestamps, |this| { this.child(settings_form_row( palette, self.tr("settings.showTimestampMilliseconds"), @@ -169,7 +172,7 @@ impl NyaTermApp { settings_switch( palette, "terminal-timestamp-ms", - self.settings.summary.terminal_show_timestamp_milliseconds, + self.settings.summary().terminal_show_timestamp_milliseconds, cx.listener(|this, _, _, cx| { this.toggle_terminal_timestamp_milliseconds(cx); }), @@ -185,7 +188,9 @@ impl NyaTermApp { settings_switch( palette, "terminal-multi-line-paste", - self.settings.summary.terminal_show_multi_line_paste_dialog, + self.settings + .summary() + .terminal_show_multi_line_paste_dialog, cx.listener(|this, _, _, cx| { this.toggle_multi_line_paste_dialog(cx); }), @@ -198,7 +203,7 @@ impl NyaTermApp { settings_switch( palette, "terminal-paste-image-path", - self.settings.summary.terminal_paste_image_as_path, + self.settings.summary().terminal_paste_image_as_path, cx.listener(|this, _, _, cx| { this.toggle_paste_image_as_path(cx); }), @@ -211,13 +216,13 @@ impl NyaTermApp { settings_switch( palette, "terminal-remote-stats", - self.settings.summary.ui_show_remote_stats, + self.settings.summary().ui_show_remote_stats, cx.listener(|this, _, _, cx| { this.toggle_remote_stats_panel(cx); }), ), )) - .when(self.settings.summary.ui_show_remote_stats, |this| { + .when(self.settings.summary().ui_show_remote_stats, |this| { this.child(settings_form_row( palette, self.tr("settings.remoteStatsInterval"), @@ -228,7 +233,7 @@ impl NyaTermApp { palette, "terminal-remote-stats-interval-minus", "terminal-remote-stats-interval-plus", - self.settings.summary.ui_remote_stats_interval.to_string(), + self.settings.summary().ui_remote_stats_interval.to_string(), cx.listener(|this, _, _, cx| { this.adjust_remote_stats_interval(-1, cx); }), @@ -247,13 +252,13 @@ impl NyaTermApp { settings_switch( palette, "terminal-process-manager", - self.settings.summary.ui_show_process_manager, + self.settings.summary().ui_show_process_manager, cx.listener(|this, _, _, cx| { this.toggle_process_manager_panel(cx); }), ), )) - .when(self.settings.summary.ui_show_process_manager, |this| { + .when(self.settings.summary().ui_show_process_manager, |this| { this.child(settings_form_row( palette, self.tr("settings.processManagerInterval"), @@ -265,7 +270,7 @@ impl NyaTermApp { "terminal-process-interval-minus", "terminal-process-interval-plus", self.settings - .summary + .summary() .ui_process_manager_interval .to_string(), cx.listener(|this, _, _, cx| { @@ -286,13 +291,13 @@ impl NyaTermApp { settings_switch( palette, "terminal-docker-manager", - self.settings.summary.ui_show_docker_manager, + self.settings.summary().ui_show_docker_manager, cx.listener(|this, _, _, cx| { this.toggle_docker_manager_panel(cx); }), ), )) - .when(self.settings.summary.ui_show_docker_manager, |this| { + .when(self.settings.summary().ui_show_docker_manager, |this| { this.child(settings_form_row( palette, self.tr("settings.dockerManagerInterval"), @@ -303,7 +308,10 @@ impl NyaTermApp { palette, "terminal-docker-interval-minus", "terminal-docker-interval-plus", - self.settings.summary.ui_docker_manager_interval.to_string(), + self.settings + .summary() + .ui_docker_manager_interval + .to_string(), cx.listener(|this, _, _, cx| { this.adjust_docker_manager_interval(-1, cx); }), @@ -353,7 +361,7 @@ impl NyaTermApp { self.tr("settings.actionLinksMatcherIpv4"), "192.168.1.1", self.tr("settings.actionLinksMatcherIpv4Desc"), - self.settings.summary.terminal_action_links_matchers.ipv4, + self.settings.summary().terminal_action_links_matchers.ipv4, action_links_enabled, cx.listener(|this, _, _, cx| { this.toggle_terminal_action_links_matcher("ipv4", cx); @@ -366,7 +374,7 @@ impl NyaTermApp { "localhost:8080", self.tr("settings.actionLinksMatcherHostPortDesc"), self.settings - .summary + .summary() .terminal_action_links_matchers .host_port, action_links_enabled, @@ -380,7 +388,10 @@ impl NyaTermApp { self.tr("settings.actionLinksMatcherArchive"), "backup.tar.gz", self.tr("settings.actionLinksMatcherArchiveDesc"), - self.settings.summary.terminal_action_links_matchers.archive, + self.settings + .summary() + .terminal_action_links_matchers + .archive, action_links_enabled, cx.listener(|this, _, _, cx| { this.toggle_terminal_action_links_matcher("archive", cx); diff --git a/crates/nyaterm-desktop/src/features/pages/settings/terminal/keywords.rs b/crates/nyaterm-desktop/src/features/pages/settings/terminal/keywords.rs index ce8bad6bd..590ebedda 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/terminal/keywords.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/terminal/keywords.rs @@ -19,8 +19,8 @@ impl NyaTermApp { ) -> impl IntoElement { let palette = self.theme_palette(); let is_dark = self.terminal_theme_is_dark(); - let rules = self.settings.keyword_config.rules.clone(); - let keyword_highlighting_enabled = self.settings.keyword_config.enabled; + let rules = self.settings.keyword_config().rules.clone(); + let keyword_highlighting_enabled = self.settings.keyword_config().enabled; let expanded = self.settings.keyword_highlight_presentation().expanded_id; let builtin_ids = nyaterm_core::builtin_keyword_rule_ids(); let pattern_count_template = self.tr("settings.keywordHighlightPatternCount"); @@ -63,7 +63,7 @@ impl NyaTermApp { settings_switch_with_enabled( palette, "settings-keyword-highlights-wrap", - self.settings.keyword_config.across_wrapped_lines, + self.settings.keyword_config().across_wrapped_lines, keyword_highlighting_enabled, cx.listener(|this, _, _, cx| { this.toggle_keyword_highlights_wrapped(cx); @@ -106,7 +106,7 @@ impl NyaTermApp { &id, is_dark, ); let enabled = self - .settings.keyword_config + .settings.keyword_config() .builtin_rules .get(&id) .copied() diff --git a/crates/nyaterm-desktop/src/features/pages/settings/terminal/search.rs b/crates/nyaterm-desktop/src/features/pages/settings/terminal/search.rs index 32518d77c..cccda3263 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/terminal/search.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/terminal/search.rs @@ -26,7 +26,7 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let palette = self.theme_palette(); - let engines = self.settings.summary.search_custom_engines.clone(); + let engines = self.settings.summary().search_custom_engines.clone(); let interaction = self.settings.search_engine_presentation(); let expanded_index = interaction.expanded_index; let icon_picker_index = interaction.icon_picker_index; diff --git a/crates/nyaterm-desktop/src/features/pages/settings/transfer/advanced.rs b/crates/nyaterm-desktop/src/features/pages/settings/transfer/advanced.rs index c1e248c13..90ae4448b 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/transfer/advanced.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/transfer/advanced.rs @@ -15,7 +15,7 @@ impl NyaTermApp { let palette = self.theme_palette(); let permissions = self .settings - .summary + .summary() .transfer_default_file_permissions .clone(); @@ -53,7 +53,12 @@ impl NyaTermApp { .text_size(px(12.)) .font_weight(FontWeight(700.)) .text_color(rgb(palette.text)) - .child(self.settings.summary.transfer_download_threads.to_string()), + .child( + self.settings + .summary() + .transfer_download_threads + .to_string(), + ), ) .child(small_button( palette, @@ -90,7 +95,7 @@ impl NyaTermApp { .text_size(px(12.)) .font_weight(FontWeight(700.)) .text_color(rgb(palette.text)) - .child(self.settings.summary.transfer_upload_threads.to_string()), + .child(self.settings.summary().transfer_upload_threads.to_string()), ) .child(small_button( palette, @@ -127,7 +132,7 @@ impl NyaTermApp { .text_size(px(12.)) .font_weight(FontWeight(700.)) .text_color(rgb(palette.text)) - .child(self.settings.summary.transfer_max_retries.to_string()), + .child(self.settings.summary().transfer_max_retries.to_string()), ) .child(small_button( palette, @@ -164,7 +169,7 @@ impl NyaTermApp { .text_size(px(12.)) .font_weight(FontWeight(700.)) .text_color(rgb(palette.text)) - .child(self.settings.summary.transfer_buffer_size.to_string()), + .child(self.settings.summary().transfer_buffer_size.to_string()), ) .child(small_button( palette, @@ -184,7 +189,7 @@ impl NyaTermApp { settings_switch( palette, "settings-transfer-preserve-timestamps", - self.settings.summary.transfer_preserve_timestamps, + self.settings.summary().transfer_preserve_timestamps, cx.listener(|this, _, _, cx| { this.toggle_transfer_preserve_timestamps(cx); }), @@ -199,7 +204,7 @@ impl NyaTermApp { settings_switch( palette, "settings-transfer-resume-broken", - self.settings.summary.transfer_resume_broken_transfer, + self.settings.summary().transfer_resume_broken_transfer, cx.listener(|this, _, _, cx| { this.toggle_transfer_resume_broken(cx); }), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/transfer/editor.rs b/crates/nyaterm-desktop/src/features/pages/settings/transfer/editor.rs index 9a86f5d9c..f7d19f61d 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/transfer/editor.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/transfer/editor.rs @@ -11,11 +11,11 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let palette = self.theme_palette(); - let editor_type = self.settings.summary.transfer_editor_type.clone(); + let editor_type = self.settings.summary().transfer_editor_type.clone(); let default_editor_input = self .text_input_box( "settings.transfer.default-editor", - &self.settings.summary.transfer_default_editor.clone(), + &self.settings.summary().transfer_default_editor.clone(), TextInputSetup::placeholder(self.tr("settings.defaultEditor")), cx, ) diff --git a/crates/nyaterm-desktop/src/features/pages/settings/transfer/files.rs b/crates/nyaterm-desktop/src/features/pages/settings/transfer/files.rs index 12abdcf63..619edfb73 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/transfer/files.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/transfer/files.rs @@ -17,7 +17,7 @@ impl NyaTermApp { let download_path_input = self .text_input_box( "settings.transfer.download-path", - &self.settings.summary.transfer_download_path.clone(), + &self.settings.summary().transfer_download_path.clone(), TextInputSetup::placeholder(self.tr("settings.downloadPath")), cx, ) @@ -63,7 +63,7 @@ impl NyaTermApp { settings_switch( palette, "transfer-ask-save", - self.settings.summary.transfer_ask_save_location, + self.settings.summary().transfer_ask_save_location, cx.listener(|this, _, _, cx| { this.toggle_transfer_ask_save_location(cx); }), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/transfer/recording.rs b/crates/nyaterm-desktop/src/features/pages/settings/transfer/recording.rs index 949a30531..5910d0afd 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/transfer/recording.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/transfer/recording.rs @@ -16,13 +16,13 @@ impl NyaTermApp { let recording_path_input = self .text_input_box( "settings.recording.path", - &self.settings.summary.recording_path.clone(), + &self.settings.summary().recording_path.clone(), TextInputSetup::placeholder(self.tr("settings.recordingPath")), cx, ) .into_any_element(); let memory_mib = - (self.settings.summary.recording_memory_limit_bytes / (1024 * 1024)).max(1); + (self.settings.summary().recording_memory_limit_bytes / (1024 * 1024)).max(1); div().flex().flex_col().gap_3().child(settings_form_section( palette, @@ -61,7 +61,7 @@ impl NyaTermApp { settings_switch( palette, "settings-recording-auto", - self.settings.summary.recording_auto_start, + self.settings.summary().recording_auto_start, cx.listener(|this, _, _, cx| { this.toggle_recording_auto_start(cx); }), @@ -76,7 +76,7 @@ impl NyaTermApp { settings_switch( palette, "settings-recording-labels", - self.settings.summary.recording_include_io_labels, + self.settings.summary().recording_include_io_labels, cx.listener(|this, _, _, cx| { this.toggle_recording_io_labels(cx); }), @@ -91,7 +91,7 @@ impl NyaTermApp { settings_switch( palette, "settings-recording-timestamps", - self.settings.summary.recording_include_timestamps, + self.settings.summary().recording_include_timestamps, cx.listener(|this, _, _, cx| { this.toggle_recording_timestamps(cx); }), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/workspace/appearance.rs b/crates/nyaterm-desktop/src/features/pages/settings/workspace/appearance.rs index 52801a660..01cc5cfeb 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/workspace/appearance.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/workspace/appearance.rs @@ -15,8 +15,8 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let palette = self.theme_palette(); - let font_size_label = self.settings.summary.terminal_font_size.to_string(); - let ui_font_size_label = self.settings.summary.ui_font_size.to_string(); + let font_size_label = self.settings.summary().terminal_font_size.to_string(); + let ui_font_size_label = self.settings.summary().ui_font_size.to_string(); div() .flex() @@ -71,7 +71,7 @@ impl NyaTermApp { { let path_label = self .settings - .summary + .summary() .background_image_path .as_deref() .map(|p| { @@ -91,7 +91,7 @@ impl NyaTermApp { } }) .unwrap_or_else(|| self.tr("settings.backgroundImageEmpty").to_string()); - let has_image = self.settings.summary.background_image_path.is_some(); + let has_image = self.settings.summary().background_image_path.is_some(); div() .flex() .flex_col() @@ -228,7 +228,7 @@ impl NyaTermApp { settings_switch( palette, "appearance-cursor-blink", - self.settings.summary.cursor_blink, + self.settings.summary().cursor_blink, cx.listener(|this, _, _, cx| { this.toggle_cursor_blink(cx); }), @@ -247,7 +247,7 @@ impl NyaTermApp { ( self.tr("settings.terminalFontFamily"), self.tr("settings.terminalFontFamilyDesc"), - self.settings.summary.terminal_font_family.clone(), + self.settings.summary().terminal_font_family.clone(), "JetBrains Mono", self.settings.terminal_font_options().to_vec(), ) @@ -255,7 +255,7 @@ impl NyaTermApp { ( self.tr("settings.uiFontFamily"), self.tr("settings.uiFontFamilyDesc"), - self.settings.summary.ui_font_family.clone(), + self.settings.summary().ui_font_family.clone(), "Inter", self.settings.ui_font_options().to_vec(), ) @@ -408,9 +408,9 @@ impl NyaTermApp { ) -> impl IntoElement { let palette = self.theme_palette(); let value = if content { - self.settings.summary.background_content_opacity + self.settings.summary().background_content_opacity } else { - self.settings.summary.background_image_opacity + self.settings.summary().background_image_opacity }; let label = if content { self.tr("settings.backgroundContentOpacity") @@ -523,12 +523,12 @@ impl NyaTermApp { }; let current = if terminal { self.settings - .summary + .summary() .terminal_theme .as_deref() .filter(|theme| !theme.trim().is_empty()) } else { - Some(self.settings.summary.theme.as_str()) + Some(self.settings.summary().theme.as_str()) }; let value = current .map(appearance_theme_label) @@ -582,7 +582,7 @@ impl NyaTermApp { fn appearance_contrast_select(&mut self, cx: &mut Context) -> impl IntoElement { let palette = self.theme_palette(); let id = "appearance-minimum-contrast"; - let current = self.settings.summary.minimum_contrast_ratio.clone(); + let current = self.settings.summary().minimum_contrast_ratio.clone(); let label_for = |ratio: &str| match ratio { "3" => self.tr("settings.minimumContrastRatio_3"), "4.5" => self.tr("settings.minimumContrastRatio_4_5"), @@ -623,7 +623,7 @@ impl NyaTermApp { ) -> impl IntoElement { let palette = self.theme_palette(); let id = "appearance-background-fit"; - let current = match self.settings.summary.background_image_fit.as_str() { + let current = match self.settings.summary().background_image_fit.as_str() { "contain" => "contain", "stretch" | "fill" => "stretch", "tile" => "tile", @@ -673,9 +673,9 @@ impl NyaTermApp { "appearance-font-weight" }; let current = if bold { - self.settings.summary.terminal_font_weight_bold + self.settings.summary().terminal_font_weight_bold } else { - self.settings.summary.terminal_font_weight + self.settings.summary().terminal_font_weight }; let label_for = |weight| match weight { 300 => self.tr("settings.fontWeight_300"), @@ -720,7 +720,7 @@ impl NyaTermApp { fn appearance_cursor_style_select(&mut self, cx: &mut Context) -> impl IntoElement { let palette = self.theme_palette(); let id = "appearance-cursor-style"; - let current = self.settings.summary.cursor_style.clone(); + let current = self.settings.summary().cursor_style.clone(); let label_for = |style: &str| match style { "underline" => self.tr("settings.cursorUnderline"), "bar" => self.tr("settings.cursorBar"), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/workspace/general.rs b/crates/nyaterm-desktop/src/features/pages/settings/workspace/general.rs index 8d1bf5a24..eccd13695 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/workspace/general.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/workspace/general.rs @@ -15,7 +15,7 @@ impl NyaTermApp { ) -> impl IntoElement { let palette = self.theme_palette(); // Tauri GeneralTab: language, nested startup layout, tray, confirm, diagnostics. - let language = self.settings.summary.language.clone(); + let language = self.settings.summary().language.clone(); let language_label = match language.as_str() { "zh-CN" | "zh" => "简体中文", "zh-TW" => "繁體中文", @@ -23,12 +23,12 @@ impl NyaTermApp { "ja" => "日本語", other => other, }; - let diagnostics_level = self.settings.summary.diagnostics_level.clone(); - let retention = self.settings.summary.diagnostics_retention_days; + let diagnostics_level = self.settings.summary().diagnostics_level.clone(); + let retention = self.settings.summary().diagnostics_retention_days; let days_unit = self.tr("common.days"); let header_status_mode = - HeaderStatusMode::from_setting(&self.settings.summary.ui_header_status_mode); - let header_status_visible = self.settings.summary.ui_header_status_visible; + HeaderStatusMode::from_setting(&self.settings.summary().ui_header_status_mode); + let header_status_visible = self.settings.summary().ui_header_status_visible; div() .flex() @@ -121,13 +121,13 @@ impl NyaTermApp { settings_switch( palette, "general-startup-restore", - self.settings.summary.startup_restore, + self.settings.summary().startup_restore, cx.listener(|this, _, _, cx| { this.toggle_startup_restore(cx); }), ), )) - .when(self.settings.summary.startup_restore, |this| { + .when(self.settings.summary().startup_restore, |this| { this.child( div() .pl_3() @@ -143,7 +143,7 @@ impl NyaTermApp { settings_switch( palette, "general-startup-restore-window-layout", - self.settings.summary.startup_restore_window_layout, + self.settings.summary().startup_restore_window_layout, cx.listener(|this, _, _, cx| { this.toggle_startup_restore_window_layout(cx); }), @@ -158,7 +158,7 @@ impl NyaTermApp { settings_switch( palette, "general-minimize-to-tray", - self.settings.summary.minimize_to_tray, + self.settings.summary().minimize_to_tray, cx.listener(|this, _, _, cx| { this.toggle_minimize_to_tray(cx); }), @@ -171,7 +171,7 @@ impl NyaTermApp { settings_switch( palette, "general-confirm-close", - self.settings.summary.confirm_on_close, + self.settings.summary().confirm_on_close, cx.listener(|this, _, _, cx| { this.toggle_confirm_on_close(cx); }), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/workspace/interaction.rs b/crates/nyaterm-desktop/src/features/pages/settings/workspace/interaction.rs index 84f163522..7515a0154 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/workspace/interaction.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/workspace/interaction.rs @@ -19,47 +19,47 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let palette = self.theme_palette(); - let encoding = self.settings.summary.interaction_default_encoding.clone(); + let encoding = self.settings.summary().interaction_default_encoding.clone(); // Built before the form, which reads `self` throughout: creating the // box needs it mutably. let word_separators_input = self .text_input_box( "settings.interaction.word-separators", - &self.settings.summary.interaction_word_separators.clone(), + &self.settings.summary().interaction_word_separators.clone(), TextInputSetup::default(), cx, ) .into_any_element(); let double_action = self .settings - .summary + .summary() .interaction_tab_double_click_action .clone(); let middle_action = self .settings - .summary + .summary() .interaction_tab_middle_click_action .clone(); let right_action = self .settings - .summary + .summary() .interaction_tab_right_click_action .clone(); let delay_ms = self .settings - .summary + .summary() .interaction_duplicate_session_command_delay_ms; let min_chars = self .settings - .summary + .summary() .interaction_command_suggestion_min_chars; let max_chars = self .settings - .summary + .summary() .interaction_command_suggestion_max_chars; let suggestions_enabled = self .settings - .summary + .summary() .interaction_command_suggestions_enabled; div() @@ -81,7 +81,7 @@ impl NyaTermApp { settings_switch( palette, "interaction-copy-select", - self.settings.summary.interaction_copy_on_select, + self.settings.summary().interaction_copy_on_select, cx.listener(|this, _, _, cx| { this.toggle_interaction_copy_on_select(cx); }), @@ -94,7 +94,7 @@ impl NyaTermApp { settings_switch( palette, "interaction-right-paste", - self.settings.summary.interaction_right_click_paste, + self.settings.summary().interaction_right_click_paste, cx.listener(|this, _, _, cx| { this.toggle_interaction_right_click_paste(cx); }), @@ -219,7 +219,7 @@ impl NyaTermApp { settings_switch( palette, "interaction-alt-meta", - self.settings.summary.interaction_alt_as_meta, + self.settings.summary().interaction_alt_as_meta, cx.listener(|this, _, _, cx| { this.toggle_alt_as_meta(cx); }), @@ -234,7 +234,7 @@ impl NyaTermApp { settings_switch( palette, "interaction-mac-ime", - self.settings.summary.interaction_mac_ime_compatibility, + self.settings.summary().interaction_mac_ime_compatibility, cx.listener(|this, _, _, cx| { this.toggle_mac_ime_compatibility(cx); }), diff --git a/crates/nyaterm-desktop/src/features/pages/settings/workspace/keybindings.rs b/crates/nyaterm-desktop/src/features/pages/settings/workspace/keybindings.rs index 977ec8da2..91c06642f 100644 --- a/crates/nyaterm-desktop/src/features/pages/settings/workspace/keybindings.rs +++ b/crates/nyaterm-desktop/src/features/pages/settings/workspace/keybindings.rs @@ -15,7 +15,7 @@ impl NyaTermApp { cx: &mut Context, ) -> impl IntoElement { let palette = self.theme_palette(); - let overrides = self.settings.summary.keybindings.len(); + let overrides = self.settings.summary().keybindings.len(); let search = self.settings.keybinding_presentation().search_draft; let search_field = self.text_input( "settings.keybindings.search", @@ -106,7 +106,7 @@ impl NyaTermApp { if needle.is_empty() { return true; } - let keys = shortcut_keys_for(shortcut.id, &self.settings.summary.keybindings) + let keys = shortcut_keys_for(shortcut.id, &self.settings.summary().keybindings) .unwrap_or_else(|| shortcut.default_keys.to_string()); let display = format_hotkey_for_display(&keys).to_ascii_lowercase(); shortcut.label.to_ascii_lowercase().contains(&needle) @@ -140,10 +140,14 @@ impl NyaTermApp { ShortcutNativeStatus::Contextual => "contextual", }, ); - let is_custom = self.settings.summary.keybindings.contains_key(shortcut.id); + let is_custom = self + .settings + .summary() + .keybindings + .contains_key(shortcut.id); let interaction = self.settings.keybinding_presentation(); let is_recording = interaction.recording_id.as_deref() == Some(shortcut.id); - let effective_keys = shortcut_keys_for(shortcut.id, &self.settings.summary.keybindings) + let effective_keys = shortcut_keys_for(shortcut.id, &self.settings.summary().keybindings) .unwrap_or_else(|| shortcut.default_keys.to_string()); let conflict = if is_recording { interaction diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/browser/view.rs b/crates/nyaterm-desktop/src/features/pages/transfers/browser/view.rs index e8bbbfaf0..410606f2f 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/browser/view.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/browser/view.rs @@ -144,7 +144,7 @@ impl NyaTermApp { .sum(); let search_active = !self.transfer.browser_view().search.trim().is_empty(); let search_expanded = self.transfer.browser_view().search_expanded || search_active; - let show_hidden_files = self.settings.summary.ui_file_explorer_show_hidden_files; + let show_hidden_files = self.settings.summary().ui_file_explorer_show_hidden_files; let search_input = search_expanded.then(|| { let field = self.text_input( "transfer.browser.search", diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/browser_filter.rs b/crates/nyaterm-desktop/src/features/pages/transfers/browser_filter.rs index 4ced52195..8fe3b3090 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/browser_filter.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/browser_filter.rs @@ -19,7 +19,7 @@ impl NyaTermApp { transfer_browser_entry_is_visible( entry, &query, - self.settings.summary.ui_file_explorer_show_hidden_files, + self.settings.summary().ui_file_explorer_show_hidden_files, ) }) .cloned() diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/browser_keys.rs b/crates/nyaterm-desktop/src/features/pages/transfers/browser_keys.rs index 8e536420a..34a2ecce3 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/browser_keys.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/browser_keys.rs @@ -76,7 +76,7 @@ impl NyaTermApp { if shortcut_matches( event, "fileExplorer.rename", - &self.settings.summary.keybindings, + &self.settings.summary().keybindings, ) && self.selected_transfer_entries().len() == 1 && self.session.active_ssh_config().is_some() && !self.transfer.rename_dialog_is_open() diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/browser_navigation.rs b/crates/nyaterm-desktop/src/features/pages/transfers/browser_navigation.rs index 923a6d5a1..8e1cbf79b 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/browser_navigation.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/browser_navigation.rs @@ -209,32 +209,17 @@ impl NyaTermApp { }; let enabled = self .settings - .summary - .ui_file_explorer_auto_sync_cwd_connection_ids - .iter() - .any(|id| id == &connection_id); + .toggle_file_explorer_auto_sync_cwd(connection_id); if enabled { - self.settings - .summary - .ui_file_explorer_auto_sync_cwd_connection_ids - .retain(|id| id != &connection_id); - self.transfer - .set_browser_status("Auto CWD disabled for this connection"); - } else { - self.settings - .summary - .ui_file_explorer_auto_sync_cwd_connection_ids - .retain(|id| id != &connection_id); - self.settings - .summary - .ui_file_explorer_auto_sync_cwd_connection_ids - .push(connection_id); self.transfer .set_browser_status("Auto CWD enabled for this connection"); + } else { + self.transfer + .set_browser_status("Auto CWD disabled for this connection"); } self.transfer.reset_browser_auto_sync_cwd(); self.persist_transfer_browser_ui_settings(); - if !enabled { + if enabled { self.start_transfer_sync_cwd_job(window, cx); } else { cx.notify(); @@ -245,14 +230,13 @@ impl NyaTermApp { &mut self, cx: &mut Context, ) { - self.settings.summary.ui_file_explorer_show_hidden_files = - !self.settings.summary.ui_file_explorer_show_hidden_files; - if !self.settings.summary.ui_file_explorer_show_hidden_files { + let show_hidden_files = self.settings.toggle_file_explorer_hidden_files(); + if !show_hidden_files { self.transfer .retain_browser_selection(|path| !remote_file_name(path).starts_with('.')); } self.transfer.set_browser_list_offset(0); - let status = if self.settings.summary.ui_file_explorer_show_hidden_files { + let status = if show_hidden_files { "hidden files shown".to_string() } else { "hidden files hidden".to_string() @@ -267,7 +251,7 @@ impl NyaTermApp { return false; }; self.settings - .summary + .summary() .ui_file_explorer_auto_sync_cwd_connection_ids .iter() .any(|id| id == &connection_id) @@ -280,7 +264,7 @@ impl NyaTermApp { }; let favorites = self .settings - .summary + .summary() .ui_file_explorer_favorite_dirs_by_connection_id .get(&connection_id) .cloned() @@ -307,17 +291,8 @@ impl NyaTermApp { return; }; let favorites = self.transfer.browser_favorites_owned(); - if favorites.is_empty() { - self.settings - .summary - .ui_file_explorer_favorite_dirs_by_connection_id - .remove(&connection_id); - } else { - self.settings - .summary - .ui_file_explorer_favorite_dirs_by_connection_id - .insert(connection_id, favorites); - } + self.settings + .set_file_explorer_favorites(connection_id, favorites); self.persist_transfer_browser_ui_settings(); cx.notify(); } @@ -327,7 +302,7 @@ impl NyaTermApp { self.runtime.config_dir(), self.runtime.portable_key_path().map(ToOwned::to_owned), ) - .and_then(|store| store.save_file_explorer_favorite_dirs(&self.settings.summary)) + .and_then(|store| store.save_file_explorer_favorite_dirs(self.settings.summary())) { Ok(settings) => { self.apply_gpui_settings(settings); diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/browser_selection.rs b/crates/nyaterm-desktop/src/features/pages/transfers/browser_selection.rs index b70e4fe46..dd0ca965c 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/browser_selection.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/browser_selection.rs @@ -499,7 +499,7 @@ impl NyaTermApp { cx.notify(); return; } - if self.settings.summary.transfer_ask_save_location { + if self.settings.summary().transfer_ask_save_location { let remote_paths = entries .into_iter() .map(|entry| entry.path) diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/editor/open.rs b/crates/nyaterm-desktop/src/features/pages/transfers/editor/open.rs index b81a2c504..2660ef974 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/editor/open.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/editor/open.rs @@ -170,7 +170,7 @@ impl NyaTermApp { entry: &SftpFileEntry, ) -> bool { entry.file_type != SftpFileType::Directory - && self.settings.summary.transfer_editor_type == "external" + && self.settings.summary().transfer_editor_type == "external" && !is_known_binary_file(&entry.name) } @@ -179,7 +179,7 @@ impl NyaTermApp { entry: &SftpFileEntry, ) -> bool { entry.file_type != SftpFileType::Directory - && self.settings.summary.transfer_editor_type == "internal" + && self.settings.summary().transfer_editor_type == "internal" } pub(in crate::features) fn open_transfer_default( @@ -188,7 +188,7 @@ impl NyaTermApp { window: &mut Window, cx: &mut Context, ) { - if self.settings.summary.transfer_editor_type == "internal" { + if self.settings.summary().transfer_editor_type == "internal" { self.open_transfer_editor(entry, window, cx); } else { self.open_transfer_external(entry, window, cx); @@ -374,7 +374,7 @@ impl NyaTermApp { self.transfer.set_remote_path(entry.path.clone()); let remote_path = entry.path.clone(); let local_path = self.transfer_external_open_path(&entry, session_id.as_deref()); - let default_editor = self.settings.summary.transfer_default_editor.clone(); + let default_editor = self.settings.summary().transfer_default_editor.clone(); let transfer_options = self.sftp_transfer_options(); let id = self.next_transfer_id("sftp-open-external"); let control = SftpTransferControl::new(); diff --git a/crates/nyaterm-desktop/src/features/pages/transfers/path_bar.rs b/crates/nyaterm-desktop/src/features/pages/transfers/path_bar.rs index 6d35da5e8..aaba41d86 100644 --- a/crates/nyaterm-desktop/src/features/pages/transfers/path_bar.rs +++ b/crates/nyaterm-desktop/src/features/pages/transfers/path_bar.rs @@ -246,7 +246,7 @@ impl NyaTermApp { let status = if path == current_path { TransferBrowserChildrenMenuStatus::Ready(transfer_browser_child_directories( &self.transfer.browser_view().entries, - self.settings.summary.ui_file_explorer_show_hidden_files, + self.settings.summary().ui_file_explorer_show_hidden_files, )) } else { TransferBrowserChildrenMenuStatus::Loading diff --git a/crates/nyaterm-desktop/src/features/panels/connection_import_overlay.rs b/crates/nyaterm-desktop/src/features/panels/connection_import_overlay.rs index eaa347f9c..b38216ca0 100644 --- a/crates/nyaterm-desktop/src/features/panels/connection_import_overlay.rs +++ b/crates/nyaterm-desktop/src/features/panels/connection_import_overlay.rs @@ -17,7 +17,7 @@ impl NyaTermApp { let width = (self.shell.viewport_size().0 - 32.).clamp(280., 480.); let docs_url = if self .settings - .summary + .summary() .language .to_ascii_lowercase() .starts_with("zh") diff --git a/crates/nyaterm-desktop/src/features/panels/lock_screen_overlay.rs b/crates/nyaterm-desktop/src/features/panels/lock_screen_overlay.rs index a56f957a4..d5c18096b 100644 --- a/crates/nyaterm-desktop/src/features/panels/lock_screen_overlay.rs +++ b/crates/nyaterm-desktop/src/features/panels/lock_screen_overlay.rs @@ -22,13 +22,13 @@ impl NyaTermApp { cx, ); let password_focus = password_input.read(cx).focus_handle(); - let overlay_focus = if self.settings.summary.has_master_password { + let overlay_focus = if self.settings.summary().has_master_password { password_focus.clone() } else { self.security.screen_lock_focus().clone() }; let lock_status = if self.security.screen_lock_status().trim().is_empty() { - if self.settings.summary.has_master_password { + if self.settings.summary().has_master_password { self.tr("lockScreen.passwordPlaceholder").to_string() } else { self.tr("settings.masterPasswordRequired").to_string() @@ -168,7 +168,7 @@ impl NyaTermApp { .text_color(rgb(palette.text_muted)) .child(self.tr("lockScreen.message")), ) - .when(self.settings.summary.has_master_password, |this| { + .when(self.settings.summary().has_master_password, |this| { this.child( div() .w(px(280.)) @@ -224,7 +224,7 @@ impl NyaTermApp { ), ) }) - .when(!self.settings.summary.has_master_password, |this| { + .when(!self.settings.summary().has_master_password, |this| { this.child( div() .text_center() diff --git a/crates/nyaterm-desktop/src/features/panels/quick_command_import_overlay.rs b/crates/nyaterm-desktop/src/features/panels/quick_command_import_overlay.rs index 97feac56e..7bd124633 100644 --- a/crates/nyaterm-desktop/src/features/panels/quick_command_import_overlay.rs +++ b/crates/nyaterm-desktop/src/features/panels/quick_command_import_overlay.rs @@ -15,7 +15,7 @@ impl NyaTermApp { let palette = self.theme_palette(); let docs_url = if self .settings - .summary + .summary() .language .to_ascii_lowercase() .starts_with("zh") diff --git a/crates/nyaterm-desktop/src/features/quick_command_window.rs b/crates/nyaterm-desktop/src/features/quick_command_window.rs index 2e94b0ad0..f83c2235f 100644 --- a/crates/nyaterm-desktop/src/features/quick_command_window.rs +++ b/crates/nyaterm-desktop/src/features/quick_command_window.rs @@ -36,7 +36,7 @@ impl Render for QuickCommandWindow { ( app.theme_palette(), app.gpui_ui_font_family(), - app.settings.summary.ui_font_size.clamp(12, 24) as f32, + app.settings.summary().ui_font_size.clamp(12, 24) as f32, app.quick_command_editor_title().to_string(), ) }); diff --git a/crates/nyaterm-desktop/src/features/remote_editor_window.rs b/crates/nyaterm-desktop/src/features/remote_editor_window.rs index d7a252506..e88e3393b 100644 --- a/crates/nyaterm-desktop/src/features/remote_editor_window.rs +++ b/crates/nyaterm-desktop/src/features/remote_editor_window.rs @@ -54,7 +54,7 @@ impl Render for RemoteFileEditorWindow { ( app.theme_palette(), app.gpui_ui_font_family(), - app.settings.summary.ui_font_size.clamp(12, 24) as f32, + app.settings.summary().ui_font_size.clamp(12, 24) as f32, format!( "{}{}", if workspace.tabs.iter().any(|tab| tab.dirty) { diff --git a/crates/nyaterm-desktop/src/features/root.rs b/crates/nyaterm-desktop/src/features/root.rs index ce3da78ef..19fa5f1dc 100644 --- a/crates/nyaterm-desktop/src/features/root.rs +++ b/crates/nyaterm-desktop/src/features/root.rs @@ -59,7 +59,7 @@ impl NyaTermApp { let palette = self.theme_palette(); let wallpaper_path = self .settings - .summary + .summary() .background_image_path .as_ref() .map(|p| p.trim()) @@ -67,8 +67,8 @@ impl NyaTermApp { .filter(|p| std::path::Path::new(p).is_file()) .map(|p| p.to_string()); let wallpaper_opacity = - (self.settings.summary.background_image_opacity.min(100) as f32) / 100.0; - let wallpaper_fit = self.settings.summary.background_image_fit.clone(); + (self.settings.summary().background_image_opacity.min(100) as f32) / 100.0; + let wallpaper_fit = self.settings.summary().background_image_fit.clone(); let wallpaper_tile_size = wallpaper_path .as_deref() .filter(|_| wallpaper_fit == "tile") @@ -80,7 +80,7 @@ impl NyaTermApp { .bg(self.shell_transparent_color(palette.bg)) .text_color(rgb(palette.text)) .font_family(self.gpui_ui_font_family()) - .text_size(px(self.settings.summary.ui_font_size.clamp(12, 24) as f32)) + .text_size(px(self.settings.summary().ui_font_size.clamp(12, 24) as f32)) .on_click(cx.listener(|this, _, _, _| { this.mark_user_activity(); })) diff --git a/crates/nyaterm-desktop/src/features/session/recording_runtime.rs b/crates/nyaterm-desktop/src/features/session/recording_runtime.rs index 6523b51d0..ff1bbb72c 100644 --- a/crates/nyaterm-desktop/src/features/session/recording_runtime.rs +++ b/crates/nyaterm-desktop/src/features/session/recording_runtime.rs @@ -29,7 +29,7 @@ impl NyaTermApp { return; } let target = recording_file_path( - &self.settings.summary, + self.settings.summary(), self.runtime.config_dir(), &session_name, ); @@ -107,9 +107,9 @@ impl NyaTermApp { let manager = self.recording.manager_for_job(); let writer = self.recording.writer(); let job_session_id = session_id.to_string(); - let memory_limit = self.settings.summary.recording_memory_limit_bytes as usize; - let include_io_labels = self.settings.summary.recording_include_io_labels; - let include_timestamps = self.settings.summary.recording_include_timestamps; + let memory_limit = self.settings.summary().recording_memory_limit_bytes as usize; + let include_io_labels = self.settings.summary().recording_include_io_labels; + let include_timestamps = self.settings.summary().recording_include_timestamps; let task = cx.background_spawn(async move { writer.flush(); manager.set_memory_limit(memory_limit); @@ -207,9 +207,9 @@ impl NyaTermApp { let manager = self.recording.manager_for_job(); let writer = self.recording.writer(); let job_session_id = session_id.to_string(); - let memory_limit = self.settings.summary.recording_memory_limit_bytes as usize; - let include_io_labels = self.settings.summary.recording_include_io_labels; - let include_timestamps = self.settings.summary.recording_include_timestamps; + let memory_limit = self.settings.summary().recording_memory_limit_bytes as usize; + let include_io_labels = self.settings.summary().recording_include_io_labels; + let include_timestamps = self.settings.summary().recording_include_timestamps; let task = cx.background_spawn(async move { writer.flush(); manager.set_memory_limit(memory_limit); @@ -249,11 +249,11 @@ impl NyaTermApp { session_name: &str, cx: &mut Context, ) { - if !self.settings.summary.recording_auto_start { + if !self.settings.summary().recording_auto_start { return; } let path = recording_file_path( - &self.settings.summary, + self.settings.summary(), self.runtime.config_dir(), session_name, ); diff --git a/crates/nyaterm-desktop/src/features/session/session_dialog_runtime.rs b/crates/nyaterm-desktop/src/features/session/session_dialog_runtime.rs index 203ad7862..94a9dd363 100644 --- a/crates/nyaterm-desktop/src/features/session/session_dialog_runtime.rs +++ b/crates/nyaterm-desktop/src/features/session/session_dialog_runtime.rs @@ -119,7 +119,7 @@ impl NyaTermApp { } let delay_ms = u64::from( self.settings - .summary + .summary() .interaction_duplicate_session_command_delay_ms, ); self.session.dialogs.open_startup_command(action, delay_ms); diff --git a/crates/nyaterm-desktop/src/features/session/session_lifecycle.rs b/crates/nyaterm-desktop/src/features/session/session_lifecycle.rs index 895f9b988..01e1bdc15 100644 --- a/crates/nyaterm-desktop/src/features/session/session_lifecycle.rs +++ b/crates/nyaterm-desktop/src/features/session/session_lifecycle.rs @@ -239,7 +239,7 @@ impl NyaTermApp { view.append_text(banner); } else { let mut view = TerminalViewState::new(); - view.set_encoding(&self.settings.summary.interaction_default_encoding); + view.set_encoding(&self.settings.summary().interaction_default_encoding); view.append_text(banner); self.terminal .view diff --git a/crates/nyaterm-desktop/src/features/session/session_order.rs b/crates/nyaterm-desktop/src/features/session/session_order.rs index abd931431..3575782a4 100644 --- a/crates/nyaterm-desktop/src/features/session/session_order.rs +++ b/crates/nyaterm-desktop/src/features/session/session_order.rs @@ -10,7 +10,7 @@ impl NyaTermApp { metadata: SessionRuntimeMetadata, ) { self.session.register_session_metadata(session_id, metadata); - let encoding = self.settings.summary.interaction_default_encoding.clone(); + let encoding = self.settings.summary().interaction_default_encoding.clone(); let view = self .terminal .view diff --git a/crates/nyaterm-desktop/src/features/session/session_runtime/background.rs b/crates/nyaterm-desktop/src/features/session/session_runtime/background.rs index f30269e66..4f789169f 100644 --- a/crates/nyaterm-desktop/src/features/session/session_runtime/background.rs +++ b/crates/nyaterm-desktop/src/features/session/session_runtime/background.rs @@ -547,7 +547,7 @@ impl NyaTermApp { session_id.clone(), TerminalViewState::from_output_with_encoding( seed_output, - &self.settings.summary.interaction_default_encoding, + &self.settings.summary().interaction_default_encoding, ), ); } @@ -595,7 +595,7 @@ impl NyaTermApp { // Do not append local log text through the full terminal decode path // on connect success — that competes with the first SSH/PTY frames. // Auto-recording file open is deferred to the idle plane. - if self.settings.summary.recording_auto_start { + if self.settings.summary().recording_auto_start { self.recording .schedule_auto_start(session_id.clone(), session_info.name.clone()); } diff --git a/crates/nyaterm-desktop/src/features/session/session_runtime/start.rs b/crates/nyaterm-desktop/src/features/session/session_runtime/start.rs index 1ea30322c..37683c97d 100644 --- a/crates/nyaterm-desktop/src/features/session/session_runtime/start.rs +++ b/crates/nyaterm-desktop/src/features/session/session_runtime/start.rs @@ -316,9 +316,9 @@ impl NyaTermApp { SshSessionConfigBuildContext { config_dir: self.runtime.config_dir().to_path_buf(), portable_key_path: self.runtime.portable_key_path().map(ToOwned::to_owned), - host_key_policy: self.settings.summary.host_key_policy.clone(), - x11_display: self.settings.summary.x11_display.clone(), - keep_alive_interval_secs: self.settings.summary.terminal_keep_alive_interval, + host_key_policy: self.settings.summary().host_key_policy.clone(), + x11_display: self.settings.summary().x11_display.clone(), + keep_alive_interval_secs: self.settings.summary().terminal_keep_alive_interval, host_key_prompts: self.session.prompts.host_key_broker(), credential_prompts: self.session.prompts.credential_broker(), otp_provider: self.session.prompts.otp_provider(), diff --git a/crates/nyaterm-desktop/src/features/session/startup_restore_runtime.rs b/crates/nyaterm-desktop/src/features/session/startup_restore_runtime.rs index 7497f6f2f..d412d107e 100644 --- a/crates/nyaterm-desktop/src/features/session/startup_restore_runtime.rs +++ b/crates/nyaterm-desktop/src/features/session/startup_restore_runtime.rs @@ -18,7 +18,7 @@ impl NyaTermApp { /// Connect/register must not open the config database or rewrite settings on /// the UI thread — that path was a major connect-time freeze source. pub(in crate::features) fn persist_open_tabs(&mut self) { - if !self.settings.summary.startup_restore { + if !self.settings.summary().startup_restore { return; } self.terminal.view.runtime.open_tabs_persist_dirty = true; @@ -28,7 +28,7 @@ impl NyaTermApp { /// Force a durable open-tabs write (window close / explicit quit paths). pub(in crate::features) fn flush_open_tabs_now(&mut self) { - if !self.settings.summary.startup_restore { + if !self.settings.summary().startup_restore { self.terminal.view.runtime.open_tabs_persist_dirty = false; self.terminal.view.runtime.window_layout_persist_dirty = false; return; @@ -44,14 +44,14 @@ impl NyaTermApp { /// rewriting settings is never done on the UI tick — that freezes connect /// and the first idle frame after connect. pub(in crate::features) fn flush_pending_session_persistence(&mut self) { - if !self.settings.summary.startup_restore { + if !self.settings.summary().startup_restore { self.terminal.view.runtime.open_tabs_persist_dirty = false; self.terminal.view.runtime.window_layout_persist_dirty = false; return; } let need_tabs = self.terminal.view.runtime.open_tabs_persist_dirty; let need_layout = self.terminal.view.runtime.window_layout_persist_dirty - && self.settings.summary.startup_restore_window_layout; + && self.settings.summary().startup_restore_window_layout; if !need_tabs && !need_layout { return; } @@ -122,14 +122,14 @@ impl NyaTermApp { /// Synchronous durable write used by window-close / quit (must not race exit). fn flush_pending_session_persistence_sync(&mut self) { - if !self.settings.summary.startup_restore { + if !self.settings.summary().startup_restore { self.terminal.view.runtime.open_tabs_persist_dirty = false; self.terminal.view.runtime.window_layout_persist_dirty = false; return; } let need_tabs = self.terminal.view.runtime.open_tabs_persist_dirty; let need_layout = self.terminal.view.runtime.window_layout_persist_dirty - && self.settings.summary.startup_restore_window_layout; + && self.settings.summary().startup_restore_window_layout; if !need_tabs && !need_layout { return; } @@ -339,7 +339,7 @@ impl NyaTermApp { if !should_restore { return; } - if !self.settings.summary.startup_restore { + if !self.settings.summary().startup_restore { self.mark_startup_restore_complete(); return; } diff --git a/crates/nyaterm-desktop/src/features/session/temporary_ssh_link.rs b/crates/nyaterm-desktop/src/features/session/temporary_ssh_link.rs index 53b110ae5..c248ffc1f 100644 --- a/crates/nyaterm-desktop/src/features/session/temporary_ssh_link.rs +++ b/crates/nyaterm-desktop/src/features/session/temporary_ssh_link.rs @@ -128,7 +128,7 @@ impl NyaTermApp { x11_forwarding: false, x11_display: String::new(), deferred_pty: true, - keep_alive_interval_secs: self.settings.summary.terminal_keep_alive_interval, + keep_alive_interval_secs: self.settings.summary().terminal_keep_alive_interval, cols: 80, rows: 24, pixel_width: 0, @@ -136,7 +136,7 @@ impl NyaTermApp { host_key_verifier: Some(Arc::new(NativeHostKeyVerifier { config_dir: self.runtime.config_dir().to_path_buf(), portable_key_path: self.runtime.portable_key_path().map(ToOwned::to_owned), - policy: self.settings.summary.host_key_policy.clone(), + policy: self.settings.summary().host_key_policy.clone(), prompt_broker: self.session.prompts.host_key_broker(), })), credential_provider: Some(self.session.prompts.credential_broker()), diff --git a/crates/nyaterm-desktop/src/features/settings/catalog.rs b/crates/nyaterm-desktop/src/features/settings/catalog.rs index 296061759..c468cfdbf 100644 --- a/crates/nyaterm-desktop/src/features/settings/catalog.rs +++ b/crates/nyaterm-desktop/src/features/settings/catalog.rs @@ -1,8 +1,8 @@ //! Compatibility-sensitive settings state shared by the settings feature owner. -pub(in crate::features) struct SettingsMasterPasswordState { - pub enabled: bool, - pub draft: String, +pub(super) struct SettingsMasterPasswordState { + pub(super) enabled: bool, + pub(super) draft: String, } impl SettingsMasterPasswordState { diff --git a/crates/nyaterm-desktop/src/features/settings/mod.rs b/crates/nyaterm-desktop/src/features/settings/mod.rs index 0999a2011..054217e78 100644 --- a/crates/nyaterm-desktop/src/features/settings/mod.rs +++ b/crates/nyaterm-desktop/src/features/settings/mod.rs @@ -11,4 +11,6 @@ mod state; pub(in crate::features) use security_state::{ SecurityCatalogState, SecurityFeatureFocus, SecurityFeatureState, }; -pub(in crate::features) use state::{SearchEngineMenu, SettingsFeatureFocus, SettingsFeatureState}; +pub(in crate::features) use state::{ + SearchEngineMenu, SettingsFeatureFocus, SettingsFeatureState, UiLayoutSettingsUpdate, +}; diff --git a/crates/nyaterm-desktop/src/features/settings/settings_runtime/draft.rs b/crates/nyaterm-desktop/src/features/settings/settings_runtime/draft.rs index 2cad05a68..b8e29fffe 100644 --- a/crates/nyaterm-desktop/src/features/settings/settings_runtime/draft.rs +++ b/crates/nyaterm-desktop/src/features/settings/settings_runtime/draft.rs @@ -17,6 +17,7 @@ impl NyaTermApp { self.cloud_sync.settings_draft_snapshot(); let (ai_settings, ai_model_draft, ai_base_url_draft, ai_secret_draft) = self.ai.settings_draft_snapshot(); + let master_password = self.settings.master_password(); self.shell .set_settings_draft_snapshot(SettingsDraftSnapshot { settings: self.settings.summary.clone(), @@ -29,8 +30,8 @@ impl NyaTermApp { translation_settings, translation_secret_draft, keyword_highlights: self.settings.keyword_config.clone(), - master_password_enabled: self.settings.master_password.enabled, - master_password_draft: self.settings.master_password.draft.clone(), + master_password_enabled: master_password.enabled, + master_password_draft: master_password.draft.to_string(), }); } @@ -38,7 +39,8 @@ impl NyaTermApp { let Some(snapshot) = self.shell.settings_draft_snapshot() else { return false; }; - snapshot.settings != self.settings.summary + let master_password = self.settings.master_password(); + snapshot.settings != *self.settings.summary() || !self.ai.settings_draft_matches( &snapshot.ai_settings, &snapshot.ai_model_draft, @@ -53,9 +55,9 @@ impl NyaTermApp { &snapshot.translation_settings, &snapshot.translation_secret_draft, ) - || snapshot.keyword_highlights != self.settings.keyword_config - || snapshot.master_password_enabled != self.settings.master_password.enabled - || snapshot.master_password_draft != self.settings.master_password.draft + || snapshot.keyword_highlights != *self.settings.keyword_config() + || snapshot.master_password_enabled != master_password.enabled + || snapshot.master_password_draft != master_password.draft } /// Returns true when a settings save should stay in the in-memory draft. @@ -78,12 +80,11 @@ impl NyaTermApp { if !settings.enabled { return None; } - if !self.settings.master_password.enabled { + let master_password = self.settings.master_password(); + if !master_password.enabled { return Some("Enable a master password before enabling cloud sync".to_string()); } - if !self.settings.summary.has_master_password - && self.settings.master_password.draft.is_empty() - { + if !self.settings.summary().has_master_password && master_password.draft.is_empty() { return Some("Enter a master password before enabling cloud sync".to_string()); } let missing = match settings.provider.as_str() { @@ -216,16 +217,17 @@ impl NyaTermApp { return; } - let settings = self.settings.summary.clone(); + let settings = self.settings.summary().clone(); let ai_settings = self.pending_ai_settings(); let cloud_sync_settings = self.cloud_sync.pending_settings(); let translation_settings = self.translation.pending_settings(); - let keyword_highlights = self.settings.keyword_config.clone(); - let master_password_update = if self.settings.master_password.draft.is_empty() { - (self.settings.summary.has_master_password && !self.settings.master_password.enabled) + let keyword_highlights = self.settings.keyword_config().clone(); + let master_password = self.settings.master_password(); + let master_password_update = if master_password.draft.is_empty() { + (self.settings.summary().has_master_password && !master_password.enabled) .then_some(None) } else { - Some(Some(self.settings.master_password.draft.clone())) + Some(Some(master_password.draft.to_string())) }; let result = ConnectionStore::open_with_portable_key_path( self.runtime.config_dir(), @@ -343,14 +345,17 @@ impl NyaTermApp { snapshot.translation_settings, snapshot.translation_secret_draft, ); - self.settings.keyword_config = snapshot.keyword_highlights; - self.settings.master_password.enabled = snapshot.master_password_enabled; - self.settings.master_password.draft = snapshot.master_password_draft; + self.settings + .replace_keyword_config(snapshot.keyword_highlights); + self.settings.restore_master_password_draft( + snapshot.master_password_enabled, + snapshot.master_password_draft, + ); self.recording - .set_memory_limit(self.settings.summary.recording_memory_limit_bytes as usize); + .set_memory_limit(self.settings.summary().recording_memory_limit_bytes as usize); self.transfer .set_duplicate_policy(SftpDuplicatePolicy::from_legacy_value( - &self.settings.summary.transfer_duplicate_strategy, + &self.settings.summary().transfer_duplicate_strategy, )); self.sync_terminal_encodings_from_settings(); self.invalidate_terminal_cell_metrics(cx); @@ -373,8 +378,7 @@ impl NyaTermApp { pub(in crate::features) fn toggle_settings_master_password(&mut self, cx: &mut Context) { self.terminal.view.status = match self .settings - .master_password - .toggle(self.cloud_sync.settings().enabled) + .toggle_master_password(self.cloud_sync.settings().enabled) { Ok(true) => "master password enabled; enter a password".to_string(), Ok(false) => "master password removal staged".to_string(), @@ -389,7 +393,7 @@ impl NyaTermApp { text: String, cx: &mut Context, ) { - if !self.settings.master_password.edit_draft(text) { + if !self.settings.edit_master_password_draft(text) { return; } self.terminal.view.status = "master password edited; apply to persist".to_string(); diff --git a/crates/nyaterm-desktop/src/features/settings/state.rs b/crates/nyaterm-desktop/src/features/settings/state.rs index 9d6f483e7..25b2f5851 100644 --- a/crates/nyaterm-desktop/src/features/settings/state.rs +++ b/crates/nyaterm-desktop/src/features/settings/state.rs @@ -1,5 +1,7 @@ //! Authoritative application settings and grouped state for the settings experience. +use std::collections::HashMap; + use gpui::FocusHandle; use nyaterm_core::{ AppSettingsSummary, KeywordHighlightConfig, KeywordHighlightRule, SearchEngineConfig, @@ -14,9 +16,9 @@ use super::catalog::{SettingsMasterPasswordState, StoreStatus}; pub(in crate::features) struct SettingsFeatureState { /// Compatibility-sensitive values loaded and persisted through `nyaterm-core`. - pub summary: AppSettingsSummary, - pub keyword_config: KeywordHighlightConfig, - pub master_password: SettingsMasterPasswordState, + pub(super) summary: AppSettingsSummary, + pub(super) keyword_config: KeywordHighlightConfig, + pub(super) master_password: SettingsMasterPasswordState, pub(super) store_status: StoreStatus, search_engines: SearchEngineSettingsState, keyword_highlights: KeywordHighlightSettingsState, @@ -45,6 +47,37 @@ pub(in crate::features) struct StoreStatusView<'a> { pub ready: bool, } +/// Borrowed staged master-password state. Deliberately does not implement +/// `Debug` so the secret draft cannot be exposed through aggregate logging. +pub(in crate::features) struct MasterPasswordView<'a> { + pub enabled: bool, + pub draft: &'a str, +} + +pub(in crate::features) struct UiLayoutSettingsUpdate { + pub left_panel_width: u32, + pub right_panel_width: u32, + pub transfer_height: u32, + pub quick_command_height: u32, + pub quick_command_visible: bool, + pub serial_send_height: u32, + pub serial_send_visible: bool, + pub active_left_panel: Option, + pub active_right_panel: Option, + pub left_panel_collapsed: bool, + pub right_panel_collapsed: bool, + pub saved_connections_sort_mode: String, + pub activity_bar_left_top: Vec, + pub activity_bar_left_bottom: Vec, + pub activity_bar_right_top: Vec, + pub activity_bar_right_bottom: Vec, + pub activity_bar_show_labels: bool, + pub panel_multi_open: bool, + pub left_open_panels: Vec, + pub right_open_panels: Vec, + pub panel_stack_sizes: HashMap, +} + struct SearchEngineSettingsState { expanded_index: Option, icon_picker_index: Option, @@ -147,10 +180,338 @@ impl SettingsFeatureState { } } - pub fn rebase_master_password(&mut self) { + pub(in crate::features) fn rebase_master_password(&mut self) { self.master_password.reset(self.summary.has_master_password); } + pub(in crate::features) fn summary(&self) -> &AppSettingsSummary { + &self.summary + } + + pub(in crate::features) fn replace_summary(&mut self, summary: AppSettingsSummary) { + self.summary = summary; + } + + pub(in crate::features) fn keyword_config(&self) -> &KeywordHighlightConfig { + &self.keyword_config + } + + pub(in crate::features) fn replace_keyword_config(&mut self, config: KeywordHighlightConfig) { + self.keyword_config = config; + } + + pub(in crate::features) fn toggle_keyword_highlights(&mut self) -> bool { + self.keyword_config.enabled = !self.keyword_config.enabled; + self.keyword_config.enabled + } + + pub(in crate::features) fn toggle_keyword_highlights_wrapped(&mut self) -> bool { + self.keyword_config.across_wrapped_lines = !self.keyword_config.across_wrapped_lines; + self.keyword_config.across_wrapped_lines + } + + pub(in crate::features) fn toggle_keyword_highlight_builtin(&mut self, rule_id: String) { + let enabled = self + .keyword_config + .builtin_rules + .get(&rule_id) + .copied() + .unwrap_or(true); + self.keyword_config.builtin_rules.insert(rule_id, !enabled); + } + + pub(in crate::features) fn toggle_keyword_highlight_rule(&mut self, rule_id: &str) -> bool { + let Some(rule) = self + .keyword_config + .rules + .iter_mut() + .find(|rule| rule.id == rule_id) + else { + return false; + }; + rule.enabled = !rule.enabled; + true + } + + pub(in crate::features) fn set_keyword_highlight_rule_color( + &mut self, + rule_id: &str, + dark: bool, + color: String, + ) -> bool { + let Some(rule) = self + .keyword_config + .rules + .iter_mut() + .find(|rule| rule.id == rule_id) + else { + return false; + }; + if dark { + rule.color_dark = color; + } else { + rule.color_light = color; + } + true + } + + pub(in crate::features) fn apply_keyword_highlight_rule_input( + &mut self, + rule_id: &str, + field: KeywordHighlightEditorField, + text: String, + ) -> bool { + let Some(rule) = self + .keyword_config + .rules + .iter_mut() + .find(|rule| rule.id == rule_id) + else { + return false; + }; + match field { + KeywordHighlightEditorField::Name => rule.name = text, + KeywordHighlightEditorField::Patterns => { + rule.patterns = text.split('\n').map(ToOwned::to_owned).collect(); + } + KeywordHighlightEditorField::ColorDark => rule.color_dark = text, + KeywordHighlightEditorField::ColorLight => rule.color_light = text, + } + self.begin_keyword_highlight_edit(rule_id.to_string(), field); + true + } + + pub(in crate::features) fn master_password(&self) -> MasterPasswordView<'_> { + MasterPasswordView { + enabled: self.master_password.enabled, + draft: &self.master_password.draft, + } + } + + pub(in crate::features) fn restore_master_password_draft( + &mut self, + enabled: bool, + draft: String, + ) { + self.master_password.enabled = enabled; + self.master_password.draft = draft; + } + + pub(in crate::features) fn toggle_master_password( + &mut self, + cloud_sync_enabled: bool, + ) -> Result { + self.master_password.toggle(cloud_sync_enabled) + } + + pub(in crate::features) fn edit_master_password_draft(&mut self, text: String) -> bool { + self.master_password.edit_draft(text) + } + + pub(in crate::features) fn set_quick_command_view_mode(&mut self, mode: String) { + self.summary.ui_quick_cmd_view_mode = mode; + } + + pub(in crate::features) fn set_quick_command_sort_mode(&mut self, mode: String) { + self.summary.ui_quick_cmd_sort_mode = mode; + } + + pub(in crate::features) fn set_saved_connections_sort_mode(&mut self, mode: String) { + self.summary.ui_saved_connections_sort_mode = mode; + } + + pub(in crate::features) fn set_header_status_mode(&mut self, mode: String) { + self.summary.ui_header_status_mode = mode; + self.summary.ui_header_status_visible = true; + } + + pub(in crate::features) fn set_header_status_visible(&mut self, visible: bool) { + self.summary.ui_header_status_visible = visible; + } + + pub(in crate::features) fn set_keybindings(&mut self, keybindings: HashMap) { + self.summary.keybindings = keybindings; + } + + pub(in crate::features) fn set_transfer_download_path(&mut self, path: String) { + self.summary.transfer_download_path = path; + } + + pub(in crate::features) fn set_recording_path(&mut self, path: String) { + self.summary.recording_path = path; + } + + pub(in crate::features) fn set_transfer_default_editor(&mut self, path: String) { + self.summary.transfer_default_editor = path; + } + + pub(in crate::features) fn set_tab_double_click_action(&mut self, action: String) { + self.summary.interaction_tab_double_click_action = action; + } + + pub(in crate::features) fn set_tab_middle_click_action(&mut self, action: String) { + self.summary.interaction_tab_middle_click_action = action; + } + + pub(in crate::features) fn set_tab_right_click_action(&mut self, action: String) { + self.summary.interaction_tab_right_click_action = action; + } + + pub(in crate::features) fn apply_ui_layout(&mut self, update: UiLayoutSettingsUpdate) { + self.summary.ui_left_panel_width = update.left_panel_width; + self.summary.ui_right_panel_width = update.right_panel_width; + self.summary.ui_transfer_height = update.transfer_height; + self.summary.ui_quick_cmd_height = update.quick_command_height; + self.summary.ui_quick_cmd_visible = update.quick_command_visible; + self.summary.ui_serial_send_height = update.serial_send_height; + self.summary.ui_serial_send_visible = update.serial_send_visible; + self.summary.ui_active_left_panel = update.active_left_panel; + self.summary.ui_active_right_panel = update.active_right_panel; + self.summary.ui_left_panel_collapsed = update.left_panel_collapsed; + self.summary.ui_right_panel_collapsed = update.right_panel_collapsed; + self.summary.ui_saved_connections_sort_mode = update.saved_connections_sort_mode; + self.summary.ui_activity_bar_left_top = update.activity_bar_left_top; + self.summary.ui_activity_bar_left_bottom = update.activity_bar_left_bottom; + self.summary.ui_activity_bar_right_top = update.activity_bar_right_top; + self.summary.ui_activity_bar_right_bottom = update.activity_bar_right_bottom; + self.summary.ui_activity_bar_show_labels = update.activity_bar_show_labels; + self.summary.ui_panel_multi_open = update.panel_multi_open; + self.summary.ui_left_open_panels = update.left_open_panels; + self.summary.ui_right_open_panels = update.right_open_panels; + self.summary.ui_panel_stack_sizes = update.panel_stack_sizes; + } + + pub(in crate::features) fn set_appearance_theme(&mut self, theme: String) { + self.summary.theme = theme; + } + + pub(in crate::features) fn set_terminal_font_family(&mut self, family: String) -> bool { + if self.summary.terminal_font_family == family { + return false; + } + self.summary.terminal_font_family = family; + true + } + + pub(in crate::features) fn set_terminal_font_size(&mut self, size: u16) -> bool { + if self.summary.terminal_font_size == size { + return false; + } + self.summary.terminal_font_size = size; + true + } + + pub(in crate::features) fn set_cursor_style(&mut self, style: String) { + self.summary.cursor_style = style; + } + + pub(in crate::features) fn toggle_cursor_blink(&mut self) -> bool { + self.summary.cursor_blink = !self.summary.cursor_blink; + self.summary.cursor_blink + } + + pub(in crate::features) fn set_terminal_theme(&mut self, theme: Option) { + self.summary.terminal_theme = theme; + } + + pub(in crate::features) fn set_minimum_contrast_ratio(&mut self, ratio: String) -> bool { + if self.summary.minimum_contrast_ratio == ratio { + return false; + } + self.summary.minimum_contrast_ratio = ratio; + true + } + + pub(in crate::features) fn set_ui_font_family(&mut self, family: String) { + self.summary.ui_font_family = family; + } + + pub(in crate::features) fn set_ui_font_size(&mut self, size: u16) { + self.summary.ui_font_size = size; + } + + pub(in crate::features) fn set_terminal_font_weight(&mut self, weight: u16) -> bool { + if self.summary.terminal_font_weight == weight { + return false; + } + self.summary.terminal_font_weight = weight; + true + } + + pub(in crate::features) fn set_terminal_font_weight_bold(&mut self, weight: u16) -> bool { + if self.summary.terminal_font_weight_bold == weight { + return false; + } + self.summary.terminal_font_weight_bold = weight; + true + } + + pub(in crate::features) fn select_background_image(&mut self, path: String) { + self.summary.background_image_path = Some(path); + if self.summary.background_image_fit.trim().is_empty() { + self.summary.background_image_fit = "cover".to_string(); + } + } + + pub(in crate::features) fn clear_background_image(&mut self) { + self.summary.background_image_path = None; + } + + pub(in crate::features) fn set_background_image_fit(&mut self, fit: String) { + self.summary.background_image_fit = fit; + } + + pub(in crate::features) fn set_background_image_opacity(&mut self, opacity: u8) -> bool { + if self.summary.background_image_opacity == opacity { + return false; + } + self.summary.background_image_opacity = opacity; + true + } + + pub(in crate::features) fn set_background_content_opacity(&mut self, opacity: u8) -> bool { + if self.summary.background_content_opacity == opacity { + return false; + } + self.summary.background_content_opacity = opacity; + true + } + + pub(in crate::features) fn toggle_file_explorer_auto_sync_cwd( + &mut self, + connection_id: String, + ) -> bool { + let ids = &mut self.summary.ui_file_explorer_auto_sync_cwd_connection_ids; + let was_enabled = ids.iter().any(|id| id == &connection_id); + ids.retain(|id| id != &connection_id); + if !was_enabled { + ids.push(connection_id); + } + !was_enabled + } + + pub(in crate::features) fn toggle_file_explorer_hidden_files(&mut self) -> bool { + self.summary.ui_file_explorer_show_hidden_files = + !self.summary.ui_file_explorer_show_hidden_files; + self.summary.ui_file_explorer_show_hidden_files + } + + pub(in crate::features) fn set_file_explorer_favorites( + &mut self, + connection_id: String, + favorites: Vec, + ) { + if favorites.is_empty() { + self.summary + .ui_file_explorer_favorite_dirs_by_connection_id + .remove(&connection_id); + } else { + self.summary + .ui_file_explorer_favorite_dirs_by_connection_id + .insert(connection_id, favorites); + } + } + pub(in crate::features) fn store_status(&self) -> StoreStatusView<'_> { StoreStatusView { path: &self.store_status.path, @@ -570,12 +931,16 @@ fn adjusted_index_after_remove(value: Option, removed: usize) -> Option String { gpui_platform_font_family( - &self.settings.summary.terminal_font_family, + &self.settings.summary().terminal_font_family, gpui_terminal_font_fallback(), true, ) } pub(in crate::features) fn gpui_ui_font_family(&self) -> String { - let raw = if self.settings.summary.ui_font_family.trim().is_empty() { - self.settings.summary.terminal_font_family.as_str() + let raw = if self.settings.summary().ui_font_family.trim().is_empty() { + self.settings.summary().terminal_font_family.as_str() } else { - self.settings.summary.ui_font_family.as_str() + self.settings.summary().ui_font_family.as_str() }; gpui_platform_font_family(raw, gpui_ui_font_fallback(), false) } pub(in crate::features) fn theme_palette(&self) -> ThemePalette { - theme_palette(&self.settings.summary.theme) + theme_palette(&self.settings.summary().theme) } pub(in crate::features) fn wallpaper_enabled(&self) -> bool { self.settings - .summary + .summary() .background_image_path .as_deref() .map(str::trim) @@ -53,7 +53,7 @@ impl NyaTermApp { if !self.wallpaper_enabled() { return rgb(color); } - let alpha = ((self.settings.summary.background_content_opacity.min(100) as f32 / 100.0) + let alpha = ((self.settings.summary().background_content_opacity.min(100) as f32 / 100.0) * 255.0) .round() as u32; rgba((color << 8) | alpha.min(0xff)) @@ -80,19 +80,19 @@ impl NyaTermApp { pub(in crate::features) fn resolved_keyword_highlight_rules( &self, ) -> Arc> { - if self.settings.summary.terminal_low_latency_mode { + if self.settings.summary().terminal_low_latency_mode { return Arc::new(Vec::new()); } if let Some(cached) = self.terminal.paint.cached_keyword_highlight_rules.as_ref() { return cached.clone(); } // Cache miss (settings path / first call without ensure): build once without storing. - if !self.settings.keyword_config.enabled { + if !self.settings.keyword_config().enabled { return Arc::new(Vec::new()); } Arc::new(merge_keyword_highlight_rules_for_paint( - &self.settings.keyword_config.rules, - &self.settings.keyword_config.builtin_rules, + &self.settings.keyword_config().rules, + &self.settings.keyword_config().builtin_rules, self.terminal_theme_is_dark(), )) } @@ -104,21 +104,21 @@ impl NyaTermApp { } fn ensure_keyword_highlight_rules_cache(&mut self) { - if self.settings.summary.terminal_low_latency_mode { + if self.settings.summary().terminal_low_latency_mode { self.terminal.paint.cached_keyword_highlight_rules = Some(Arc::new(Vec::new())); return; } if self.terminal.paint.cached_keyword_highlight_rules.is_some() { return; } - let rules = if !self.settings.keyword_config.enabled { + let rules = if !self.settings.keyword_config().enabled { Arc::new(Vec::new()) } else { // terminal_theme_is_dark uses palette; ensure palette first. self.ensure_terminal_theme_palette_cache(); Arc::new(merge_keyword_highlight_rules_for_paint( - &self.settings.keyword_config.rules, - &self.settings.keyword_config.builtin_rules, + &self.settings.keyword_config().rules, + &self.settings.keyword_config().builtin_rules, self.terminal_theme_is_dark(), )) }; @@ -127,16 +127,16 @@ impl NyaTermApp { /// Terminal surface palette: follows optional `terminal_theme`, else UI theme. pub(in crate::features) fn terminal_theme_palette(&self) -> ThemePalette { - let ui_theme = self.settings.summary.theme.as_str(); + let ui_theme = self.settings.summary().theme.as_str(); let terminal_theme = self .settings - .summary + .summary() .terminal_theme .as_deref() .map(str::trim) .filter(|s| !s.is_empty()) .unwrap_or(""); - let contrast = self.settings.summary.minimum_contrast_ratio.as_str(); + let contrast = self.settings.summary().minimum_contrast_ratio.as_str(); if let Some((cached_ui, cached_term, cached_contrast, palette)) = self.terminal.paint.cached_terminal_theme_palette.as_ref() { @@ -157,17 +157,17 @@ impl NyaTermApp { } fn ensure_terminal_theme_palette_cache(&mut self) { - let ui_theme = self.settings.summary.theme.clone(); + let ui_theme = self.settings.summary().theme.clone(); let terminal_theme = self .settings - .summary + .summary() .terminal_theme .as_deref() .map(str::trim) .filter(|s| !s.is_empty()) .unwrap_or("") .to_string(); - let contrast = self.settings.summary.minimum_contrast_ratio.clone(); + let contrast = self.settings.summary().minimum_contrast_ratio.clone(); if let Some((cached_ui, cached_term, cached_contrast, _)) = self.terminal.paint.cached_terminal_theme_palette.as_ref() { @@ -237,7 +237,7 @@ impl NyaTermApp { } else { theme }; - self.settings.summary.theme = theme.to_string(); + self.settings.set_appearance_theme(theme.to_string()); self.save_appearance_settings(cx); } @@ -246,10 +246,9 @@ impl NyaTermApp { family: &str, cx: &mut Context, ) { - if self.settings.summary.terminal_font_family == family { + if !self.settings.set_terminal_font_family(family.to_string()) { return; } - self.settings.summary.terminal_font_family = family.to_string(); self.invalidate_terminal_cell_metrics(cx); self.save_appearance_settings(cx); } @@ -273,22 +272,20 @@ impl NyaTermApp { delta: i16, cx: &mut Context, ) { - let next = (self.settings.summary.terminal_font_size as i16 + delta) + let next = (self.settings.summary().terminal_font_size as i16 + delta) .clamp(TERMINAL_FONT_SIZE_MIN, TERMINAL_FONT_SIZE_MAX); - if self.settings.summary.terminal_font_size == next as u16 { + if !self.settings.set_terminal_font_size(next as u16) { return; } - self.settings.summary.terminal_font_size = next as u16; self.invalidate_terminal_cell_metrics(cx); self.save_appearance_settings(cx); } pub(in crate::features) fn reset_terminal_font_size(&mut self, cx: &mut Context) { let default_size = AppSettingsSummary::default().terminal_font_size; - if self.settings.summary.terminal_font_size == default_size { + if !self.settings.set_terminal_font_size(default_size) { return; } - self.settings.summary.terminal_font_size = default_size; self.invalidate_terminal_cell_metrics(cx); self.save_appearance_settings(cx); } @@ -302,15 +299,15 @@ impl NyaTermApp { "underline" | "bar" => style, _ => "block", }; - self.settings.summary.cursor_style = normalized.to_string(); + self.settings.set_cursor_style(normalized.to_string()); self.save_appearance_settings(cx); self.terminal.view.status = format!("cursor style → {normalized}"); } pub(in crate::features) fn toggle_cursor_blink(&mut self, cx: &mut Context) { - self.settings.summary.cursor_blink = !self.settings.summary.cursor_blink; + let cursor_blink = self.settings.toggle_cursor_blink(); self.save_appearance_settings(cx); - self.terminal.view.status = if self.settings.summary.cursor_blink { + self.terminal.view.status = if cursor_blink { "cursor blink on".to_string() } else { "cursor blink off".to_string() @@ -322,16 +319,16 @@ impl NyaTermApp { theme: Option<&str>, cx: &mut Context, ) { - self.settings.summary.terminal_theme = - theme.map(str::trim).filter(|s| !s.is_empty()).map(|s| { - if s == "catppuccin" { - "catppuccin-mocha".to_string() - } else { - s.to_string() - } - }); + let theme = theme.map(str::trim).filter(|s| !s.is_empty()).map(|s| { + if s == "catppuccin" { + "catppuccin-mocha".to_string() + } else { + s.to_string() + } + }); + self.settings.set_terminal_theme(theme); self.save_appearance_settings(cx); - self.terminal.view.status = match self.settings.summary.terminal_theme.as_deref() { + self.terminal.view.status = match self.settings.summary().terminal_theme.as_deref() { Some(id) => format!("terminal theme → {id}"), None => "terminal theme → follow UI".to_string(), }; @@ -346,10 +343,9 @@ impl NyaTermApp { "3" | "4.5" | "7" | "21" => ratio, _ => "1", }; - if self.settings.summary.minimum_contrast_ratio == ratio { + if !self.settings.set_minimum_contrast_ratio(ratio.to_string()) { return; } - self.settings.summary.minimum_contrast_ratio = ratio.to_string(); self.save_appearance_settings(cx); self.terminal.view.status = format!("minimum contrast → {ratio}"); } @@ -359,7 +355,7 @@ impl NyaTermApp { family: &str, cx: &mut Context, ) { - self.settings.summary.ui_font_family = family.to_string(); + self.settings.set_ui_font_family(family.to_string()); self.save_appearance_settings(cx); } @@ -371,9 +367,9 @@ impl NyaTermApp { cx: &mut Context, ) { let raw = if terminal { - &self.settings.summary.terminal_font_family + &self.settings.summary().terminal_font_family } else { - &self.settings.summary.ui_font_family + &self.settings.summary().ui_font_family }; let fallback = if terminal { "JetBrains Mono" } else { "Inter" }; let mut fonts = appearance_font_stack(raw, fallback); @@ -391,9 +387,9 @@ impl NyaTermApp { cx: &mut Context, ) { let raw = if terminal { - &self.settings.summary.terminal_font_family + &self.settings.summary().terminal_font_family } else { - &self.settings.summary.ui_font_family + &self.settings.summary().ui_font_family }; let fallback = if terminal { "JetBrains Mono" } else { "Inter" }; let mut fonts = appearance_font_stack(raw, fallback); @@ -415,9 +411,9 @@ impl NyaTermApp { cx: &mut Context, ) { let raw = if terminal { - &self.settings.summary.terminal_font_family + &self.settings.summary().terminal_font_family } else { - &self.settings.summary.ui_font_family + &self.settings.summary().ui_font_family }; let fallback = if terminal { "JetBrains Mono" } else { "Inter" }; let mut fonts = appearance_font_stack(raw, fallback); @@ -447,8 +443,8 @@ impl NyaTermApp { } pub(in crate::features) fn adjust_ui_font_size(&mut self, delta: i16, cx: &mut Context) { - let next = (self.settings.summary.ui_font_size as i16 + delta).clamp(12, 24) as u16; - self.settings.summary.ui_font_size = next; + let next = (self.settings.summary().ui_font_size as i16 + delta).clamp(12, 24) as u16; + self.settings.set_ui_font_size(next); self.save_appearance_settings(cx); } @@ -461,10 +457,9 @@ impl NyaTermApp { 300 | 400 | 500 | 600 | 700 | 800 => weight, _ => 400, }; - if self.settings.summary.terminal_font_weight == weight { + if !self.settings.set_terminal_font_weight(weight) { return; } - self.settings.summary.terminal_font_weight = weight; self.invalidate_terminal_cell_metrics(cx); self.save_appearance_settings(cx); } @@ -478,10 +473,9 @@ impl NyaTermApp { 300 | 400 | 500 | 600 | 700 | 800 => weight, _ => 700, }; - if self.settings.summary.terminal_font_weight_bold == weight { + if !self.settings.set_terminal_font_weight_bold(weight) { return; } - self.settings.summary.terminal_font_weight_bold = weight; self.invalidate_terminal_cell_metrics(cx); self.save_appearance_settings(cx); } @@ -495,7 +489,7 @@ impl NyaTermApp { } pub(in crate::features) fn prompt_background_image(&mut self, cx: &mut Context) { - if self.settings.summary.background_image_path.is_some() { + if self.settings.summary().background_image_path.is_some() { // allow replace } let options = PathPromptOptions { @@ -515,10 +509,8 @@ impl NyaTermApp { }; let _ = this.update(cx, |this, cx| { if let Some(path) = path { - this.settings.summary.background_image_path = Some(path.display().to_string()); - if this.settings.summary.background_image_fit.trim().is_empty() { - this.settings.summary.background_image_fit = "cover".to_string(); - } + this.settings + .select_background_image(path.display().to_string()); this.save_appearance_settings(cx); this.terminal.view.status = "wallpaper image selected".to_string(); } else { @@ -532,7 +524,7 @@ impl NyaTermApp { } pub(in crate::features) fn clear_background_image(&mut self, cx: &mut Context) { - self.settings.summary.background_image_path = None; + self.settings.clear_background_image(); self.settings.close_appearance_menu(); self.save_appearance_settings(cx); self.terminal.view.status = "wallpaper cleared".to_string(); @@ -549,7 +541,8 @@ impl NyaTermApp { "tile" => "tile", _ => "cover", }; - self.settings.summary.background_image_fit = normalized.to_string(); + self.settings + .set_background_image_fit(normalized.to_string()); self.save_appearance_settings(cx); self.terminal.view.status = format!("wallpaper fit → {normalized}"); } @@ -560,10 +553,9 @@ impl NyaTermApp { cx: &mut Context, ) { let next = value.min(100); - if self.settings.summary.background_image_opacity == next { + if !self.settings.set_background_image_opacity(next) { return; } - self.settings.summary.background_image_opacity = next; self.save_appearance_settings(cx); } @@ -573,10 +565,9 @@ impl NyaTermApp { cx: &mut Context, ) { let next = value.min(100); - if self.settings.summary.background_content_opacity == next { + if !self.settings.set_background_content_opacity(next) { return; } - self.settings.summary.background_content_opacity = next; self.save_appearance_settings(cx); } @@ -589,7 +580,7 @@ impl NyaTermApp { self.runtime.config_dir(), self.runtime.portable_key_path().map(ToOwned::to_owned), ) - .and_then(|store| store.save_appearance_settings(&self.settings.summary)) + .and_then(|store| store.save_appearance_settings(self.settings.summary())) { Ok(settings) => { self.apply_gpui_settings(settings); diff --git a/crates/nyaterm-desktop/src/features/shell/event_pump/bridge.rs b/crates/nyaterm-desktop/src/features/shell/event_pump/bridge.rs index 6191f0ea8..b726d57af 100644 --- a/crates/nyaterm-desktop/src/features/shell/event_pump/bridge.rs +++ b/crates/nyaterm-desktop/src/features/shell/event_pump/bridge.rs @@ -3,7 +3,7 @@ use crate::features::NyaTermApp; impl NyaTermApp { pub(in crate::features) fn sync_session_event_bridge_config(&self) { self.session.configure_event_bridge( - self.settings.summary.interaction_default_encoding.clone(), + self.settings.summary().interaction_default_encoding.clone(), self.terminal_scrollback_line_limit(), ); } diff --git a/crates/nyaterm-desktop/src/features/shell/event_pump/mod.rs b/crates/nyaterm-desktop/src/features/shell/event_pump/mod.rs index 00f97ec7f..82059e490 100644 --- a/crates/nyaterm-desktop/src/features/shell/event_pump/mod.rs +++ b/crates/nyaterm-desktop/src/features/shell/event_pump/mod.rs @@ -256,18 +256,18 @@ impl NyaTermApp { cx: &mut Context, ) -> bool { if self.security.screen_locked() - || !self.settings.summary.enable_screen_lock - || self.settings.summary.idle_lock_minutes == 0 + || !self.settings.summary().enable_screen_lock + || self.settings.summary().idle_lock_minutes == 0 { return false; } let idle_for = self.security.screen_lock_idle_for(); let lock_after = - Duration::from_secs(u64::from(self.settings.summary.idle_lock_minutes) * 60); + Duration::from_secs(u64::from(self.settings.summary().idle_lock_minutes) * 60); if idle_for < lock_after { return false; } - let lock_status = if self.settings.summary.has_master_password { + let lock_status = if self.settings.summary().has_master_password { "Enter the master password to unlock.".to_string() } else { "No master password is configured.".to_string() @@ -276,9 +276,9 @@ impl NyaTermApp { self.forget_text_inputs("lock-screen.password"); self.terminal.view.status = format!( "screen locked after {} minute(s) idle", - self.settings.summary.idle_lock_minutes + self.settings.summary().idle_lock_minutes ); - if self.settings.summary.has_master_password { + if self.settings.summary().has_master_password { let field = self.text_input("lock-screen.password", "", TextInputSetup::masked(), cx); window.focus(&field.read(cx).focus_handle()); } else { @@ -371,7 +371,7 @@ impl NyaTermApp { if self.terminal.view.runtime.visual_bell_ticks > 0 { return true; } - if self.settings.summary.cursor_blink + if self.settings.summary().cursor_blink && !self.visible_terminal_session_ids().is_empty() && self .terminal @@ -538,30 +538,30 @@ impl NyaTermApp { let right_panel = self.current_right_panel(); if (right_panel == Some(NavItem::Stats) || self.header_status_needs_remote_stats()) - && self.settings.summary.ui_show_remote_stats + && self.settings.summary().ui_show_remote_stats && !self.remote_ops.stats_is_pending() && remote_refresh_due( self.remote_ops.stats_last_refresh_at(), - self.settings.summary.ui_remote_stats_interval.max(1), + self.settings.summary().ui_remote_stats_interval.max(1), ) { self.refresh_stats(window, cx); dirty = true; } else if right_panel == Some(NavItem::Processes) - && self.settings.summary.ui_show_process_manager + && self.settings.summary().ui_show_process_manager && !self.remote_ops.process_is_pending() && remote_refresh_due( self.remote_ops.process_last_refresh_at(), - self.settings.summary.ui_process_manager_interval.max(3), + self.settings.summary().ui_process_manager_interval.max(3), ) { self.refresh_processes(window, cx); dirty = true; } else if right_panel == Some(NavItem::Docker) - && self.settings.summary.ui_show_docker_manager + && self.settings.summary().ui_show_docker_manager && !self.remote_ops.docker_is_pending() { - let interval = self.settings.summary.ui_docker_manager_interval.max(3); + let interval = self.settings.summary().ui_docker_manager_interval.max(3); if remote_refresh_due(self.remote_ops.docker_last_refresh_at(), interval) { self.refresh_docker(window, cx); dirty = true; diff --git a/crates/nyaterm-desktop/src/features/shell/event_pump/planes.rs b/crates/nyaterm-desktop/src/features/shell/event_pump/planes.rs index a71e0ce7c..42910065c 100644 --- a/crates/nyaterm-desktop/src/features/shell/event_pump/planes.rs +++ b/crates/nyaterm-desktop/src/features/shell/event_pump/planes.rs @@ -674,7 +674,7 @@ impl NyaTermApp { // can stay slow without stretching cursor blink to multi-second periods. // Under output pressure / connect settle, keep last blink phase. let mut surface_visual_dirty = false; - if runtime_cursor_blink_allowed(output_pressure, self.settings.summary.cursor_blink) { + if runtime_cursor_blink_allowed(output_pressure, self.settings.summary().cursor_blink) { let next_blink_at = self .terminal .view @@ -689,7 +689,7 @@ impl NyaTermApp { } else { self.terminal.view.runtime.cursor_blink_next_at = Some(next_blink_at); } - } else if !self.settings.summary.cursor_blink { + } else if !self.settings.summary().cursor_blink { if !self.terminal.view.runtime.cursor_blink_on { surface_visual_dirty = true; } diff --git a/crates/nyaterm-desktop/src/features/shell/event_pump/session_events.rs b/crates/nyaterm-desktop/src/features/shell/event_pump/session_events.rs index a7fa631b9..8b4f2c3f3 100644 --- a/crates/nyaterm-desktop/src/features/shell/event_pump/session_events.rs +++ b/crates/nyaterm-desktop/src/features/shell/event_pump/session_events.rs @@ -348,7 +348,7 @@ impl NyaTermApp { self.note_zmodem_output_discontinuity(&session_id, bytes, cx); self.note_ai_agent_output_discontinuity(&session_id, bytes, cx); self.session.route_session_events_to_ui(&session_id); - let encoding = self.settings.summary.interaction_default_encoding.clone(); + let encoding = self.settings.summary().interaction_default_encoding.clone(); let view = self .terminal .view diff --git a/crates/nyaterm-desktop/src/features/shell/global_shortcut_runtime.rs b/crates/nyaterm-desktop/src/features/shell/global_shortcut_runtime.rs index b831395c3..3f695376b 100644 --- a/crates/nyaterm-desktop/src/features/shell/global_shortcut_runtime.rs +++ b/crates/nyaterm-desktop/src/features/shell/global_shortcut_runtime.rs @@ -39,7 +39,7 @@ impl NyaTermApp { return true; } - let keybindings = self.settings.summary.keybindings.clone(); + let keybindings = self.settings.summary().keybindings.clone(); if shortcut_matches(event, "terminal.copy", &keybindings) { self.copy_terminal_selection_or_visible(cx); return true; diff --git a/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keybindings.rs b/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keybindings.rs index 26f84464a..0683ac920 100644 --- a/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keybindings.rs +++ b/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keybindings.rs @@ -10,7 +10,7 @@ impl NyaTermApp { /// First display chord for empty-workspace / UI labels (Tauri-style chips). pub(in crate::features) fn display_shortcut_for(&self, id: &str, fallback: &str) -> String { use crate::shortcuts::{format_hotkey_for_display, shortcut_keys_for}; - let raw = shortcut_keys_for(id, &self.settings.summary.keybindings) + let raw = shortcut_keys_for(id, &self.settings.summary().keybindings) .unwrap_or_else(|| fallback.to_string()); let display = format_hotkey_for_display(&raw); display @@ -59,7 +59,7 @@ impl NyaTermApp { cx.notify(); return; } - let mut keybindings = self.settings.summary.keybindings.clone(); + let mut keybindings = self.settings.summary().keybindings.clone(); let is_default = crate::shortcuts::SHORTCUT_REGISTRY .iter() .find(|s| s.id == shortcut_id) @@ -77,7 +77,7 @@ impl NyaTermApp { shortcut_id: String, cx: &mut Context, ) { - let mut keybindings = self.settings.summary.keybindings.clone(); + let mut keybindings = self.settings.summary().keybindings.clone(); keybindings.remove(&shortcut_id); self.save_keybindings(keybindings, format!("shortcut {shortcut_id} reset"), cx); } @@ -92,7 +92,7 @@ impl NyaTermApp { success_message: String, cx: &mut Context, ) { - self.settings.summary.keybindings = keybindings.clone(); + self.settings.set_keybindings(keybindings.clone()); if self.defer_settings_persistence(cx) { self.settings.finish_keybinding_recording(); self.terminal.view.status = success_message.replace("saved", "staged"); @@ -177,7 +177,7 @@ impl NyaTermApp { } let existing = crate::shortcuts::shortcut_keys_for( shortcut.id, - &self.settings.summary.keybindings, + &self.settings.summary().keybindings, ) .unwrap_or_else(|| shortcut.default_keys.to_string()); let normalized_existing = existing diff --git a/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keyword_highlights.rs b/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keyword_highlights.rs index 1ef4639a4..f4049fdcc 100644 --- a/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keyword_highlights.rs +++ b/crates/nyaterm-desktop/src/features/shell/keybinding_runtime/keyword_highlights.rs @@ -8,8 +8,8 @@ const MAX_KEYWORD_HIGHLIGHT_IMPORT_BYTES: u64 = 4 * 1024 * 1024; impl NyaTermApp { pub(in crate::features) fn toggle_keyword_highlights(&mut self, cx: &mut Context) { - self.settings.keyword_config.enabled = !self.settings.keyword_config.enabled; - if !self.settings.keyword_config.enabled { + let enabled = self.settings.toggle_keyword_highlights(); + if !enabled { self.settings.clear_keyword_highlight_edit(); self.forget_text_inputs("keyword.highlight."); } @@ -20,8 +20,7 @@ impl NyaTermApp { &mut self, cx: &mut Context, ) { - self.settings.keyword_config.across_wrapped_lines = - !self.settings.keyword_config.across_wrapped_lines; + self.settings.toggle_keyword_highlights_wrapped(); self.save_keyword_highlights(cx); } @@ -34,10 +33,10 @@ impl NyaTermApp { self.runtime.config_dir(), self.runtime.portable_key_path().map(ToOwned::to_owned), ) - .and_then(|store| store.save_keyword_highlights(&self.settings.keyword_config)) + .and_then(|store| store.save_keyword_highlights(self.settings.keyword_config())) { Ok(config) => { - self.settings.keyword_config = config; + self.settings.replace_keyword_config(config); self.settings .set_store_message("keyword highlight settings saved".to_string()); self.settings.set_store_ready(true); @@ -158,7 +157,7 @@ impl NyaTermApp { self.runtime.portable_key_path().map(ToOwned::to_owned), ) { if let Ok(config) = store.load_keyword_highlights() { - self.settings.keyword_config = config; + self.settings.replace_keyword_config(config); self.forget_text_inputs("keyword.highlight."); } } @@ -169,17 +168,7 @@ impl NyaTermApp { rule_id: String, cx: &mut Context, ) { - let enabled = self - .settings - .keyword_config - .builtin_rules - .get(&rule_id) - .copied() - .unwrap_or(true); - self.settings - .keyword_config - .builtin_rules - .insert(rule_id, !enabled); + self.settings.toggle_keyword_highlight_builtin(rule_id); self.save_keyword_highlights(cx); } @@ -188,14 +177,7 @@ impl NyaTermApp { rule_id: String, cx: &mut Context, ) { - if let Some(rule) = self - .settings - .keyword_config - .rules - .iter_mut() - .find(|rule| rule.id == rule_id) - { - rule.enabled = !rule.enabled; + if self.settings.toggle_keyword_highlight_rule(&rule_id) { self.save_keyword_highlights(cx); } } @@ -266,18 +248,10 @@ impl NyaTermApp { } else { normalize_keyword_highlight_color(color) }; - if let Some(rule) = self + if self .settings - .keyword_config - .rules - .iter_mut() - .find(|rule| rule.id == rule_id) + .set_keyword_highlight_rule_color(&rule_id, dark, color.clone()) { - if dark { - rule.color_dark = color.clone(); - } else { - rule.color_light = color.clone(); - } let field = if dark { KeywordHighlightEditorField::ColorDark } else { @@ -301,7 +275,7 @@ impl NyaTermApp { ) { let value = self .settings - .keyword_config + .keyword_config() .rules .iter() .find(|rule| rule.id == rule_id) @@ -389,29 +363,13 @@ impl NyaTermApp { KeywordHighlightEditorField::ColorDark | KeywordHighlightEditorField::ColorLight ) .then(|| normalize_keyword_highlight_color(&text)); - let Some(rule) = self + let value = normalized_color.clone().unwrap_or(text); + if !self .settings - .keyword_config - .rules - .iter_mut() - .find(|rule| rule.id == rule_id) - else { + .apply_keyword_highlight_rule_input(rule_id, field, value) + { return; - }; - match field { - KeywordHighlightEditorField::Name => rule.name = text, - KeywordHighlightEditorField::Patterns => { - rule.patterns = text.split('\n').map(ToOwned::to_owned).collect(); - } - KeywordHighlightEditorField::ColorDark => { - rule.color_dark = normalized_color.clone().unwrap_or_default(); - } - KeywordHighlightEditorField::ColorLight => { - rule.color_light = normalized_color.clone().unwrap_or_default(); - } } - self.settings - .begin_keyword_highlight_edit(rule_id.to_string(), field); if let Some(color) = normalized_color { self.reset_text_input(&keyword_highlight_text_input_id(rule_id, field), &color, cx); } diff --git a/crates/nyaterm-desktop/src/features/shell/panel_resize_runtime.rs b/crates/nyaterm-desktop/src/features/shell/panel_resize_runtime.rs index f651a3e54..855a23e41 100644 --- a/crates/nyaterm-desktop/src/features/shell/panel_resize_runtime.rs +++ b/crates/nyaterm-desktop/src/features/shell/panel_resize_runtime.rs @@ -4,7 +4,7 @@ use gpui::{ }; use nyaterm_core::ConnectionStore; -use crate::features::NyaTermApp; +use crate::features::{NyaTermApp, settings::UiLayoutSettingsUpdate}; use crate::models::{ BottomPanelMode, NavItem, PanelResizeSide, PanelSide, panel_collapsed_from_persistence, }; @@ -16,8 +16,6 @@ const BOTTOM_PANEL_HEIGHT_MAX: f32 = 520.; impl NyaTermApp { pub(in crate::features) fn set_bottom_panel_mode(&mut self, mode: BottomPanelMode) { self.shell.bottom_panel.mode = mode; - self.settings.summary.ui_quick_cmd_visible = mode == BottomPanelMode::QuickCommands; - self.settings.summary.ui_serial_send_visible = mode == BottomPanelMode::CommandSend; self.persist_ui_layout(); } @@ -76,90 +74,110 @@ impl NyaTermApp { } pub(in crate::features) fn apply_ui_layout_from_settings(&mut self) { - self.shell.panels.left_width = self.settings.summary.ui_left_panel_width as f32; - self.shell.panels.right_width = self.settings.summary.ui_right_panel_width as f32; + self.shell.panels.left_width = self.settings.summary().ui_left_panel_width as f32; + self.shell.panels.right_width = self.settings.summary().ui_right_panel_width as f32; self.transfer - .set_panel_height(self.settings.summary.ui_transfer_height as f32); + .set_panel_height(self.settings.summary().ui_transfer_height as f32); self.shell.bottom_panel.quick_commands_height = - self.settings.summary.ui_quick_cmd_height as f32; + self.settings.summary().ui_quick_cmd_height as f32; self.shell.bottom_panel.command_send_height = - self.settings.summary.ui_serial_send_height as f32; + self.settings.summary().ui_serial_send_height as f32; self.apply_activity_layout_from_settings(); self.shell.panels.active_left = self .settings - .summary + .summary() .ui_active_left_panel .as_deref() .and_then(NavItem::from_persistence_id) .filter(|item| self.panel_side_for_item(*item) == Some(PanelSide::Left)); self.shell.panels.active_right = self .settings - .summary + .summary() .ui_active_right_panel .as_deref() .and_then(NavItem::from_persistence_id) .filter(|item| self.panel_side_for_item(*item) == Some(PanelSide::Right)); self.shell.panels.left_collapsed = panel_collapsed_from_persistence( - self.settings.summary.ui_left_panel_collapsed, - self.settings.summary.ui_panel_multi_open, + self.settings.summary().ui_left_panel_collapsed, + self.settings.summary().ui_panel_multi_open, self.shell.panels.active_left.is_some(), - !self.settings.summary.ui_left_open_panels.is_empty(), + !self.settings.summary().ui_left_open_panels.is_empty(), ); self.shell.panels.right_collapsed = panel_collapsed_from_persistence( - self.settings.summary.ui_right_panel_collapsed, - self.settings.summary.ui_panel_multi_open, + self.settings.summary().ui_right_panel_collapsed, + self.settings.summary().ui_panel_multi_open, self.shell.panels.active_right.is_some(), - !self.settings.summary.ui_right_open_panels.is_empty(), + !self.settings.summary().ui_right_open_panels.is_empty(), ); self.apply_panel_stack_from_settings(); - if !self.settings.summary.has_master_password { + if !self.settings.summary().has_master_password { self.security.unlock_without_master_password(); } } pub(in crate::features) fn persist_ui_layout(&mut self) { - self.settings.summary.ui_left_panel_width = - self.shell.panels.left_width.round().clamp(160., 720.) as u32; - self.settings.summary.ui_right_panel_width = - self.shell.panels.right_width.round().clamp(200., 720.) as u32; - self.settings.summary.ui_transfer_height = - self.transfer.panel_height().round().clamp(60., 600.) as u32; - self.settings.summary.ui_quick_cmd_height = - self.shell + let update = UiLayoutSettingsUpdate { + left_panel_width: self.shell.panels.left_width.round().clamp(160., 720.) as u32, + right_panel_width: self.shell.panels.right_width.round().clamp(200., 720.) as u32, + transfer_height: self.transfer.panel_height().round().clamp(60., 600.) as u32, + quick_command_height: self + .shell .bottom_panel .quick_commands_height .round() - .clamp(QUICK_CMD_HEIGHT_MIN, BOTTOM_PANEL_HEIGHT_MAX) as u32; - self.settings.summary.ui_serial_send_height = - self.shell + .clamp(QUICK_CMD_HEIGHT_MIN, BOTTOM_PANEL_HEIGHT_MAX) + as u32, + quick_command_visible: self.shell.bottom_panel.mode == BottomPanelMode::QuickCommands, + serial_send_height: self + .shell .bottom_panel .command_send_height .round() - .clamp(SERIAL_SEND_HEIGHT_MIN, BOTTOM_PANEL_HEIGHT_MAX) as u32; - self.settings.summary.ui_active_left_panel = self - .shell - .panels - .active_left - .map(|item| item.persistence_id().to_string()); - self.settings.summary.ui_active_right_panel = self - .shell - .panels - .active_right - .map(|item| item.persistence_id().to_string()); - self.settings.summary.ui_left_panel_collapsed = self.shell.panels.left_collapsed; - self.settings.summary.ui_right_panel_collapsed = self.shell.panels.right_collapsed; - self.settings.summary.ui_saved_connections_sort_mode = self - .connection_state - .list_sort_mode() - .persistence_id() - .to_string(); - self.sync_activity_layout_to_settings(); - self.sync_panel_stack_to_settings(); + .clamp(SERIAL_SEND_HEIGHT_MIN, BOTTOM_PANEL_HEIGHT_MAX) + as u32, + serial_send_visible: self.shell.bottom_panel.mode == BottomPanelMode::CommandSend, + active_left_panel: self + .shell + .panels + .active_left + .map(|item| item.persistence_id().to_string()), + active_right_panel: self + .shell + .panels + .active_right + .map(|item| item.persistence_id().to_string()), + left_panel_collapsed: self.shell.panels.left_collapsed, + right_panel_collapsed: self.shell.panels.right_collapsed, + saved_connections_sort_mode: self + .connection_state + .list_sort_mode() + .persistence_id() + .to_string(), + activity_bar_left_top: self.shell.chrome.activity_bar_layout.left_top.clone(), + activity_bar_left_bottom: self.shell.chrome.activity_bar_layout.left_bottom.clone(), + activity_bar_right_top: self.shell.chrome.activity_bar_layout.right_top.clone(), + activity_bar_right_bottom: self.shell.chrome.activity_bar_layout.right_bottom.clone(), + activity_bar_show_labels: self.shell.chrome.activity_bar_layout.show_labels, + panel_multi_open: self.shell.panels.multi_open, + left_open_panels: self.shell.panels.left_open.clone(), + right_open_panels: self.shell.panels.right_open.clone(), + panel_stack_sizes: self + .shell + .panels + .stack_sizes + .iter() + .filter_map(|(key, value)| { + let scaled = (*value * 1000.).round(); + (scaled.is_finite() && scaled > 0.).then(|| (key.clone(), scaled as u32)) + }) + .collect(), + }; + self.settings.apply_ui_layout(update); if let Ok(store) = ConnectionStore::open_with_portable_key_path( self.runtime.config_dir(), self.runtime.portable_key_path().map(ToOwned::to_owned), ) { - match store.save_ui_layout_settings(&self.settings.summary) { + match store.save_ui_layout_settings(self.settings.summary()) { Ok(summary) => { self.apply_gpui_settings(summary); self.settings.set_store_ready(true); diff --git a/crates/nyaterm-desktop/src/features/shell/panel_stack_runtime.rs b/crates/nyaterm-desktop/src/features/shell/panel_stack_runtime.rs index 2e5a4cab3..a2fba78d7 100644 --- a/crates/nyaterm-desktop/src/features/shell/panel_stack_runtime.rs +++ b/crates/nyaterm-desktop/src/features/shell/panel_stack_runtime.rs @@ -366,29 +366,13 @@ impl NyaTermApp { } } - pub(in crate::features) fn sync_panel_stack_to_settings(&mut self) { - self.settings.summary.ui_panel_multi_open = self.shell.panels.multi_open; - self.settings.summary.ui_left_open_panels = self.shell.panels.left_open.clone(); - self.settings.summary.ui_right_open_panels = self.shell.panels.right_open.clone(); - self.settings.summary.ui_panel_stack_sizes = self - .shell - .panels - .stack_sizes - .iter() - .filter_map(|(key, value)| { - let scaled = (*value * 1000.).round(); - (scaled.is_finite() && scaled > 0.).then(|| (key.clone(), scaled as u32)) - }) - .collect(); - } - pub(in crate::features) fn apply_panel_stack_from_settings(&mut self) { - self.shell.panels.multi_open = self.settings.summary.ui_panel_multi_open; - self.shell.panels.left_open = self.settings.summary.ui_left_open_panels.clone(); - self.shell.panels.right_open = self.settings.summary.ui_right_open_panels.clone(); + self.shell.panels.multi_open = self.settings.summary().ui_panel_multi_open; + self.shell.panels.left_open = self.settings.summary().ui_left_open_panels.clone(); + self.shell.panels.right_open = self.settings.summary().ui_right_open_panels.clone(); self.shell.panels.stack_sizes = self .settings - .summary + .summary() .ui_panel_stack_sizes .iter() .filter_map(|(key, value)| (*value > 0).then(|| (key.clone(), (*value as f32) / 1000.))) diff --git a/crates/nyaterm-desktop/src/features/shell/tab_mouse.rs b/crates/nyaterm-desktop/src/features/shell/tab_mouse.rs index 6b9369ff5..162f8eded 100644 --- a/crates/nyaterm-desktop/src/features/shell/tab_mouse.rs +++ b/crates/nyaterm-desktop/src/features/shell/tab_mouse.rs @@ -225,22 +225,24 @@ impl NyaTermApp { let action = normalize_tab_mouse_action(action); match target { TabMouseActionTarget::Double => { - if self.settings.summary.interaction_tab_double_click_action == action { + if self.settings.summary().interaction_tab_double_click_action == action { return; } - self.settings.summary.interaction_tab_double_click_action = action.to_string(); + self.settings + .set_tab_double_click_action(action.to_string()); } TabMouseActionTarget::Middle => { - if self.settings.summary.interaction_tab_middle_click_action == action { + if self.settings.summary().interaction_tab_middle_click_action == action { return; } - self.settings.summary.interaction_tab_middle_click_action = action.to_string(); + self.settings + .set_tab_middle_click_action(action.to_string()); } TabMouseActionTarget::Right => { - if self.settings.summary.interaction_tab_right_click_action == action { + if self.settings.summary().interaction_tab_right_click_action == action { return; } - self.settings.summary.interaction_tab_right_click_action = action.to_string(); + self.settings.set_tab_right_click_action(action.to_string()); } } self.save_interaction_settings(cx); @@ -260,7 +262,7 @@ impl NyaTermApp { cx.stop_propagation(); let action = self .settings - .summary + .summary() .interaction_tab_middle_click_action .clone(); if action == "none" { @@ -276,7 +278,7 @@ impl NyaTermApp { cx.stop_propagation(); let action = self .settings - .summary + .summary() .interaction_tab_right_click_action .clone(); if action == "none" { @@ -301,7 +303,7 @@ impl NyaTermApp { if is_left_double_click { let action = self .settings - .summary + .summary() .interaction_tab_double_click_action .clone(); if action != "none" { diff --git a/crates/nyaterm-desktop/src/features/shell/tab_windows_runtime.rs b/crates/nyaterm-desktop/src/features/shell/tab_windows_runtime.rs index 1a8c8802b..3d60e769b 100644 --- a/crates/nyaterm-desktop/src/features/shell/tab_windows_runtime.rs +++ b/crates/nyaterm-desktop/src/features/shell/tab_windows_runtime.rs @@ -247,8 +247,8 @@ impl NyaTermApp { } pub(in crate::features) fn persist_terminal_window_layout(&mut self) { - if !self.settings.summary.startup_restore - || !self.settings.summary.startup_restore_window_layout + if !self.settings.summary().startup_restore + || !self.settings.summary().startup_restore_window_layout { return; } @@ -260,8 +260,8 @@ impl NyaTermApp { if self.terminal.windows.restored { return; } - if !self.settings.summary.startup_restore - || !self.settings.summary.startup_restore_window_layout + if !self.settings.summary().startup_restore + || !self.settings.summary().startup_restore_window_layout { self.terminal.windows.restored = true; return; diff --git a/crates/nyaterm-desktop/src/features/shell/workspace_runtime.rs b/crates/nyaterm-desktop/src/features/shell/workspace_runtime.rs index 8e1a44363..ed41fc5e8 100644 --- a/crates/nyaterm-desktop/src/features/shell/workspace_runtime.rs +++ b/crates/nyaterm-desktop/src/features/shell/workspace_runtime.rs @@ -452,8 +452,8 @@ impl NyaTermApp { } } pub(in crate::features) fn persist_workspace_pane_layout(&mut self) { - if !self.settings.summary.startup_restore - || !self.settings.summary.startup_restore_window_layout + if !self.settings.summary().startup_restore + || !self.settings.summary().startup_restore_window_layout { return; } @@ -500,8 +500,8 @@ impl NyaTermApp { if self.shell.workspace.pane_layout_restored { return; } - if !self.settings.summary.startup_restore - || !self.settings.summary.startup_restore_window_layout + if !self.settings.summary().startup_restore + || !self.settings.summary().startup_restore_window_layout { self.shell.workspace.pane_layout_restored = true; return; diff --git a/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/jobs.rs b/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/jobs.rs index 6a09c6713..9a47d7155 100644 --- a/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/jobs.rs +++ b/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/jobs.rs @@ -408,7 +408,7 @@ impl NyaTermApp { pub(in crate::features) fn refresh_cloud_sync_history(&mut self) { let history = read_cloud_sync_history( self.runtime.log_dir(), - self.settings.summary.diagnostics_retention_days, + self.settings.summary().diagnostics_retention_days, CLOUD_SYNC_HISTORY_LIMIT, ) .unwrap_or_default(); diff --git a/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/settings.rs b/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/settings.rs index 445627701..b22313140 100644 --- a/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/settings.rs +++ b/crates/nyaterm-desktop/src/features/sync/cloud_sync_runtime/settings.rs @@ -21,9 +21,9 @@ impl NyaTermApp { pub(in crate::features) fn toggle_cloud_sync_enabled(&mut self, cx: &mut Context) { if !self.cloud_sync.settings().enabled - && (!self.settings.master_password.enabled - || (!self.settings.summary.has_master_password - && self.settings.master_password.draft.is_empty())) + && (!self.settings.master_password().enabled + || (!self.settings.summary().has_master_password + && self.settings.master_password().draft.is_empty())) { self.shell.set_settings_active_tab(SettingsTab::Security); self.cloud_sync @@ -91,8 +91,8 @@ impl NyaTermApp { } pub(in crate::features) fn cloud_sync_form_enabled(&self) -> bool { - self.settings.master_password.enabled - && (self.settings.summary.has_master_password - || !self.settings.master_password.draft.is_empty()) + self.settings.master_password().enabled + && (self.settings.summary().has_master_password + || !self.settings.master_password().draft.is_empty()) } } diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_context_menu_runtime/menu.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_context_menu_runtime/menu.rs index e0e79b92c..e5ac4d93d 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_context_menu_runtime/menu.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_context_menu_runtime/menu.rs @@ -64,7 +64,7 @@ impl NyaTermApp { let selected_for_paste = selected.clone(); let search_engines: Vec<(String, String, Option)> = self .settings - .summary + .summary() .search_custom_engines .iter() .filter(|engine| { @@ -118,9 +118,9 @@ impl NyaTermApp { .collect(); let selection_link_kind: Option<&'static str> = None; let selection_actions: Vec<(String, ActionLinkAction)> = - if self.settings.summary.terminal_action_links_enabled && has_selection { + if self.settings.summary().terminal_action_links_enabled && has_selection { let trimmed = selected.trim(); - let matchers = &self.settings.summary.terminal_action_links_matchers; + let matchers = &self.settings.summary().terminal_action_links_matchers; let entity = find_action_links(trimmed, matchers, true) .into_iter() .find(|item| item.text == trimmed || item.value == trimmed) diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/buffer.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/buffer.rs index c8b521a36..1eda96d18 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/buffer.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/buffer.rs @@ -186,7 +186,7 @@ fn terminal_scroll_snapshot_request_action_links_enabled( impl NyaTermApp { pub(in crate::features) fn terminal_scrollback_line_limit(&self) -> usize { self.settings - .summary + .summary() .terminal_scrollback_lines .clamp(100, 100_000) as usize } @@ -216,7 +216,7 @@ impl NyaTermApp { self.terminal.view.frame_pipeline.submit_output( session_id.to_string(), data, - self.settings.summary.interaction_default_encoding.clone(), + self.settings.summary().interaction_default_encoding.clone(), self.terminal_scrollback_line_limit(), ); } @@ -228,7 +228,7 @@ impl NyaTermApp { if outputs.is_empty() { return; } - let encoding = self.settings.summary.interaction_default_encoding.clone(); + let encoding = self.settings.summary().interaction_default_encoding.clone(); let scrollback_limit = self.terminal_scrollback_line_limit(); let submissions = outputs .into_iter() @@ -271,9 +271,12 @@ impl NyaTermApp { self.terminal.view.frame_pipeline.request_snapshot( session_id.to_string(), offset, - self.settings.summary.terminal_action_links_enabled - && !self.settings.summary.terminal_low_latency_mode, - self.settings.summary.terminal_action_links_matchers.clone(), + self.settings.summary().terminal_action_links_enabled + && !self.settings.summary().terminal_low_latency_mode, + self.settings + .summary() + .terminal_action_links_matchers + .clone(), ); true } @@ -299,7 +302,10 @@ impl NyaTermApp { session_id.to_string(), offset, false, - self.settings.summary.terminal_action_links_matchers.clone(), + self.settings + .summary() + .terminal_action_links_matchers + .clone(), ); true } @@ -390,21 +396,27 @@ impl NyaTermApp { if priority { let action_links_enabled = terminal_scroll_snapshot_request_action_links_enabled( priority, - self.settings.summary.terminal_action_links_enabled, - self.settings.summary.terminal_low_latency_mode, + self.settings.summary().terminal_action_links_enabled, + self.settings.summary().terminal_low_latency_mode, ); self.terminal.view.frame_pipeline.request_priority_snapshot( session_id.to_string(), offset, action_links_enabled, - self.settings.summary.terminal_action_links_matchers.clone(), + self.settings + .summary() + .terminal_action_links_matchers + .clone(), ); } else { self.terminal.view.frame_pipeline.request_snapshot( session_id.to_string(), offset, false, - self.settings.summary.terminal_action_links_matchers.clone(), + self.settings + .summary() + .terminal_action_links_matchers + .clone(), ); } true @@ -418,14 +430,14 @@ impl NyaTermApp { ) -> bool { if session_id.is_empty() || offset == 0 - || !self.settings.summary.terminal_action_links_enabled - || self.settings.summary.terminal_low_latency_mode + || !self.settings.summary().terminal_action_links_enabled + || self.settings.summary().terminal_low_latency_mode { return false; } let matcher_key = terminal_action_link_matcher_key( true, - &self.settings.summary.terminal_action_links_matchers, + &self.settings.summary().terminal_action_links_matchers, ); let Some(view) = self.terminal.view.views.get_mut(session_id) else { return false; @@ -452,7 +464,10 @@ impl NyaTermApp { session_id.to_string(), offset, true, - self.settings.summary.terminal_action_links_matchers.clone(), + self.settings + .summary() + .terminal_action_links_matchers + .clone(), ); true } @@ -490,7 +505,7 @@ impl NyaTermApp { self.terminal.view.frame_pipeline.seed_session( session_id.to_string(), output, - self.settings.summary.interaction_default_encoding.clone(), + self.settings.summary().interaction_default_encoding.clone(), self.terminal_scrollback_line_limit(), ); } @@ -997,14 +1012,15 @@ impl NyaTermApp { "terminal frame snapshot row reuse" ); // Snapshot applies only dirties the surface, not chrome. - let current_action_link_matcher_key = (self.settings.summary.terminal_action_links_enabled - && !self.settings.summary.terminal_low_latency_mode) - .then(|| { - terminal_action_link_matcher_key( - true, - &self.settings.summary.terminal_action_links_matchers, - ) - }); + let current_action_link_matcher_key = + (self.settings.summary().terminal_action_links_enabled + && !self.settings.summary().terminal_low_latency_mode) + .then(|| { + terminal_action_link_matcher_key( + true, + &self.settings.summary().terminal_action_links_matchers, + ) + }); let should_paint = self.terminal_session_has_visible_surface(&frame.session_id) && self .terminal @@ -1164,7 +1180,7 @@ impl NyaTermApp { session_id: &str, data: &[u8], ) -> String { - let encoding = self.settings.summary.interaction_default_encoding.clone(); + let encoding = self.settings.summary().interaction_default_encoding.clone(); let view = self .terminal .view @@ -1213,7 +1229,7 @@ impl NyaTermApp { if let Some(session_id) = session_id { let is_active = self.session.active_id() == Some(session_id); - let encoding = self.settings.summary.interaction_default_encoding.clone(); + let encoding = self.settings.summary().interaction_default_encoding.clone(); let view = self .terminal .view diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/paste.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/paste.rs index 146c4a8e3..7ba9f7d48 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/paste.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/paste.rs @@ -29,7 +29,11 @@ impl NyaTermApp { cx.notify(); return; } - if self.settings.summary.terminal_show_multi_line_paste_dialog && is_multi_line_paste(&text) + if self + .settings + .summary() + .terminal_show_multi_line_paste_dialog + && is_multi_line_paste(&text) { self.terminal.paste.open(text); self.terminal.view.status = "multi-line paste confirmation opened".to_string(); diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/sessions.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/sessions.rs index c91a30732..083ea03bb 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/sessions.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/sessions.rs @@ -93,7 +93,7 @@ impl NyaTermApp { self.terminal .view .screen - .set_encoding(&self.settings.summary.interaction_default_encoding); + .set_encoding(&self.settings.summary().interaction_default_encoding); self.terminal.view.status = "session closed".to_string(); } } else { @@ -160,7 +160,7 @@ impl NyaTermApp { self.terminal .view .screen - .set_encoding(&self.settings.summary.interaction_default_encoding); + .set_encoding(&self.settings.summary().interaction_default_encoding); } } @@ -179,7 +179,7 @@ impl NyaTermApp { // Tauri minimize_to_tray: hide window instead of taskbar minimize when enabled. // GPUI lacks a portable tray today; minimize still uses the platform minimize path, // and the flag is honored as a documented no-op tray intent with status feedback. - if self.settings.summary.minimize_to_tray { + if self.settings.summary().minimize_to_tray { window.minimize_window(); self.terminal.view.status = "minimized (tray mode preferred; OS tray polish pending)".to_string(); @@ -195,7 +195,7 @@ impl NyaTermApp { cx: &mut Context, ) { let open_sessions = self.ordered_sessions().len(); - if self.settings.summary.confirm_on_close && open_sessions > 0 { + if self.settings.summary().confirm_on_close && open_sessions > 0 { // Reuse close-all confirmation as quit-with-sessions gate (Tauri confirm_on_close). self.session.dialogs.request_quit_after_close_all(); self.open_close_all_sessions_confirm(window, cx); @@ -205,7 +205,7 @@ impl NyaTermApp { return; } // Persist workspace before exit when startup restore is enabled. - if self.settings.summary.startup_restore { + if self.settings.summary().startup_restore { self.flush_open_tabs_now(); } window.remove_window(); @@ -244,7 +244,7 @@ impl NyaTermApp { let quit_after = self.session.dialogs.take_close_all_sessions_confirm(); self.close_all_sessions(cx); if quit_after { - if self.settings.summary.startup_restore { + if self.settings.summary().startup_restore { self.flush_open_tabs_now(); } self.terminal.view.status = "sessions closed; closing window".to_string(); diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/view_io.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/view_io.rs index 52087c3e0..bf7f9a89d 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/view_io.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_runtime/view_io.rs @@ -713,9 +713,9 @@ impl NyaTermApp { if track_suggestions { if terminal_should_track_command_suggestion_input( track_suggestions, - self.settings.summary.terminal_low_latency_mode, + self.settings.summary().terminal_low_latency_mode, self.settings - .summary + .summary() .interaction_command_suggestions_enabled, ) { self.note_command_suggestion_input(&bytes, cx); @@ -851,9 +851,9 @@ impl NyaTermApp { if track_suggestions { if terminal_should_track_command_suggestion_input( track_suggestions, - self.settings.summary.terminal_low_latency_mode, + self.settings.summary().terminal_low_latency_mode, self.settings - .summary + .summary() .interaction_command_suggestions_enabled, ) { self.note_command_suggestion_input(&primary_bytes, cx); @@ -1392,7 +1392,7 @@ impl NyaTermApp { /// Keep all live terminal screens on the current interaction encoding. pub(in crate::features) fn sync_terminal_encodings_from_settings(&mut self) { - let label = self.settings.summary.interaction_default_encoding.clone(); + let label = self.settings.summary().interaction_default_encoding.clone(); self.terminal.view.screen.set_encoding(&label); self.terminal.view.output_decoder.set_encoding(&label); for view in self.terminal.view.views.values_mut() { @@ -1628,7 +1628,7 @@ impl NyaTermApp { terminal_key_bytes_for_mode_and_settings( event, self.terminal_key_mode_for_session(session_id), - self.settings.summary.interaction_alt_as_meta, + self.settings.summary().interaction_alt_as_meta, ) } @@ -1637,7 +1637,7 @@ impl NyaTermApp { event: &KeyDownEvent, ) -> bool { terminal_should_defer_key_text_to_input_handler_for_state( - self.settings.summary.interaction_mac_ime_compatibility, + self.settings.summary().interaction_mac_ime_compatibility, &self.terminal.input.ime_marked_text, event, ) @@ -1716,7 +1716,7 @@ impl NyaTermApp { }; let matcher_key = terminal_action_link_matcher_key( true, - &self.settings.summary.terminal_action_links_matchers, + &self.settings.summary().terminal_action_links_matchers, ); if view.frame_action_links.as_ref().is_some_and(|links| { links.matcher_key == matcher_key && links.covers_all_snapshot_rows(snapshot.as_ref()) @@ -1726,7 +1726,7 @@ impl NyaTermApp { let Some(action_links) = crate::models::prepare_terminal_frame_action_links( snapshot.as_ref(), true, - &self.settings.summary.terminal_action_links_matchers, + &self.settings.summary().terminal_action_links_matchers, ) else { return; }; @@ -1774,8 +1774,8 @@ impl NyaTermApp { ) else { return false; }; - let action_links_enabled = self.settings.summary.terminal_action_links_enabled - && !self.settings.summary.terminal_low_latency_mode; + let action_links_enabled = self.settings.summary().terminal_action_links_enabled + && !self.settings.summary().terminal_low_latency_mode; self.ensure_terminal_live_action_links_for_snapshot( session_id, display_offset, @@ -1788,12 +1788,12 @@ impl NyaTermApp { let palette = self.terminal_theme_palette(); let transparent_background = self.wallpaper_enabled(); let font_family = self.gpui_terminal_font_family(); - let font_size = self.settings.summary.terminal_font_size as f32; - let normal_weight = self.settings.summary.terminal_font_weight as f32; - let bold_weight = self.settings.summary.terminal_font_weight_bold as f32; - let show_line_numbers = self.settings.summary.terminal_show_line_numbers; - let show_timestamps = self.settings.summary.terminal_show_timestamps; - let show_timestamp_ms = self.settings.summary.terminal_show_timestamp_milliseconds; + let font_size = self.settings.summary().terminal_font_size as f32; + let normal_weight = self.settings.summary().terminal_font_weight as f32; + let bold_weight = self.settings.summary().terminal_font_weight_bold as f32; + let show_line_numbers = self.settings.summary().terminal_show_line_numbers; + let show_timestamps = self.settings.summary().terminal_show_timestamps; + let show_timestamp_ms = self.settings.summary().terminal_show_timestamp_milliseconds; let (cell_w, cell_h) = self .terminal .layout @@ -1803,14 +1803,14 @@ impl NyaTermApp { let visual_bell = is_active && self.terminal.view.runtime.visual_bell_ticks > 0; let layout_cache = view.render_cache.layout_cache.clone(); let render_degraded = - view.render_degraded || self.settings.summary.terminal_low_latency_mode; + view.render_degraded || self.settings.summary().terminal_low_latency_mode; let has_new = view.has_new_while_scrolled; let performance_overlay = view.performance_overlay; let skipped = view.skipped_output_chars; let protocol_state = view.protocol_state; let search_matches = if is_active && !input_latency_active - && !self.settings.summary.terminal_low_latency_mode + && !self.settings.summary().terminal_low_latency_mode && self.terminal.search.open && self.terminal.search.mode == TerminalSearchMode::Buffer { @@ -1820,7 +1820,7 @@ impl NyaTermApp { }; let action_link_matcher_key = terminal_action_link_matcher_key( action_links_enabled, - &self.settings.summary.terminal_action_links_matchers, + &self.settings.summary().terminal_action_links_matchers, ); let frame_action_links = action_links_enabled .then(|| { @@ -1845,7 +1845,9 @@ impl NyaTermApp { &frame_action_links, action_links_enabled, action_links_enabled, - is_active && !input_latency_active && !self.settings.summary.terminal_low_latency_mode, + is_active + && !input_latency_active + && !self.settings.summary().terminal_low_latency_mode, ); let has_action_link_decorations = crate::features::terminal::terminal_surface::terminal_action_links_have_ranges_for_snapshot( @@ -1991,7 +1993,7 @@ impl NyaTermApp { || mode == TerminalPerformanceMode::Overloaded || user_scroll_active || input_latency_active - || self.settings.summary.terminal_low_latency_mode; + || self.settings.summary().terminal_low_latency_mode; let render_degraded = render_degraded_view || render_pressure; let configured_keyword_rules = self.resolved_keyword_highlight_rules(); let clear_keyword_highlights = configured_keyword_rules.is_empty(); @@ -2036,8 +2038,8 @@ impl NyaTermApp { retained_surface_snapshot, ); let snapshot_duration = snapshot_started_at.elapsed(); - let action_links_enabled = self.settings.summary.terminal_action_links_enabled - && !self.settings.summary.terminal_low_latency_mode; + let action_links_enabled = self.settings.summary().terminal_action_links_enabled + && !self.settings.summary().terminal_low_latency_mode; if let Some(snapshot) = snapshot.as_ref() { self.ensure_terminal_live_action_links_for_snapshot( session_id, @@ -2050,12 +2052,12 @@ impl NyaTermApp { let palette = self.terminal_theme_palette(); let transparent_background = self.wallpaper_enabled(); let font_family = self.gpui_terminal_font_family(); - let font_size = self.settings.summary.terminal_font_size as f32; - let normal_weight = self.settings.summary.terminal_font_weight as f32; - let bold_weight = self.settings.summary.terminal_font_weight_bold as f32; - let show_line_numbers = self.settings.summary.terminal_show_line_numbers; - let show_timestamps = self.settings.summary.terminal_show_timestamps; - let show_timestamp_ms = self.settings.summary.terminal_show_timestamp_milliseconds; + let font_size = self.settings.summary().terminal_font_size as f32; + let normal_weight = self.settings.summary().terminal_font_weight as f32; + let bold_weight = self.settings.summary().terminal_font_weight_bold as f32; + let show_line_numbers = self.settings.summary().terminal_show_line_numbers; + let show_timestamps = self.settings.summary().terminal_show_timestamps; + let show_timestamp_ms = self.settings.summary().terminal_show_timestamp_milliseconds; let (cell_w, cell_h) = self .terminal .layout @@ -2133,7 +2135,7 @@ impl NyaTermApp { let remote_cursor_visible = snapshot.cursor.visible && snapshot.cursor.shape != nyaterm_terminal::CursorShape::Hidden && cursor_row != usize::MAX; - let blink_enabled = self.settings.summary.cursor_blink || snapshot.cursor.blinking; + let blink_enabled = self.settings.summary().cursor_blink || snapshot.cursor.blinking; let show_cursor = terminal_cursor_visible_for_display_offset( is_active, is_disconnected, @@ -2145,8 +2147,8 @@ impl NyaTermApp { let cursor_style = match snapshot.cursor.shape { nyaterm_terminal::CursorShape::Underline => "underline".to_string(), nyaterm_terminal::CursorShape::Beam => "bar".to_string(), - nyaterm_terminal::CursorShape::Hidden => self.settings.summary.cursor_style.clone(), - nyaterm_terminal::CursorShape::Block => self.settings.summary.cursor_style.clone(), + nyaterm_terminal::CursorShape::Hidden => self.settings.summary().cursor_style.clone(), + nyaterm_terminal::CursorShape::Block => self.settings.summary().cursor_style.clone(), }; let search_mapping_started_at = Instant::now(); @@ -2161,7 +2163,7 @@ impl NyaTermApp { let enhanced = paint_policy.enhanced_decorations; let action_link_matcher_key = terminal_action_link_matcher_key( action_links_enabled, - &self.settings.summary.terminal_action_links_matchers, + &self.settings.summary().terminal_action_links_matchers, ); let frame_action_links = action_links_enabled .then(|| { diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/action_links.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/action_links.rs index c580b7a76..9c9f18e80 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/action_links.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/action_links.rs @@ -75,8 +75,8 @@ impl NyaTermApp { event: &MouseMoveEvent, cx: &mut Context, ) { - if !self.settings.summary.terminal_action_links_enabled - || self.settings.summary.terminal_low_latency_mode + if !self.settings.summary().terminal_action_links_enabled + || self.settings.summary().terminal_low_latency_mode || self.runtime_output_pressure_active() { self.clear_action_link_tooltip(cx); @@ -179,7 +179,7 @@ impl NyaTermApp { position: Point, cx: &App, ) -> Option<(ActionLinkMatch, Vec)> { - if !self.settings.summary.terminal_action_links_enabled { + if !self.settings.summary().terminal_action_links_enabled { return None; } let session_id = self.terminal_session_at_point(position)?; @@ -203,8 +203,8 @@ impl NyaTermApp { } let cell = self.point_to_terminal_cell_for_session(session_id, position, cx)?; let action_link_matcher_key = terminal_action_link_matcher_key( - self.settings.summary.terminal_action_links_enabled, - &self.settings.summary.terminal_action_links_matchers, + self.settings.summary().terminal_action_links_enabled, + &self.settings.summary().terminal_action_links_matchers, ); let offset = self.terminal_display_offset_for_session(session_id); let snapshot = self.terminal_snapshot_for_session(session_id, offset); @@ -245,7 +245,7 @@ impl NyaTermApp { .cloned() }) .or_else(|| { - let matchers = &self.settings.summary.terminal_action_links_matchers; + let matchers = &self.settings.summary().terminal_action_links_matchers; match_at_offset(line, byte_offset, matchers) })?; let actions = actions_for_match(&item); @@ -453,8 +453,8 @@ impl NyaTermApp { || view.output_burst_bytes > 0 || view.performance_mode == TerminalPerformanceMode::Overloaded; terminal_expensive_interactions_enabled( - self.settings.summary.terminal_action_links_enabled - && !self.settings.summary.terminal_low_latency_mode, + self.settings.summary().terminal_action_links_enabled + && !self.settings.summary().terminal_low_latency_mode, is_active, render_degraded, runtime_output_pressure, diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/helpers.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/helpers.rs index a69b6f1d7..8c3582e14 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/helpers.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/helpers.rs @@ -68,7 +68,7 @@ pub(in crate::features) fn terminal_bounds_tracker( if input_entity .read(cx) .settings - .summary + .summary() .interaction_mac_ime_compatibility { window.handle_input(&focus, ElementInputHandler::new(bounds, input_entity), cx); diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/metrics.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/metrics.rs index eefd0fdf5..3686517a5 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/metrics.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/metrics.rs @@ -25,7 +25,7 @@ impl NyaTermApp { } pub(in crate::features) fn fallback_terminal_cell_size(&self) -> (f32, f32) { - let font_size = self.settings.summary.terminal_font_size.max(8) as f32; + let font_size = self.settings.summary().terminal_font_size.max(8) as f32; // Prefer painted fixed 18px when font is near default; scale with font otherwise. let cell_h = if (font_size - 14.).abs() < 0.5 { 18. @@ -40,7 +40,7 @@ impl NyaTermApp { /// Refresh monospaced cell metrics from GPUI TextSystem for the configured terminal font. pub(in crate::features) fn refresh_terminal_cell_metrics(&mut self, cx: &App) { - let font_size = self.settings.summary.terminal_font_size.max(8) as f32; + let font_size = self.settings.summary().terminal_font_size.max(8) as f32; let family = self.gpui_terminal_font_family(); let text_system = cx.text_system(); let font_id = text_system.resolve_font(&gpui::font(SharedString::from(family))); @@ -68,9 +68,9 @@ impl NyaTermApp { } pub(in crate::features) fn terminal_content_insets(&self) -> TerminalViewportInsets { - if self.settings.summary.terminal_show_workspace_padding - && !self.settings.summary.terminal_show_line_numbers - && !self.settings.summary.terminal_show_timestamps + if self.settings.summary().terminal_show_workspace_padding + && !self.settings.summary().terminal_show_line_numbers + && !self.settings.summary().terminal_show_timestamps { // Tauri applies workspace padding as `pl-2`; it does not add a // default margin or vertical padding around the terminal grid. @@ -98,9 +98,9 @@ impl NyaTermApp { let snapshot = self.terminal_snapshot_for_session(session_id, display_offset); terminal_gutter_metrics( cell_w, - self.settings.summary.terminal_show_timestamps, - self.settings.summary.terminal_show_timestamp_milliseconds, - self.settings.summary.terminal_show_line_numbers, + self.settings.summary().terminal_show_timestamps, + self.settings.summary().terminal_show_timestamp_milliseconds, + self.settings.summary().terminal_show_line_numbers, terminal_line_number_digits(snapshot.as_ref()), ) .total_width() diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/selection.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/selection.rs index eb188787f..7cddf9123 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/selection.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_selection_runtime/selection.rs @@ -352,12 +352,12 @@ impl NyaTermApp { } else { selected.end }; - if self.settings.summary.interaction_copy_on_select { + if self.settings.summary().interaction_copy_on_select { let _ = self.copy_terminal_selection(cx); } let _ = self.move_smart_input_cursor(target, cx); self.clear_terminal_selection(cx); - } else if self.settings.summary.interaction_copy_on_select { + } else if self.settings.summary().interaction_copy_on_select { let _ = self.copy_terminal_selection(cx); } else if self.terminal.selection.selection.is_some() { // One shell notify for status after drag ends (not per mouse move). @@ -452,7 +452,7 @@ impl NyaTermApp { } let idx = cell.col.min(cells.len().saturating_sub(1)); // xterm wordSeparator semantics: characters listed are separators, not word body. - let separators = self.settings.summary.interaction_word_separators.as_str(); + let separators = self.settings.summary().interaction_word_separators.as_str(); let is_word = |cell: &TerminalTextCell| terminal_text_cell_is_word(cell, separators); if !is_word(&cells[idx]) { return (idx, idx.saturating_add(1)); diff --git a/crates/nyaterm-desktop/src/features/terminal/terminal_surface/canvas.rs b/crates/nyaterm-desktop/src/features/terminal/terminal_surface/canvas.rs index 1a0f244bf..223645549 100644 --- a/crates/nyaterm-desktop/src/features/terminal/terminal_surface/canvas.rs +++ b/crates/nyaterm-desktop/src/features/terminal/terminal_surface/canvas.rs @@ -50,9 +50,9 @@ impl NyaTermApp { && self .terminal_protocol_state_for_session(&session_id) .mouse_reporting; - let low_latency_mode = self.settings.summary.terminal_low_latency_mode; + let low_latency_mode = self.settings.summary().terminal_low_latency_mode; let action_links_enabled = - self.settings.summary.terminal_action_links_enabled && !low_latency_mode; + self.settings.summary().terminal_action_links_enabled && !low_latency_mode; let render_output_pressure = self.runtime_output_pressure_active(); let render_pressure = self .terminal @@ -98,7 +98,7 @@ impl NyaTermApp { ); let action_link_matcher_key = terminal_action_link_matcher_key( action_links_enabled, - &self.settings.summary.terminal_action_links_matchers, + &self.settings.summary().terminal_action_links_matchers, ); let keyword_rules = if session_id.is_empty() { if render_degraded || !is_active { @@ -132,7 +132,7 @@ impl NyaTermApp { // cursor placement, or when there is no session (empty bootstrap canvas). let needs_shell_viewport_snapshot = session_id.is_empty() || (is_active - && self.settings.summary.interaction_mac_ime_compatibility + && self.settings.summary().interaction_mac_ime_compatibility && !self.terminal.input.ime_marked_text.is_empty()); let snapshot = if needs_shell_viewport_snapshot { self.terminal_snapshot_for_session( @@ -150,16 +150,16 @@ impl NyaTermApp { let snapshot_rows = snapshot.row_count(); let snapshot_cols = snapshot.cols; let viewport_snapshot_duration = snapshot_stage_started_at.elapsed(); - let show_line_numbers = self.settings.summary.terminal_show_line_numbers; - let show_timestamps = self.settings.summary.terminal_show_timestamps; - let show_timestamp_ms = self.settings.summary.terminal_show_timestamp_milliseconds; + let show_line_numbers = self.settings.summary().terminal_show_line_numbers; + let show_timestamps = self.settings.summary().terminal_show_timestamps; + let show_timestamp_ms = self.settings.summary().terminal_show_timestamp_milliseconds; let gutter_enabled = show_line_numbers || show_timestamps; // Prefer remote cursor visibility/shape from the terminal model; settings // supply the default paint style when the model reports a block cursor. let remote_cursor_visible = snapshot.cursor.visible && snapshot.cursor.shape != nyaterm_terminal::CursorShape::Hidden && cursor_row != usize::MAX; - let blink_enabled = self.settings.summary.cursor_blink || snapshot.cursor.blinking; + let blink_enabled = self.settings.summary().cursor_blink || snapshot.cursor.blinking; let show_cursor = is_active && !session_id.is_empty() && !is_disconnected @@ -169,8 +169,8 @@ impl NyaTermApp { let cursor_style = match snapshot.cursor.shape { nyaterm_terminal::CursorShape::Underline => "underline".to_string(), nyaterm_terminal::CursorShape::Beam => "bar".to_string(), - nyaterm_terminal::CursorShape::Hidden => self.settings.summary.cursor_style.clone(), - nyaterm_terminal::CursorShape::Block => self.settings.summary.cursor_style.clone(), + nyaterm_terminal::CursorShape::Hidden => self.settings.summary().cursor_style.clone(), + nyaterm_terminal::CursorShape::Block => self.settings.summary().cursor_style.clone(), }; let (abs_start, abs_end) = terminal_snapshot_absolute_range(&snapshot); let _ = abs_end; @@ -333,7 +333,7 @@ impl NyaTermApp { let terminal_font_family = self.gpui_terminal_font_family(); let ime_preedit_text = (is_active && !session_id.is_empty() - && self.settings.summary.interaction_mac_ime_compatibility + && self.settings.summary().interaction_mac_ime_compatibility && !self.terminal.input.ime_marked_text.is_empty()) .then(|| self.terminal.input.ime_marked_text.clone()); let ime_preedit_position = ime_preedit_text.as_ref().map(|_| { @@ -409,7 +409,7 @@ impl NyaTermApp { .pr(px(8.)) .text_color(rgb(palette.text_dimmed)) .font_family(terminal_font_family.clone()) - .text_size(px(self.settings.summary.terminal_font_size as f32)) + .text_size(px(self.settings.summary().terminal_font_size as f32)) .when(show_timestamps, |this| { this.child(div().w(px(ts_w)).flex_none().child(ts_label)) }) @@ -464,9 +464,9 @@ impl NyaTermApp { cell_h, palette, terminal_font_family.clone(), - self.settings.summary.terminal_font_size as f32, - self.settings.summary.terminal_font_weight as f32, - self.settings.summary.terminal_font_weight_bold as f32, + self.settings.summary().terminal_font_size as f32, + self.settings.summary().terminal_font_weight as f32, + self.settings.summary().terminal_font_weight_bold as f32, ); if let Some(cache) = layout_cache { grid = grid.with_layout_cache(cache); @@ -487,9 +487,9 @@ impl NyaTermApp { cell_h, palette, terminal_font_family.clone(), - self.settings.summary.terminal_font_size as f32, - self.settings.summary.terminal_font_weight as f32, - self.settings.summary.terminal_font_weight_bold as f32, + self.settings.summary().terminal_font_size as f32, + self.settings.summary().terminal_font_weight as f32, + self.settings.summary().terminal_font_weight_bold as f32, ); if let Some(cache) = layout_cache { grid = grid.with_layout_cache(cache); @@ -507,7 +507,7 @@ impl NyaTermApp { .unwrap_or(palette.link); let sync_status_label = if sync_is_paused { "Paused" } else { "Syncing" }; let output_session_id = session_id.clone(); - let terminal_font_size = self.settings.summary.terminal_font_size as f32; + let terminal_font_size = self.settings.summary().terminal_font_size as f32; let performance_overlay = self .terminal .view @@ -581,7 +581,7 @@ impl NyaTermApp { .font_family(terminal_font_family.clone()) .text_size(px(terminal_font_size)) .font_weight(FontWeight( - self.settings.summary.terminal_font_weight as f32, + self.settings.summary().terminal_font_weight as f32, )) .text_color(rgb(palette.terminal_fg)) .child( @@ -855,7 +855,7 @@ impl NyaTermApp { this.close_action_link_menu(cx); let mods = event.modifiers; let skip_selection = - this.settings.summary.terminal_action_links_enabled + this.settings.summary().terminal_action_links_enabled && (mods.alt || mods.control || mods.platform); if !skip_selection { this.start_terminal_selection_for_session( @@ -893,7 +893,7 @@ impl NyaTermApp { return; } } - if this.settings.summary.interaction_right_click_paste { + if this.settings.summary().interaction_right_click_paste { this.paste_from_clipboard(window, cx); this.clear_terminal_selection(cx); } else { @@ -947,7 +947,7 @@ impl NyaTermApp { } window.focus(&this.terminal.input.focus); let modifiers = event.modifiers(); - if this.settings.summary.terminal_action_links_enabled { + if this.settings.summary().terminal_action_links_enabled { if modifiers.alt { if this.try_open_action_link_menu_at_click(event, cx) { cx.stop_propagation(); @@ -1032,7 +1032,7 @@ impl NyaTermApp { .text_color(rgb(palette.terminal_fg)) .font_family(terminal_font_family.clone()) .text_size(px( - self.settings.summary.terminal_font_size as f32 + self.settings.summary().terminal_font_size as f32, )) .child(marked_text), ) @@ -1260,7 +1260,7 @@ impl NyaTermApp { expensive_interactions_enabled, output_burst_bytes, ?performance_mode, - action_links_enabled = self.settings.summary.terminal_action_links_enabled, + action_links_enabled = self.settings.summary().terminal_action_links_enabled, search_open = self.terminal.search.open, search_matches = search_matches_len, render_cache_hits, diff --git a/crates/nyaterm-desktop/src/features/transfer_external_sync_window.rs b/crates/nyaterm-desktop/src/features/transfer_external_sync_window.rs index b64a76411..0a7112114 100644 --- a/crates/nyaterm-desktop/src/features/transfer_external_sync_window.rs +++ b/crates/nyaterm-desktop/src/features/transfer_external_sync_window.rs @@ -44,7 +44,7 @@ impl Render for TransferExternalSyncWindow { ( app.theme_palette(), app.gpui_ui_font_family(), - app.settings.summary.ui_font_size.clamp(12, 24) as f32, + app.settings.summary().ui_font_size.clamp(12, 24) as f32, app.tr("fileExplorer.fileModified").to_string(), ) }); diff --git a/crates/nyaterm-desktop/src/features/transfers/transfer_events.rs b/crates/nyaterm-desktop/src/features/transfers/transfer_events.rs index 8cfff8164..19d7818ce 100644 --- a/crates/nyaterm-desktop/src/features/transfers/transfer_events.rs +++ b/crates/nyaterm-desktop/src/features/transfers/transfer_events.rs @@ -260,7 +260,7 @@ impl NyaTermApp { entry.file_type == SftpFileType::Directory && entry.name != "." && entry.name != ".." - && (self.settings.summary.ui_file_explorer_show_hidden_files + && (self.settings.summary().ui_file_explorer_show_hidden_files || !entry.name.starts_with('.')) }); entries.sort_by(|left, right| { diff --git a/crates/nyaterm-desktop/src/features/transfers/transfer_options.rs b/crates/nyaterm-desktop/src/features/transfers/transfer_options.rs index a0ecad356..78defd64b 100644 --- a/crates/nyaterm-desktop/src/features/transfers/transfer_options.rs +++ b/crates/nyaterm-desktop/src/features/transfers/transfer_options.rs @@ -5,10 +5,12 @@ use crate::features::NyaTermApp; impl NyaTermApp { pub(in crate::features) fn sftp_transfer_options(&self) -> SftpTransferOptions { SftpTransferOptions::default() - .with_buffer_size_bytes(self.settings.summary.transfer_buffer_size as usize * 1024) - .with_max_retries(self.settings.summary.transfer_max_retries) - .with_preserve_timestamps(self.settings.summary.transfer_preserve_timestamps) - .with_default_file_permissions(&self.settings.summary.transfer_default_file_permissions) - .with_resume_broken_transfer(self.settings.summary.transfer_resume_broken_transfer) + .with_buffer_size_bytes(self.settings.summary().transfer_buffer_size as usize * 1024) + .with_max_retries(self.settings.summary().transfer_max_retries) + .with_preserve_timestamps(self.settings.summary().transfer_preserve_timestamps) + .with_default_file_permissions( + &self.settings.summary().transfer_default_file_permissions, + ) + .with_resume_broken_transfer(self.settings.summary().transfer_resume_broken_transfer) } } diff --git a/crates/nyaterm-desktop/src/features/transfers/transfer_paths.rs b/crates/nyaterm-desktop/src/features/transfers/transfer_paths.rs index 3d65b9afb..ba5535cb2 100644 --- a/crates/nyaterm-desktop/src/features/transfers/transfer_paths.rs +++ b/crates/nyaterm-desktop/src/features/transfers/transfer_paths.rs @@ -36,7 +36,8 @@ impl NyaTermApp { }; let _ = this.update(cx, |this, cx| { if let Some(path) = path { - this.settings.summary.transfer_download_path = path.display().to_string(); + this.settings + .set_transfer_download_path(path.display().to_string()); this.save_transfer_settings("transfer download path saved", cx); } else { this.terminal.view.status = "download path selection cancelled".to_string(); @@ -64,7 +65,7 @@ impl NyaTermApp { }; let _ = this.update(cx, |this, cx| { if let Some(path) = path { - this.settings.summary.recording_path = path.display().to_string(); + this.settings.set_recording_path(path.display().to_string()); this.save_recording_settings(cx); } else { this.terminal.view.status = "recording path selection cancelled".to_string(); @@ -95,7 +96,8 @@ impl NyaTermApp { }; let _ = this.update(cx, |this, cx| { if let Some(path) = path { - this.settings.summary.transfer_default_editor = path.display().to_string(); + this.settings + .set_transfer_default_editor(path.display().to_string()); this.save_transfer_settings("transfer editor path saved", cx); } else { this.terminal.view.status = "editor path selection cancelled".to_string(); @@ -108,7 +110,7 @@ impl NyaTermApp { } pub(in crate::features) fn resolved_transfer_download_dir(&self) -> Option { - let configured = self.settings.summary.transfer_download_path.trim(); + let configured = self.settings.summary().transfer_download_path.trim(); if configured.is_empty() { return default_transfer_download_dir(); } @@ -149,7 +151,7 @@ impl NyaTermApp { if value.is_empty() { let file_name = download_file_name_from_remote_path(&self.transfer.normalized_remote_path()); - let download_path = self.settings.summary.transfer_download_path.trim(); + let download_path = self.settings.summary().transfer_download_path.trim(); if download_path.is_empty() { PathBuf::from(file_name) } else { diff --git a/docs/architecture/gpui-migration-status.md b/docs/architecture/gpui-migration-status.md index 6a2b40d16..d12a043f5 100644 --- a/docs/architecture/gpui-migration-status.md +++ b/docs/architecture/gpui-migration-status.md @@ -236,8 +236,13 @@ these as staged extraction candidates, not as formatting-only refactor targets. and backing child settings-module-private. Cross-domain persistence adapters now update its message/readiness through owner methods, rendering receives a borrowed immutable view, and store reopen replaces path/message/readiness as - one transition. Filesystem prompts, persistence and GPUI notification stay - in their existing adapters. + one transition. The next compatibility-catalog batch made + `AppSettingsSummary`, `KeywordHighlightConfig` and staged master-password + state settings-module-private as well. Cross-domain readers now use borrowed + immutable access, appearance/keyword/browser preferences enter through + semantic transitions, and the shell persists its complete UI layout through + one typed owner update instead of piecemeal field writes. Filesystem prompts, + persistence and GPUI notification stay in their existing adapters. - Translation and native-update background state now have authoritative `TranslationFeatureState` and `UpdateFeatureState` owners. Eighteen app fields became two feature fields; each owner constructs and retains its own job @@ -1491,6 +1496,7 @@ Current ownership map: | Command history and persistence worker | Private state in `NyaTermApp.commands` | Persisted catalog plus background runtime | History snapshots, queue admission, event polling and idle checks enter through `CommandFeatureState`; failed optimistic use-count updates roll back on the owner. | | Send-command composer/options/progress | Private children in `NyaTermApp.send_command` | Transient editor and send lifecycle | Views receive immutable presentation data; control edits, mutually-exclusive menus, data/mode defaults, progress counters and cancellation enter through `SendCommandFeatureState`. Session selection, terminal writes, GPUI/text-input routing and status remain in adapters. | | Settings interaction and prompts | Private children in `NyaTermApp.settings` | Transient settings UI and prompt lifecycle | Search-engine rows, keyword-highlight editing, appearance menus, keybinding recording/search and config/diagnostics/import/password prompt admission enter through `SettingsFeatureState`; views use immutable presentation values and read-only focus/font access. Persistence, native filesystem prompts, text inputs and GPUI notification remain in adapters. | +| Application settings and keyword catalogs | Settings-module-private children in `NyaTermApp.settings` | Compatibility-sensitive persisted configuration plus staged master-password input | Cross-domain consumers borrow immutable summaries/configs and use semantic owner transitions. Shell UI layout is replaced through one typed update; master-password presentation is a non-`Debug` borrowed view. Serialization, encryption and persistence remain in `nyaterm-core` and existing adapters. | | Global storage status | Settings-module-private child in `NyaTermApp.settings` | Runtime persistence health/presentation state | Cross-domain persistence adapters update message/readiness through `SettingsFeatureState`; rendering receives a borrowed immutable view, while store reopen replaces path/message/readiness together. Database work and compatibility handling remain in existing adapters and `nyaterm-core`. | | AI settings/chat/history/discovery/agent/panel | AI-module-private children in `NyaTermApp.ai` | Persisted settings plus transient UI and background lifecycle | Desktop consumers use read-only slices/queries and semantic transitions; settings draft groups, menu exclusion, confirmations, request/focus preparation, detected-error throttling, picker clamping and Agent capture/reset enter through `AiFeatureState`. Persistence, terminal-context collection, GPUI focus/rendering and notification remain in adapters. | | Shell viewport/navigation/panels/chrome/workspace | Shell-module-private children in `NyaTermApp.shell` | Transient GPUI composition and interaction state | Other desktop modules use read-only geometry/navigation/pane queries and semantic transitions; menu exclusion, settings-window lifecycle, mobile panels, failure chrome, submenu paths and pane ownership update through `ShellFeatureState`. Persistence, rendering, GPUI windows/notification and terminal coordination remain in adapters. | @@ -1755,6 +1761,12 @@ honest remaining list. implementation type settings-module-private, moved cross-domain status writes behind owner transitions, and gave rendering a borrowed immutable view without changing storage execution or compatibility formats. + The following settings compatibility-catalog batch completed child + encapsulation for `SettingsFeatureState`: summary, keyword-highlight config + and staged master-password state are now settings-module-private, all + cross-domain reads are immutable, keyword/appearance/browser mutations use + owner transitions, and UI layout persistence applies one typed atomic + update. No mutable-reference accessor was introduced. What remains at the composition root is stores, runtime and focused feature owners. Group by cohesion where a cluster exists; do not force the count down for diff --git a/scripts/check-architecture-boundaries.sh b/scripts/check-architecture-boundaries.sh index a5a06d185..571603742 100755 --- a/scripts/check-architecture-boundaries.sh +++ b/scripts/check-architecture-boundaries.sh @@ -268,6 +268,27 @@ check_no_multiline_matches_excluding \ '(self|this|app)\.settings[[:space:]]*\.[[:space:]]*store_status[[:space:]]*(\.|=)' \ crates/nyaterm-desktop/src/features \ 'crates/nyaterm-desktop/src/features/settings/**' +check_no_multiline_matches \ + "SettingsFeatureState compatibility children must stay inside the settings module" \ + 'struct[[:space:]]+SettingsFeatureState[[:space:]]*\{[^}]*pub([[:space:]]|\(crate\)|\(in crate\)|\(in crate::features\))[[:space:]]+(summary|keyword_config|master_password)[[:space:]]*:' \ + crates/nyaterm-desktop/src/features/settings/state.rs +check_no_matches \ + "SettingsMasterPasswordState implementation must stay inside the settings module" \ + 'pub([[:space:]]|\(crate\)|\(in crate\)|\(in crate::features\))[[:space:]]+struct[[:space:]]+SettingsMasterPasswordState' \ + crates/nyaterm-desktop/src/features/settings/catalog.rs +check_no_matches \ + "settings compatibility implementation types must not be re-exported" \ + 'pub([^;]*)use([^;]*)(SettingsMasterPasswordState|StoreStatus)' \ + crates/nyaterm-desktop/src/features/settings/mod.rs +check_no_multiline_matches_excluding \ + "settings compatibility children must use SettingsFeatureState methods outside settings" \ + '(self|this|app)\.settings[[:space:]]*\.[[:space:]]*(summary|keyword_config|master_password)[[:space:]]*(\.|=|\[)' \ + crates/nyaterm-desktop/src/features \ + 'crates/nyaterm-desktop/src/features/settings/**' +check_no_matches \ + "settings compatibility state must not expose mutable-reference accessors" \ + 'fn[[:space:]]+(summary|keyword_config|master_password)(_mut|_mutable)|->[[:space:]]*&mut[[:space:]]+(AppSettingsSummary|KeywordHighlightConfig|SettingsMasterPasswordState)' \ + crates/nyaterm-desktop/src/features/settings check_no_matches \ "translation state must stay grouped under TranslationFeatureState" \