diff --git a/nebula_app/src/gpui_shell/config.rs b/nebula_app/src/gpui_shell/config.rs index 7f953c5..0ad3336 100644 --- a/nebula_app/src/gpui_shell/config.rs +++ b/nebula_app/src/gpui_shell/config.rs @@ -42,6 +42,7 @@ pub(crate) const fn effective_cursor_blink(configured: Option) -> bool { pub struct Settings { /// 已解析的界面语言。GPUI 组件只读这个内存全局,渲染路径不得重复读盘。 pub ui_language: UiLanguage, + pub panel_resize: bool, pub font_family: String, pub font_cjk: Option<[gpui::Font; 4]>, pub font_bold_family: String, @@ -117,6 +118,10 @@ pub(crate) fn ui_language(cx: &App) -> UiLanguage { cx.try_global::().map(|settings| settings.ui_language).unwrap_or(UiLanguage::EnUs) } +pub(crate) fn panel_resize(cx: &App) -> bool { + cx.try_global::().is_some_and(|settings| settings.panel_resize) +} + pub(crate) fn ai_toasts_enabled(cx: &App) -> bool { cx.try_global::().is_none_or(|settings| settings.ai_toasts) } @@ -248,6 +253,7 @@ impl Settings { Settings { ui_language, + panel_resize: runtime.panel_resize, font_bold_family: secondary(&raw.font.bold), font_italic_family: secondary(&raw.font.italic), font_bold_italic_family: secondary(&raw.font.bold_italic), @@ -792,6 +798,25 @@ mod tests { assert_eq!(resolve_ui_language(LanguagePref::EnUs), UiLanguage::EnUs); } + #[cfg(feature = "gpui-test-support")] + #[gpui::test] + fn render_preferences_read_current_settings(cx: &mut gpui::TestAppContext) { + cx.update(|cx| { + for (theme, language, panel_resize) in [ + (ThemeName::Nord, UiLanguage::ZhCn, false), + (ThemeName::Paper, UiLanguage::EnUs, true), + ] { + let mut runtime = RuntimeSettings::from_raw(&RawSettings::default()); + runtime.panel_resize = panel_resize; + let mut settings = Settings::load_with_runtime(theme, runtime); + settings.ui_language = language; + cx.set_global(settings); + assert_eq!(super::ui_language(cx), language); + assert_eq!(super::panel_resize(cx), panel_resize); + } + }); + } + #[test] fn system_theme_owns_terminal_background_while_following_system() { let custom = Some([0x0f, 0x11, 0x1a]); diff --git a/nebula_app/src/gpui_shell/terminal/ssh_connect_overlay.rs b/nebula_app/src/gpui_shell/terminal/ssh_connect_overlay.rs index 7d91955..74fb677 100644 --- a/nebula_app/src/gpui_shell/terminal/ssh_connect_overlay.rs +++ b/nebula_app/src/gpui_shell/terminal/ssh_connect_overlay.rs @@ -72,12 +72,6 @@ mod connection_state_tests { } } -/// UiLanguage 由设置折算(两壳同源)。 -pub(super) fn language() -> crate::display::UiLanguage { - let runtime = nebula_settings::RuntimeSettings::load(); - crate::display::LanguagePreference::from(runtime.language).resolved() -} - fn hsla_from_rgba(r: u8, g: u8, b: u8) -> Hsla { let (rf, gf, bf) = (f32::from(r) / 255.0, f32::from(g) / 255.0, f32::from(b) / 255.0); let max = rf.max(gf).max(bf); @@ -288,7 +282,7 @@ pub(super) fn overlay( use gpui::prelude::FluentBuilder as _; let theme = cx.theme(); - let lang = language(); + let lang = crate::gpui_shell::config::ui_language(cx); // chrome 文本锚定配置字号(旧壳 ui_font 合同),不跟终端缩放。 let ui_px = cx .try_global::() diff --git a/nebula_app/src/gpui_shell/workspace/file_tree.rs b/nebula_app/src/gpui_shell/workspace/file_tree.rs index 4528f46..bb47378 100644 --- a/nebula_app/src/gpui_shell/workspace/file_tree.rs +++ b/nebula_app/src/gpui_shell/workspace/file_tree.rs @@ -316,7 +316,7 @@ impl NebulaWorkspace { let editing_path = self.file_tree_path.is_some(); let theme = cx.theme(); let muted = theme.muted_foreground; - let language = super::workspace_ui_language(); + let language = crate::gpui_shell::config::ui_language(cx); let search_options = self.side_panel.file_search_options(); let search_active = !self.side_panel.search.trim().is_empty(); let search_pending = self.side_panel.file_search_pending(); @@ -428,7 +428,7 @@ impl NebulaWorkspace { }); let root_dir = self.side_panel.root().map(std::path::Path::to_path_buf); let row_count = self.side_panel.file_rows().len(); - let empty = self.file_tree_empty_state(); + let empty = self.file_tree_empty_state(cx); let scroll_handle = self.file_tree_scroll.clone(); v_flex() @@ -611,13 +611,13 @@ impl NebulaWorkspace { /// side_panel.rs:3112-3131 同源。判据也照旧壳:`..` 不算内容,只剩它时这个 /// 目录仍然是空的;"读不到"和"确实是空的"必须分开说,否则用户没法判断该 /// 重试还是该换目录。 - fn file_tree_empty_state(&self) -> Option { + fn file_tree_empty_state(&self, cx: &gpui::App) -> Option { if self.side_panel.file_rows().iter().any(|row| !row.is_parent) { return None; } if !self.side_panel.search.trim().is_empty() { use crate::i18n::Message; - let language = super::workspace_ui_language(); + let language = crate::gpui_shell::config::ui_language(cx); return Some(if let Some(error) = self.side_panel.file_search_error() { crate::ux::EmptyState::new( language.text(Message::FilesSearchFailed), diff --git a/nebula_app/src/gpui_shell/workspace/file_tree/path_bar.rs b/nebula_app/src/gpui_shell/workspace/file_tree/path_bar.rs index 149ab4a..7de022a 100644 --- a/nebula_app/src/gpui_shell/workspace/file_tree/path_bar.rs +++ b/nebula_app/src/gpui_shell/workspace/file_tree/path_bar.rs @@ -210,7 +210,7 @@ impl NebulaWorkspace { if self.file_tree_path.as_ref().is_some_and(|edit| edit.origin != origin) { self.file_tree_path = None; } - let language = super::super::workspace_ui_language(); + let language = crate::gpui_shell::config::ui_language(cx); let Some(edit) = self.file_tree_path.as_ref() else { let value = origin .wsl diff --git a/nebula_app/src/gpui_shell/workspace/palette.rs b/nebula_app/src/gpui_shell/workspace/palette.rs index 63b49c3..15eea49 100644 --- a/nebula_app/src/gpui_shell/workspace/palette.rs +++ b/nebula_app/src/gpui_shell/workspace/palette.rs @@ -68,7 +68,7 @@ impl NebulaWorkspace { .get(self.active) .and_then(WorkspaceTab::focused_view) .is_some_and(|view| view.read(cx).working_directory().is_some()); - let language = workspace_ui_language(); + let language = crate::gpui_shell::config::ui_language(cx); let rows = self.palette_override.clone().unwrap_or_else(|| { let mut rows: Vec = crate::display::command_palette::catalog() .iter() @@ -176,7 +176,7 @@ impl NebulaWorkspace { let muted = theme.muted_foreground; let overlay = theme.overlay; let mono_family = theme.mono_font_family.clone(); - let language = workspace_ui_language(); + let language = crate::gpui_shell::config::ui_language(cx); let palette_filters = if self.shell_picker_open { Some(( WorkspacePaletteFilter::Launcher(self.launcher_filter), diff --git a/nebula_app/src/gpui_shell/workspace/remote_files.rs b/nebula_app/src/gpui_shell/workspace/remote_files.rs index eb07878..4502ce0 100644 --- a/nebula_app/src/gpui_shell/workspace/remote_files.rs +++ b/nebula_app/src/gpui_shell/workspace/remote_files.rs @@ -458,6 +458,7 @@ impl NebulaWorkspace { let muted = theme.muted_foreground; let foreground = theme.foreground; let drop_highlight = theme.accent.opacity(0.18); + let language = crate::gpui_shell::config::ui_language(cx); let rows = self.remote_rows(); let row_count = rows.len(); let destination = self.remote_browser.destination.clone(); @@ -467,7 +468,7 @@ impl NebulaWorkspace { self.remote_browser.path.clone() }; let at_root = self.remote_browser.path == "/" || self.remote_browser.path.is_empty(); - let notice = self.remote_notice(); + let notice = self.remote_notice(language); v_flex() .h_full() @@ -488,7 +489,7 @@ impl NebulaWorkspace { this.drop_upload_paths(vec![file.local_path.clone()], None, window, cx); })) .child(div().px(px(TEXT_INSET)).text_xs().text_color(muted) - .child(workspace_ui_language().text(if crate::platform::file_drag::supported() { + .child(language.text(if crate::platform::file_drag::supported() { crate::i18n::Message::TransferDragHint } else { crate::i18n::Message::TransferUploadHint }))) // 主机名单独一行:远端浏览器最危险的误操作是"以为在另一台机器上", @@ -849,14 +850,12 @@ impl NebulaWorkspace { /// /// 三种情况必须分开说。"读不到"和"是空的"混为一谈,用户就不知道该重试 /// 还是该换目录——这是空态里最常见也最误导人的一处偷懒。 - fn remote_notice(&self) -> Option { + fn remote_notice(&self, language: crate::display::UiLanguage) -> Option { if let Some(error) = self.remote_browser.error.as_deref() { return Some(format!("{error}(点右上角重新读取)")); } if self.remote_browser.preflighting { - return Some( - workspace_ui_language().text(crate::i18n::Message::TransferChecking).to_owned(), - ); + return Some(language.text(crate::i18n::Message::TransferChecking).to_owned()); } if let Some(snapshot) = self.remote_transfer_snapshot() && snapshot.phase == SftpPhase::Working @@ -868,7 +867,7 @@ impl NebulaWorkspace { return Some("正在读取远端目录…".to_owned()); } if let Some(outcome) = self.remote_browser.last_outcome { - return Some(workspace_ui_language().text(outcome).to_owned()); + return Some(language.text(outcome).to_owned()); } self.remote_browser.entries.is_empty().then(|| "此目录为空。".to_owned()) } diff --git a/nebula_app/src/gpui_shell/workspace/settings_navigation.rs b/nebula_app/src/gpui_shell/workspace/settings_navigation.rs index 4761f45..6a56b6e 100644 --- a/nebula_app/src/gpui_shell/workspace/settings_navigation.rs +++ b/nebula_app/src/gpui_shell/workspace/settings_navigation.rs @@ -122,7 +122,9 @@ impl NebulaWorkspace { return self.tab_presentation(index, cx, dark); } TabPresentation { - title: super::workspace_ui_language().text(crate::i18n::Message::CommonSettings).into(), + title: crate::gpui_shell::config::ui_language(cx) + .text(crate::i18n::Message::CommonSettings) + .into(), tooltip: None, is_settings: true, activity: SidebarActivity::Idle, diff --git a/nebula_app/src/gpui_shell/workspace/sidebar_resize.rs b/nebula_app/src/gpui_shell/workspace/sidebar_resize.rs index 852a6a8..7e9ffeb 100644 --- a/nebula_app/src/gpui_shell/workspace/sidebar_resize.rs +++ b/nebula_app/src/gpui_shell/workspace/sidebar_resize.rs @@ -61,7 +61,7 @@ impl NebulaWorkspace { || self.settings_open || self.reader_focus_active(cx) || self.tabs_position == nebula_settings::TabsPositionName::Top - || !nebula_settings::RuntimeSettings::load().panel_resize + || !crate::gpui_shell::config::panel_resize(cx) { return None; } diff --git a/nebula_app/src/gpui_shell/workspace/update_dialog.rs b/nebula_app/src/gpui_shell/workspace/update_dialog.rs index 5b6a9e6..9795203 100644 --- a/nebula_app/src/gpui_shell/workspace/update_dialog.rs +++ b/nebula_app/src/gpui_shell/workspace/update_dialog.rs @@ -195,7 +195,7 @@ pub(crate) fn open_update_dialog( let dialog_result = result.clone(); window.open_dialog(cx, move |dialog, window, cx| { - let language = workspace_ui_language(); + let language = crate::gpui_shell::config::ui_language(cx); let title: SharedString = language.pick("Pebrel 更新", "Pebrel Update").into(); let current_label: SharedString = language.pick("当前版本", "Current").into(); let latest_label: SharedString = language.pick("最新版本", "Latest").into();