From ae66b69e7818f3d11f71d11fb9800013906ba53f Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 16:52:06 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=8D=95?= =?UTF-8?q?=E7=8B=AC=E8=AE=BE=E7=BD=AE=E4=B8=AD=E6=96=87=E7=BB=88=E7=AB=AF?= =?UTF-8?q?=E5=AD=97=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nebula_app/i18n/en-US.json | 3 +++ nebula_app/i18n/zh-CN.json | 3 +++ nebula_app/src/gpui_shell/config.rs | 15 ++++++++++++ nebula_app/src/gpui_shell/settings_pane.rs | 1 + .../settings_pane/appearance_advanced.rs | 10 +++++++- .../gpui_shell/settings_pane/font_picker.rs | 4 ++-- .../settings_pane/initialization.rs | 23 +++++++++++++++++++ nebula_app/src/gpui_shell/terminal/element.rs | 6 +++++ nebula_settings/src/lib.rs | 5 ++++ nebula_settings/src/reset.rs | 1 + 10 files changed, 68 insertions(+), 3 deletions(-) diff --git a/nebula_app/i18n/en-US.json b/nebula_app/i18n/en-US.json index 40365c5..4674cfd 100644 --- a/nebula_app/i18n/en-US.json +++ b/nebula_app/i18n/en-US.json @@ -77,6 +77,9 @@ "background_cover_description": "The image will extend beneath the title bar, window buttons, tabs, and SSH sidebar. Low-contrast images may make controls harder to see; the interface retains a minimum-opacity safeguard." }, "font": { + "english": "English terminal font", + "chinese": "Chinese terminal font", + "chinese_description": "Used for Chinese and other full-width characters. Enter a font family or comma-separated families; leave empty to use the English font settings. Applies on Enter or leaving the field.", "fallback_description": "The first family is preferred; missing glyphs fall back through the remaining families, with bundled Maple as the final safe fallback.", "group_description": "The first family is primary; the rest are used in fallback order.", "loading": "Enumerating system fonts...", diff --git a/nebula_app/i18n/zh-CN.json b/nebula_app/i18n/zh-CN.json index 09d7b8a..35fd88e 100644 --- a/nebula_app/i18n/zh-CN.json +++ b/nebula_app/i18n/zh-CN.json @@ -77,6 +77,9 @@ "background_cover_description": "背景图会延伸到标题栏、窗口按钮、Tab 与 SSH 侧栏下方,低对比度图片可能影响操作可见性;界面仍会保留最低不透明度保护。" }, "font": { + "english": "英文终端字体", + "chinese": "中文终端字体", + "chinese_description": "用于中文等全宽字符。输入字体名称,多个字体以逗号分隔;留空时使用英文字体设置。按回车或离开输入框后生效。", "fallback_description": "第 1 项优先显示;缺失字形依次使用后续字体,内置 Maple 是最终安全兜底。", "group_description": "第 1 项是主字体,其余按顺序回退", "loading": "正在枚举系统字体…", diff --git a/nebula_app/src/gpui_shell/config.rs b/nebula_app/src/gpui_shell/config.rs index e87af53..26c4354 100644 --- a/nebula_app/src/gpui_shell/config.rs +++ b/nebula_app/src/gpui_shell/config.rs @@ -42,6 +42,7 @@ pub struct Settings { /// 已解析的界面语言。GPUI 组件只读这个内存全局,渲染路径不得重复读盘。 pub ui_language: UiLanguage, pub font_family: String, + pub font_cjk: Option<[gpui::Font; 4]>, pub font_bold_family: String, pub font_italic_family: String, pub font_bold_italic_family: String, @@ -193,6 +194,20 @@ impl Settings { cjk_bold_regular: runtime.cjk_bold_regular, shell_id: runtime.shell.clone(), font_family: normal_family, + font_cjk: runtime.font_family_cjk.as_deref().map(|family| { + use gpui::{FontStyle, FontWeight}; + [ + (FontWeight::NORMAL, FontStyle::Normal), + (FontWeight::BOLD, FontStyle::Normal), + (FontWeight::NORMAL, FontStyle::Italic), + (FontWeight::BOLD, FontStyle::Italic), + ] + .map(|(weight, style)| gpui::Font { + weight, + style, + ..crate::font_install::gpui_font_with_fallbacks(family) + }) + }), load_notice, // 这里是唯一能正确合并「主题自带几何」与「用户显式覆盖」的地方: // `theme` 已是 follow_system 折算后的**生效**主题,runtime 是同一次 diff --git a/nebula_app/src/gpui_shell/settings_pane.rs b/nebula_app/src/gpui_shell/settings_pane.rs index 5d99305..550cca3 100644 --- a/nebula_app/src/gpui_shell/settings_pane.rs +++ b/nebula_app/src/gpui_shell/settings_pane.rs @@ -175,6 +175,7 @@ pub struct SettingsPane { /// GPUI text system 已注册的导入族名(启动扫描 + 本次导入累计)。 font_imported: Vec, font_family_input: Entity, + font_family_cjk_input: Entity, /// 字体输入框上一帧的窗口坐标。字体目录是宽弹层,不能把整条设置行当 /// 锚点;否则输入框在右侧、菜单却会从正文左缘展开。 font_picker_trigger_bounds: Option>, diff --git a/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs b/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs index 741eb29..bd0a867 100644 --- a/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs +++ b/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs @@ -96,11 +96,19 @@ impl SettingsPane { let terminal = self .group(language.pick("终端外观", "Terminal appearance"), cx) .child(self.row( - language.pick("终端字体", "Terminal font"), + language.text(crate::i18n::Message::SettingsFontEnglish), help("font_family", language), font_picker, cx, )) + .child(self.row( + language.text(crate::i18n::Message::SettingsFontChinese), + language.text(crate::i18n::Message::SettingsFontChineseDescription), + Input::new(&self.font_family_cjk_input) + .w(px(SETTINGS_SELECT_WIDTH)) + .aria_label(language.text(crate::i18n::Message::SettingsFontChinese)), + cx, + )) .child(self.terminal_font_size_row(cx)) .child(self.select_row( "cell_width_mode", diff --git a/nebula_app/src/gpui_shell/settings_pane/font_picker.rs b/nebula_app/src/gpui_shell/settings_pane/font_picker.rs index 2a9b42c..ea23ee5 100644 --- a/nebula_app/src/gpui_shell/settings_pane/font_picker.rs +++ b/nebula_app/src/gpui_shell/settings_pane/font_picker.rs @@ -219,7 +219,7 @@ impl SettingsPane { cx.stop_propagation(); cx.notify(); }))) - .aria_label(language.pick("终端字体", "Terminal font")), + .aria_label(language.text(crate::i18n::Message::SettingsFontEnglish)), ) // 弹层仍须取输入框的真实窗口坐标,才能正确处理滚动、缩放与 DPI。 .child( @@ -246,7 +246,7 @@ impl SettingsPane { return control.into_any_element(); } self.row( - language.pick("终端字体", "Terminal font"), + language.text(crate::i18n::Message::SettingsFontEnglish), help("font_family", language), control, cx, diff --git a/nebula_app/src/gpui_shell/settings_pane/initialization.rs b/nebula_app/src/gpui_shell/settings_pane/initialization.rs index fac590c..e3792f9 100644 --- a/nebula_app/src/gpui_shell/settings_pane/initialization.rs +++ b/nebula_app/src/gpui_shell/settings_pane/initialization.rs @@ -439,6 +439,28 @@ impl SettingsPane { }, )); + let font_family_cjk_input = cx.new(|cx| { + InputState::new(window, cx) + .default_value(runtime.font_family_cjk.clone().unwrap_or_default()) + }); + subscriptions.push(cx.subscribe_in( + &font_family_cjk_input, + window, + |this: &mut Self, _, event: &InputEvent, window, cx| { + if matches!(event, InputEvent::PressEnter { .. } | InputEvent::Blur) { + let value = crate::font_install::normalize_font_family_chain( + &this.font_family_cjk_input.read(cx).value(), + ); + this.font_family_cjk_input.update(cx, |input, cx| { + input.set_value(value.clone(), window, cx); + }); + if this.runtime.font_family_cjk.as_deref().unwrap_or_default() != value { + this.persist(&[("font_family_cjk", value)], cx); + } + } + }, + )); + let bg_picker_hsv = { let term = crate::gpui_shell::theme::chrome_theme_resolved(cx).palette().term_bg; let rgb = runtime.background.unwrap_or([term.r, term.g, term.b]); @@ -548,6 +570,7 @@ impl SettingsPane { font_system: None, font_imported: Vec::new(), font_family_input, + font_family_cjk_input, font_picker_trigger_bounds: None, backup_selection: crate::encrypted_backup::BackupSelection::default(), backup_pass_input: cx.new(|cx| { diff --git a/nebula_app/src/gpui_shell/terminal/element.rs b/nebula_app/src/gpui_shell/terminal/element.rs index 1955790..094e7a0 100644 --- a/nebula_app/src/gpui_shell/terminal/element.rs +++ b/nebula_app/src/gpui_shell/terminal/element.rs @@ -497,8 +497,14 @@ impl Element for TerminalElement { .try_global::() .map(|settings| settings.cjk_bold_regular) .unwrap_or(true); + let cjk_fonts = cx + .try_global::() + .and_then(|settings| settings.font_cjk.clone()); let pick_font = |bold: bool, italic: bool, wide: bool| { let bold = bold && !(wide && cjk_bold_regular); + if let Some(fonts) = cjk_fonts.as_ref().filter(|_| wide) { + return fonts[usize::from(bold) + 2 * usize::from(italic)].clone(); + } match (bold, italic) { (false, false) => font.clone(), (true, false) => bold_font.clone(), diff --git a/nebula_settings/src/lib.rs b/nebula_settings/src/lib.rs index e097f80..692f61f 100644 --- a/nebula_settings/src/lib.rs +++ b/nebula_settings/src/lib.rs @@ -941,6 +941,7 @@ pub struct RuntimeSettings { /// 系统外观变化时自动在主题家族的深浅成员间切换(默认关,尊重显式选择)。 pub follow_system_theme: bool, pub font_family: Option, + pub font_family_cjk: Option, /// **逻辑像素**(旧壳写盘语义:设置页 spinner 与 Ctrl+滚轮缩放持久化时 /// 已除以 scale factor)。`None` = 跟随 nebula.toml 的 `font.size`(pt)。 pub font_size_px: Option, @@ -1081,6 +1082,7 @@ impl RuntimeSettings { .unwrap_or_default(), follow_system_theme: raw.bool_on("follow_system_theme").unwrap_or(false), font_family: raw.value("font_family").map(str::to_owned), + font_family_cjk: raw.value("font_family_cjk").map(str::to_owned), font_size_px: raw.f32("font_size").map(|size| size.clamp(4.0, 96.0)), cursor_shape: raw.value("cursor_shape").and_then(CursorShapeName::from_settings), cursor_blink: raw.bool_on("cursor_blink"), @@ -1305,6 +1307,7 @@ mod tests { completion_style=popup\n\ shell=pwsh\n\ font_family=Maple Mono Normal NF CN\n\ + font_family_cjk=PingFang SC\n\ font_size=16.3\n\ cursor_shape=beam\n\ cursor_blink=1\n\ @@ -1339,6 +1342,7 @@ mod tests { assert_eq!(settings.language, LanguagePref::ZhCn); assert_eq!(settings.theme, ThemeName::SilverLight); assert_eq!(settings.font_family.as_deref(), Some("Maple Mono Normal NF CN")); + assert_eq!(settings.font_family_cjk.as_deref(), Some("PingFang SC")); // font_size 键存的是逻辑像素(旧壳写盘语义),不做 pt 换算。 assert_eq!(settings.font_size_px, Some(16.3)); assert_eq!(settings.cursor_shape, Some(CursorShapeName::Beam)); @@ -1381,6 +1385,7 @@ mod tests { assert_eq!(settings.language, LanguagePref::System); assert_eq!(settings.theme, ThemeName::Nord); assert_eq!(settings.font_family, None); + assert_eq!(settings.font_family_cjk, None); assert!(!settings.copy_on_select); assert!(settings.multiline_paste_confirm, "多行粘贴确认默认开(上游兼容)"); assert!(settings.tab_close_visible, "标签关闭按钮默认可见(上游兼容)"); diff --git a/nebula_settings/src/reset.rs b/nebula_settings/src/reset.rs index 2e8c87c..c54b470 100644 --- a/nebula_settings/src/reset.rs +++ b/nebula_settings/src/reset.rs @@ -10,6 +10,7 @@ const RESET_KEYS: &[&str] = &[ "app_icon", "follow_system_theme", "font_family", + "font_family_cjk", "font_size", "cursor_shape", "cursor_blink", From 7db14e5130016971d2350d30bb8ab9fe055da3d3 Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:06:05 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E7=95=8C=E9=9D=A2=E5=AD=97=E4=BD=93=E5=92=8C=E5=AD=97?= =?UTF-8?q?=E5=8F=B7=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对应 https://github.com/Kuddev/pebrel/issues/123 --- nebula_app/i18n/en-US.json | 4 ++ nebula_app/i18n/zh-CN.json | 4 ++ nebula_app/src/gpui_shell/config.rs | 2 + nebula_app/src/gpui_shell/settings_pane.rs | 15 ++---- .../gpui_shell/settings_pane/appearance.rs | 37 ++++++++------- .../settings_pane/appearance_advanced.rs | 11 ++++- .../settings_pane/initialization.rs | 47 ++++++++++++------- .../src/gpui_shell/ssh_settings/library.rs | 2 +- .../terminal/ssh_connect_overlay.rs | 4 +- nebula_app/src/gpui_shell/theme.rs | 12 ++++- .../src/gpui_shell/workspace/pane_header.rs | 4 +- .../src/gpui_shell/workspace/sidebar.rs | 13 ++--- .../src/gpui_shell/workspace/top_tabs.rs | 4 +- nebula_settings/src/lib.rs | 10 ++++ nebula_settings/src/reset.rs | 2 + 15 files changed, 107 insertions(+), 64 deletions(-) diff --git a/nebula_app/i18n/en-US.json b/nebula_app/i18n/en-US.json index 4674cfd..34b60ab 100644 --- a/nebula_app/i18n/en-US.json +++ b/nebula_app/i18n/en-US.json @@ -77,6 +77,10 @@ "background_cover_description": "The image will extend beneath the title bar, window buttons, tabs, and SSH sidebar. Low-contrast images may make controls harder to see; the interface retains a minimum-opacity safeguard." }, "font": { + "ui_family": "Interface font", + "ui_family_description": "Enter an installed font family; leave empty for the default. Applies on Enter or leaving the field.", + "ui_size": "Interface font size", + "ui_size_description": "Changes interface text and component sizing independently of terminal text.", "english": "English terminal font", "chinese": "Chinese terminal font", "chinese_description": "Used for Chinese and other full-width characters. Enter a font family or comma-separated families; leave empty to use the English font settings. Applies on Enter or leaving the field.", diff --git a/nebula_app/i18n/zh-CN.json b/nebula_app/i18n/zh-CN.json index 35fd88e..c38c597 100644 --- a/nebula_app/i18n/zh-CN.json +++ b/nebula_app/i18n/zh-CN.json @@ -77,6 +77,10 @@ "background_cover_description": "背景图会延伸到标题栏、窗口按钮、Tab 与 SSH 侧栏下方,低对比度图片可能影响操作可见性;界面仍会保留最低不透明度保护。" }, "font": { + "ui_family": "界面字体", + "ui_family_description": "输入已安装的字体名称;留空使用默认字体。按回车或离开输入框后生效。", + "ui_size": "界面字号", + "ui_size_description": "独立调整界面文字和组件字号,不影响终端文字。", "english": "英文终端字体", "chinese": "中文终端字体", "chinese_description": "用于中文等全宽字符。输入字体名称,多个字体以逗号分隔;留空时使用英文字体设置。按回车或离开输入框后生效。", diff --git a/nebula_app/src/gpui_shell/config.rs b/nebula_app/src/gpui_shell/config.rs index 26c4354..a6046c6 100644 --- a/nebula_app/src/gpui_shell/config.rs +++ b/nebula_app/src/gpui_shell/config.rs @@ -51,6 +51,7 @@ pub struct Settings { /// 配置文件的基准字号,不含设置页/Ctrl+滚轮持久化的终端缩放。 /// 启动窗口按它定形,和旧壳的 `window_size` 契约一致。 pub base_font_size_px: f32, + pub ui_font_size_px: f32, /// 字体 cell 的物理像素偏移;旧壳 Windows 默认 y=4,必须在设备像素 /// 域参与取整,才能在 125%/150% DPI 下保持同一行数。 pub font_offset_x: f32, @@ -166,6 +167,7 @@ impl Settings { font_bold_italic_family: secondary(&raw.font.bold_italic), font_size_px, base_font_size_px, + ui_font_size_px: runtime.ui_font_size_px.unwrap_or(base_font_size_px), font_offset_x: f32::from(offset.x), font_offset_y: f32::from(offset.y), palette, diff --git a/nebula_app/src/gpui_shell/settings_pane.rs b/nebula_app/src/gpui_shell/settings_pane.rs index 550cca3..32b7427 100644 --- a/nebula_app/src/gpui_shell/settings_pane.rs +++ b/nebula_app/src/gpui_shell/settings_pane.rs @@ -176,6 +176,7 @@ pub struct SettingsPane { font_imported: Vec, font_family_input: Entity, font_family_cjk_input: Entity, + ui_font_family_input: Entity, /// 字体输入框上一帧的窗口坐标。字体目录是宽弹层,不能把整条设置行当 /// 锚点;否则输入框在右侧、菜单却会从正文左缘展开。 font_picker_trigger_bounds: Option>, @@ -377,14 +378,9 @@ impl SettingsPane { self.sync_background_color_picker(window, cx); } - /// Workspace tab 与设置正文共用的主文字字号事实源;导航使用组件小字号。 - /// - /// 旧壳合同(display/mod.rs `ui_font_px`):chrome 排版锚定**配置字号** - /// (nebula.toml `font.size`,默认 11.25pt = 15px),终端的持久化缩放 - /// (设置 spinner / Ctrl+滚轮写入的 `font_size=`)只影响终端网格, - /// 不得放大侧栏与设置文字。 + /// 界面独立字号;未设置时保留原配置字号,终端缩放不影响这里。 fn font_size_px(&self, cx: &App) -> f32 { - cx.global::().base_font_size_px + cx.global::().ui_font_size_px } /// 终端字号(预览与「终端字号」步进行显示的值)。 @@ -392,11 +388,6 @@ impl SettingsPane { cx.global::().font_size_px } - fn set_font_size(&mut self, size: f32, cx: &mut Context) { - let size = size.clamp(4.0, 96.0); - self.persist(&[("font_size", format!("{size:.2}"))], cx); - } - /// 拖拽期间只做"改 alpha + 重绘"这两件必须的事,落盘与整套热应用 /// 交给 [`Self::schedule_slider_persist`]。旧壳拖 opacity 就是改内存 + 重绘, /// 这条快路径是为了对回那个手感。 diff --git a/nebula_app/src/gpui_shell/settings_pane/appearance.rs b/nebula_app/src/gpui_shell/settings_pane/appearance.rs index d965e97..cc13a4e 100644 --- a/nebula_app/src/gpui_shell/settings_pane/appearance.rs +++ b/nebula_app/src/gpui_shell/settings_pane/appearance.rs @@ -88,45 +88,46 @@ impl SettingsPane { .into_any_element() } - pub(super) fn terminal_font_size_row(&self, cx: &Context) -> gpui::AnyElement { + pub(super) fn font_size_row(&self, ui: bool, cx: &Context) -> gpui::AnyElement { let language = crate::gpui_shell::config::ui_language(cx); - let size = self.terminal_font_size_px(cx); + let size = if ui { self.font_size_px(cx) } else { self.terminal_font_size_px(cx) }; + let (key, min, max) = if ui { ("ui_font_size", 10.0, 24.0) } else { ("font_size", 4.0, 96.0) }; let stepper = h_flex() .w(px(142.0)) .h(px(36.0)) .items_center() .child( - Button::new("appearance-font-smaller") + Button::new(SharedString::from(format!("{key}-smaller"))) .icon(IconName::Minus) .ghost() .size(px(34.0)) - .disabled(size <= 4.0) + .disabled(size <= min) .tooltip(language.pick("减小字号", "Decrease font size")) - .on_click(cx.listener(|this, _, _, cx| { - this.set_font_size( - (this.terminal_font_size_px(cx).ceil() - 1.0).round(), - cx, - ); + .on_click(cx.listener(move |this, _, _, cx| { + let next = (size.ceil() - 1.0).clamp(min, max); + this.persist(&[(key, format!("{next:.2}"))], cx); })), ) .child(div().flex_1().text_center().child(format!("{size:.0} px"))) .child( - Button::new("appearance-font-larger") + Button::new(SharedString::from(format!("{key}-larger"))) .icon(IconName::Plus) .ghost() .size(px(34.0)) - .disabled(size >= 96.0) + .disabled(size >= max) .tooltip(language.pick("增大字号", "Increase font size")) - .on_click(cx.listener(|this, _, _, cx| { - this.set_font_size( - (this.terminal_font_size_px(cx).floor() + 1.0).round(), - cx, - ); + .on_click(cx.listener(move |this, _, _, cx| { + let next = (size.floor() + 1.0).clamp(min, max); + this.persist(&[(key, format!("{next:.2}"))], cx); })), ); self.row( - language.pick("终端字号(Ctrl+滚轮缩放)", "Terminal font size (Ctrl+wheel)"), - language.pick("只调整终端文字大小。", "Changes only the terminal text size."), + if ui { language.text(crate::i18n::Message::SettingsFontUiSize) } else { + language.pick("终端字号(Ctrl+滚轮缩放)", "Terminal font size (Ctrl+wheel)") + }, + if ui { language.text(crate::i18n::Message::SettingsFontUiSizeDescription) } else { + language.pick("只调整终端文字大小。", "Changes only the terminal text size.") + }, stepper, cx, ) diff --git a/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs b/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs index bd0a867..feaa88e 100644 --- a/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs +++ b/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs @@ -61,6 +61,15 @@ impl SettingsPane { )); let interface = self .group(language.pick("界面", "Interface"), cx) + .child(self.row( + language.text(crate::i18n::Message::SettingsFontUiFamily), + language.text(crate::i18n::Message::SettingsFontUiFamilyDescription), + Input::new(&self.ui_font_family_input) + .w(px(SETTINGS_SELECT_WIDTH)) + .aria_label(language.text(crate::i18n::Message::SettingsFontUiFamily)), + cx, + )) + .child(self.font_size_row(true, cx)) .child(self.switch_row( "tab_close_visible", language.pick("显示标签关闭按钮", "Show tab close buttons"), @@ -109,7 +118,7 @@ impl SettingsPane { .aria_label(language.text(crate::i18n::Message::SettingsFontChinese)), cx, )) - .child(self.terminal_font_size_row(cx)) + .child(self.font_size_row(false, cx)) .child(self.select_row( "cell_width_mode", language.pick("字体间距", "Character spacing"), diff --git a/nebula_app/src/gpui_shell/settings_pane/initialization.rs b/nebula_app/src/gpui_shell/settings_pane/initialization.rs index e3792f9..5bdb36e 100644 --- a/nebula_app/src/gpui_shell/settings_pane/initialization.rs +++ b/nebula_app/src/gpui_shell/settings_pane/initialization.rs @@ -443,23 +443,37 @@ impl SettingsPane { InputState::new(window, cx) .default_value(runtime.font_family_cjk.clone().unwrap_or_default()) }); - subscriptions.push(cx.subscribe_in( - &font_family_cjk_input, - window, - |this: &mut Self, _, event: &InputEvent, window, cx| { - if matches!(event, InputEvent::PressEnter { .. } | InputEvent::Blur) { - let value = crate::font_install::normalize_font_family_chain( - &this.font_family_cjk_input.read(cx).value(), - ); - this.font_family_cjk_input.update(cx, |input, cx| { - input.set_value(value.clone(), window, cx); - }); - if this.runtime.font_family_cjk.as_deref().unwrap_or_default() != value { - this.persist(&[("font_family_cjk", value)], cx); + let ui_font_family_input = cx.new(|cx| { + InputState::new(window, cx) + .default_value(runtime.ui_font_family.clone().unwrap_or_default()) + }); + for (key, input) in [ + ("font_family_cjk", &font_family_cjk_input), + ("ui_font_family", &ui_font_family_input), + ] { + subscriptions.push(cx.subscribe_in( + input, + window, + move |this: &mut Self, input, event: &InputEvent, window, cx| { + if matches!(event, InputEvent::PressEnter { .. } | InputEvent::Blur) { + let raw = input.read(cx).value(); + let (value, current) = if key == "ui_font_family" { + (raw.trim().to_owned(), &this.runtime.ui_font_family) + } else { + ( + crate::font_install::normalize_font_family_chain(&raw), + &this.runtime.font_family_cjk, + ) + }; + let changed = current.as_deref().unwrap_or_default() != value; + input.update(cx, |input, cx| input.set_value(value.clone(), window, cx)); + if changed { + this.persist(&[(key, value)], cx); + } } - } - }, - )); + }, + )); + } let bg_picker_hsv = { let term = crate::gpui_shell::theme::chrome_theme_resolved(cx).palette().term_bg; @@ -571,6 +585,7 @@ impl SettingsPane { font_imported: Vec::new(), font_family_input, font_family_cjk_input, + ui_font_family_input, font_picker_trigger_bounds: None, backup_selection: crate::encrypted_backup::BackupSelection::default(), backup_pass_input: cx.new(|cx| { diff --git a/nebula_app/src/gpui_shell/ssh_settings/library.rs b/nebula_app/src/gpui_shell/ssh_settings/library.rs index 65ea75c..a23a8c0 100644 --- a/nebula_app/src/gpui_shell/ssh_settings/library.rs +++ b/nebula_app/src/gpui_shell/ssh_settings/library.rs @@ -226,7 +226,7 @@ impl SettingsPane { let symbol_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); let font_px = cx .try_global::() - .map(|s| s.base_font_size_px) + .map(|s| s.ui_font_size_px) .unwrap_or(15.0); let title_h = font_px; let subtitle_h = font_px * 0.78; 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 bd1b60e..a1ba366 100644 --- a/nebula_app/src/gpui_shell/terminal/ssh_connect_overlay.rs +++ b/nebula_app/src/gpui_shell/terminal/ssh_connect_overlay.rs @@ -293,9 +293,9 @@ pub(super) fn overlay( // chrome 文本锚定配置字号(旧壳 ui_font 合同),不跟终端缩放。 let ui_px = cx .try_global::() - .map(|settings| settings.base_font_size_px) + .map(|settings| settings.ui_font_size_px) .unwrap_or(15.0); - let chrome_family = theme.mono_font_family.clone(); + let chrome_family = theme.font_family.clone(); let log_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); let ink_strong = theme.sidebar_accent_foreground; let ink = theme.foreground; diff --git a/nebula_app/src/gpui_shell/theme.rs b/nebula_app/src/gpui_shell/theme.rs index 4208b98..20f5baa 100644 --- a/nebula_app/src/gpui_shell/theme.rs +++ b/nebula_app/src/gpui_shell/theme.rs @@ -547,6 +547,7 @@ fn soften(c: crate::display::color::Rgb, keep: f32) -> crate::display::color::Rg } fn apply_skin_tokens(chrome: NebulaTheme, cx: &mut App) { + let runtime = nebula_settings::RuntimeSettings::load(); let sk = chrome.skin(); let transparent = hsla(0.0, 0.0, 0.0, 0.0); let theme = Theme::global_mut(cx); @@ -666,8 +667,8 @@ fn apply_skin_tokens(chrome: NebulaTheme, cx: &mut App) { // 字号与圆角:控件 pill 档 = 旧壳 UI_CORNER_RADIUS_LOGICAL(8);浮层 // 12,低于终端卡的 14——三档呼应旧壳的圆角层级。 - theme.font_size = px(14.0); - theme.mono_font_size = px(13.0); + theme.font_size = px(runtime.ui_font_size_px.unwrap_or(14.0)); + theme.mono_font_size = theme.font_size * (13.0 / 14.0); // 整壳的兜底字体(fork `root.rs` 用 `theme.font_family` 给根容器)。上游 // 默认 `.SystemUIFont` 在 Windows 上没落到 UI 字体,中文最终回落进终端等 @@ -685,6 +686,13 @@ fn apply_skin_tokens(chrome: NebulaTheme, cx: &mut App) { // 随终端主字体变化,进而破坏 chrome 的既定间距。 theme.mono_font_family = crate::font_install::REQUIRED_FONT_FAMILY.into(); } + theme.font_family = runtime + .ui_font_family + .unwrap_or_else(|| { + if cfg!(target_os = "windows") { "Microsoft YaHei UI" } else { ".SystemUIFont" } + .to_owned() + }) + .into(); theme.radius = px(crate::display::UI_CORNER_RADIUS_LOGICAL); theme.radius_lg = px(12.0); diff --git a/nebula_app/src/gpui_shell/workspace/pane_header.rs b/nebula_app/src/gpui_shell/workspace/pane_header.rs index 473c7fe..acf2402 100644 --- a/nebula_app/src/gpui_shell/workspace/pane_header.rs +++ b/nebula_app/src/gpui_shell/workspace/pane_header.rs @@ -258,9 +258,9 @@ impl NebulaWorkspace { let bar_bg = if focused { theme.sidebar_accent.opacity(0.55) } else { theme.muted.opacity(0.28) }; let settings = cx.try_global::(); - let chrome_family = theme.mono_font_family.clone(); + let chrome_family = theme.font_family.clone(); let symbol_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); - let label_px = settings.map(|settings| settings.base_font_size_px).unwrap_or(15.0); + let label_px = settings.map(|settings| settings.ui_font_size_px).unwrap_or(15.0); let title_px = label_px * 0.78; let PaneTitle { logo, glyph, text } = self.pane_title(view, cx, dark); let group: SharedString = format!("pane-header-{pane_id}").into(); diff --git a/nebula_app/src/gpui_shell/workspace/sidebar.rs b/nebula_app/src/gpui_shell/workspace/sidebar.rs index 4b114c7..07e14fb 100644 --- a/nebula_app/src/gpui_shell/workspace/sidebar.rs +++ b/nebula_app/src/gpui_shell/workspace/sidebar.rs @@ -240,13 +240,10 @@ impl NebulaWorkspace { let tab_reveal = settings .map(|settings| settings.tab_reveal) .unwrap_or(nebula_settings::TabRevealName::Slide); - let chrome_family = theme.mono_font_family.clone(); + let chrome_family = theme.font_family.clone(); let symbol_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); - // 旧壳合同(display/mod.rs `ui_font_px`):chrome 锚定**配置字号** - // (nebula.toml `font.size` 默认 11.25pt = 15px)。终端的持久化缩放 - // (`font_size=` 键)只影响终端网格,侧栏不得跟着变粗/变大; - // 固定 14px 的旧毛病(比旧壳小一号)也不能回潮。 - let label_px = settings.map(|settings| settings.base_font_size_px).unwrap_or(15.0); + // 界面字号独立于终端缩放。 + let label_px = settings.map(|settings| settings.ui_font_size_px).unwrap_or(15.0); let cell_w = self.sidebar_cell_width(window, &chrome_family, label_px); // 受约束拖拽的渲染参数:激活后被拖行骑指针位移,落点槽位由位移换算。 @@ -1041,9 +1038,9 @@ impl NebulaWorkspace { let (ink, dim, badge_fill) = (theme.foreground, theme.muted_foreground, theme.muted); let dark = theme.is_dark(); let settings = cx.try_global::(); - let chrome_family = theme.mono_font_family.clone(); + let chrome_family = theme.font_family.clone(); let symbol_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); - let label_px = settings.map(|settings| settings.base_font_size_px).unwrap_or(15.0); + let label_px = settings.map(|settings| settings.ui_font_size_px).unwrap_or(15.0); let TabPresentation { title, logo_image, program_glyph, pane_count, .. } = self.tab_presentation(self.active, cx, dark); slot.child( diff --git a/nebula_app/src/gpui_shell/workspace/top_tabs.rs b/nebula_app/src/gpui_shell/workspace/top_tabs.rs index f9968fc..704de7f 100644 --- a/nebula_app/src/gpui_shell/workspace/top_tabs.rs +++ b/nebula_app/src/gpui_shell/workspace/top_tabs.rs @@ -141,9 +141,9 @@ impl NebulaWorkspace { let tab_reveal = settings .map(|settings| settings.tab_reveal) .unwrap_or(nebula_settings::TabRevealName::Slide); - let chrome_family = theme.mono_font_family.clone(); + let chrome_family = theme.font_family.clone(); let symbol_family: SharedString = crate::font_install::REQUIRED_FONT_FAMILY.into(); - let label_px = settings.map(|settings| settings.base_font_size_px).unwrap_or(15.0); + let label_px = settings.map(|settings| settings.ui_font_size_px).unwrap_or(15.0); let tab_capacity_w = (f32::from(window.viewport_size().width) - TOP_TAB_RESERVED_W).max(TOP_TAB_MIN_W); let tab_w = tab_width(tab_capacity_w, self.top_tab_count()); diff --git a/nebula_settings/src/lib.rs b/nebula_settings/src/lib.rs index 692f61f..19ae7bf 100644 --- a/nebula_settings/src/lib.rs +++ b/nebula_settings/src/lib.rs @@ -942,6 +942,8 @@ pub struct RuntimeSettings { pub follow_system_theme: bool, pub font_family: Option, pub font_family_cjk: Option, + pub ui_font_family: Option, + pub ui_font_size_px: Option, /// **逻辑像素**(旧壳写盘语义:设置页 spinner 与 Ctrl+滚轮缩放持久化时 /// 已除以 scale factor)。`None` = 跟随 nebula.toml 的 `font.size`(pt)。 pub font_size_px: Option, @@ -1083,6 +1085,8 @@ impl RuntimeSettings { follow_system_theme: raw.bool_on("follow_system_theme").unwrap_or(false), font_family: raw.value("font_family").map(str::to_owned), font_family_cjk: raw.value("font_family_cjk").map(str::to_owned), + ui_font_family: raw.value("ui_font_family").map(str::to_owned), + ui_font_size_px: raw.f32("ui_font_size").map(|size| size.clamp(10.0, 24.0)), font_size_px: raw.f32("font_size").map(|size| size.clamp(4.0, 96.0)), cursor_shape: raw.value("cursor_shape").and_then(CursorShapeName::from_settings), cursor_blink: raw.bool_on("cursor_blink"), @@ -1307,6 +1311,8 @@ mod tests { completion_style=popup\n\ shell=pwsh\n\ font_family=Maple Mono Normal NF CN\n\ + ui_font_family=Arial\n\ + ui_font_size=18\n\ font_family_cjk=PingFang SC\n\ font_size=16.3\n\ cursor_shape=beam\n\ @@ -1343,6 +1349,8 @@ mod tests { assert_eq!(settings.theme, ThemeName::SilverLight); assert_eq!(settings.font_family.as_deref(), Some("Maple Mono Normal NF CN")); assert_eq!(settings.font_family_cjk.as_deref(), Some("PingFang SC")); + assert_eq!(settings.ui_font_family.as_deref(), Some("Arial")); + assert_eq!(settings.ui_font_size_px, Some(18.0)); // font_size 键存的是逻辑像素(旧壳写盘语义),不做 pt 换算。 assert_eq!(settings.font_size_px, Some(16.3)); assert_eq!(settings.cursor_shape, Some(CursorShapeName::Beam)); @@ -1386,6 +1394,8 @@ mod tests { assert_eq!(settings.theme, ThemeName::Nord); assert_eq!(settings.font_family, None); assert_eq!(settings.font_family_cjk, None); + assert_eq!(settings.ui_font_family, None); + assert_eq!(settings.ui_font_size_px, None); assert!(!settings.copy_on_select); assert!(settings.multiline_paste_confirm, "多行粘贴确认默认开(上游兼容)"); assert!(settings.tab_close_visible, "标签关闭按钮默认可见(上游兼容)"); diff --git a/nebula_settings/src/reset.rs b/nebula_settings/src/reset.rs index c54b470..cc71e31 100644 --- a/nebula_settings/src/reset.rs +++ b/nebula_settings/src/reset.rs @@ -11,6 +11,8 @@ const RESET_KEYS: &[&str] = &[ "follow_system_theme", "font_family", "font_family_cjk", + "ui_font_family", + "ui_font_size", "font_size", "cursor_shape", "cursor_blink", From db11967f5089d809a57411ad76c4f1346db4f6f1 Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:13:43 +0800 Subject: [PATCH 3/6] =?UTF-8?q?ci:=20=E6=89=A9=E5=B1=95=E5=9B=9B=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E5=85=A8=E9=87=8F=E6=B5=8B=E8=AF=95=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/linux-lua.yml | 39 +++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/linux-lua.yml b/.github/workflows/linux-lua.yml index 6a5b6b2..da8e1b7 100644 --- a/.github/workflows/linux-lua.yml +++ b/.github/workflows/linux-lua.yml @@ -1,6 +1,7 @@ -name: Linux Lua configuration +name: Full native tests on: + workflow_dispatch: push: paths: - "**/*.rs" @@ -20,15 +21,30 @@ permissions: contents: read jobs: - ubuntu-24-04: - runs-on: ubuntu-24.04 + native-tests: + name: Tests (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, windows-2022, macos-15, macos-15-intel] + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + env: + PYTHONUTF8: "1" + CARGO_TERM_COLOR: always steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" - name: Install native dependencies + if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ - build-essential cmake pkg-config libasound2-dev libdbus-1-dev libudev-dev \ + build-essential cmake pkg-config zsh libasound2-dev libdbus-1-dev libudev-dev \ libfontconfig1-dev libfreetype6-dev \ libegl1-mesa-dev libgl1-mesa-dev \ libwayland-dev libxkbcommon-dev libxkbcommon-x11-dev \ @@ -38,8 +54,13 @@ jobs: with: toolchain: "1.97.1" - uses: Swatinem/rust-cache@v2 - - name: Test Lua configuration on Wayland and X11 build - run: | - cargo test --locked -p nebula config:: - - name: Check Linux release build - run: cargo check --workspace --release + - name: Test all Python helpers + run: python -m unittest discover -s scripts/tests -v + - name: Test all conformance harness helpers + run: python -m unittest discover -s scripts/conformance/tests -v + - name: Test the full Rust workspace + run: cargo test --locked --workspace + - name: Test all GPUI product and interaction cases + run: cargo test --locked -p nebula --bin pebrel --features gpui-test-support + - name: Check the release workspace + run: cargo check --locked --workspace --release From 28c071185b21cb62e9f97b70d0a8ed00fc1fa669 Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:50:14 +0800 Subject: [PATCH 4/6] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E8=B7=A8?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E4=B8=B4=E6=97=B6=E8=B7=AF=E5=BE=84=E6=AF=94?= =?UTF-8?q?=E8=BE=83=E5=B9=B6=E8=A7=A6=E5=8F=91=20Python=20=E6=94=B9?= =?UTF-8?q?=E5=8A=A8=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/linux-lua.yml | 2 ++ scripts/conformance/tests/test_harness.py | 2 +- scripts/tests/test_architecture_budgets.py | 2 +- scripts/tests/test_xplat.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux-lua.yml b/.github/workflows/linux-lua.yml index da8e1b7..f64ffce 100644 --- a/.github/workflows/linux-lua.yml +++ b/.github/workflows/linux-lua.yml @@ -5,6 +5,7 @@ on: push: paths: - "**/*.rs" + - "scripts/**/*.py" - "**/Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" @@ -12,6 +13,7 @@ on: pull_request: paths: - "**/*.rs" + - "scripts/**/*.py" - "**/Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" diff --git a/scripts/conformance/tests/test_harness.py b/scripts/conformance/tests/test_harness.py index 950c0f0..e1b97e5 100644 --- a/scripts/conformance/tests/test_harness.py +++ b/scripts/conformance/tests/test_harness.py @@ -139,7 +139,7 @@ class ArchiveSafetyTests(unittest.TestCase): def test_macos_bundle_resolves_declared_executable_and_rejects_other_paths(self) -> None: with tempfile.TemporaryDirectory() as directory: - root = Path(directory) / "Pebrel Preview.app" + root = Path(directory).resolve() / "Pebrel Preview.app" binary = root / "Contents" / "MacOS" / "pebrel" binary.parent.mkdir(parents=True) binary.write_bytes(b"fixture") diff --git a/scripts/tests/test_architecture_budgets.py b/scripts/tests/test_architecture_budgets.py index bbfa0f6..91cd27a 100644 --- a/scripts/tests/test_architecture_budgets.py +++ b/scripts/tests/test_architecture_budgets.py @@ -13,7 +13,7 @@ class LineBudgetTests(unittest.TestCase): def setUp(self): self.temporary = tempfile.TemporaryDirectory() self.addCleanup(self.temporary.cleanup) - self.root = Path(self.temporary.name) + self.root = Path(self.temporary.name).resolve() (self.root / "app/src").mkdir(parents=True) def write(self, name, count): diff --git a/scripts/tests/test_xplat.py b/scripts/tests/test_xplat.py index 55720a1..cc238ad 100644 --- a/scripts/tests/test_xplat.py +++ b/scripts/tests/test_xplat.py @@ -18,7 +18,7 @@ class XplatIsolationTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory(prefix="nebula-xplat-test-") self.addCleanup(self.temp.cleanup) - self.root = Path(self.temp.name) + self.root = Path(self.temp.name).resolve() self.repo = self.root / "repo with spaces" self.scripts = self.repo / "scripts/xplat" shutil.copytree(ROOT / "scripts/xplat", self.scripts) From 7d48b6a4fdffb2746d608216696e75cdd77cc94c Mon Sep 17 00:00:00 2001 From: kuddev Date: Sun, 13 Sep 2026 20:06:03 +0800 Subject: [PATCH 5/6] style(settings): format independent font controls --- .../gpui_shell/settings_pane/appearance.rs | 11 ++++-- .../settings_pane/appearance_advanced.rs | 36 ++++++++++--------- .../settings_pane/initialization.rs | 7 ++-- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/nebula_app/src/gpui_shell/settings_pane/appearance.rs b/nebula_app/src/gpui_shell/settings_pane/appearance.rs index cc13a4e..7337207 100644 --- a/nebula_app/src/gpui_shell/settings_pane/appearance.rs +++ b/nebula_app/src/gpui_shell/settings_pane/appearance.rs @@ -91,7 +91,8 @@ impl SettingsPane { pub(super) fn font_size_row(&self, ui: bool, cx: &Context) -> gpui::AnyElement { let language = crate::gpui_shell::config::ui_language(cx); let size = if ui { self.font_size_px(cx) } else { self.terminal_font_size_px(cx) }; - let (key, min, max) = if ui { ("ui_font_size", 10.0, 24.0) } else { ("font_size", 4.0, 96.0) }; + let (key, min, max) = + if ui { ("ui_font_size", 10.0, 24.0) } else { ("font_size", 4.0, 96.0) }; let stepper = h_flex() .w(px(142.0)) .h(px(36.0)) @@ -122,10 +123,14 @@ impl SettingsPane { })), ); self.row( - if ui { language.text(crate::i18n::Message::SettingsFontUiSize) } else { + if ui { + language.text(crate::i18n::Message::SettingsFontUiSize) + } else { language.pick("终端字号(Ctrl+滚轮缩放)", "Terminal font size (Ctrl+wheel)") }, - if ui { language.text(crate::i18n::Message::SettingsFontUiSizeDescription) } else { + if ui { + language.text(crate::i18n::Message::SettingsFontUiSizeDescription) + } else { language.pick("只调整终端文字大小。", "Changes only the terminal text size.") }, stepper, diff --git a/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs b/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs index feaa88e..db2748a 100644 --- a/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs +++ b/nebula_app/src/gpui_shell/settings_pane/appearance_advanced.rs @@ -61,14 +61,16 @@ impl SettingsPane { )); let interface = self .group(language.pick("界面", "Interface"), cx) - .child(self.row( - language.text(crate::i18n::Message::SettingsFontUiFamily), - language.text(crate::i18n::Message::SettingsFontUiFamilyDescription), - Input::new(&self.ui_font_family_input) - .w(px(SETTINGS_SELECT_WIDTH)) - .aria_label(language.text(crate::i18n::Message::SettingsFontUiFamily)), - cx, - )) + .child( + self.row( + language.text(crate::i18n::Message::SettingsFontUiFamily), + language.text(crate::i18n::Message::SettingsFontUiFamilyDescription), + Input::new(&self.ui_font_family_input) + .w(px(SETTINGS_SELECT_WIDTH)) + .aria_label(language.text(crate::i18n::Message::SettingsFontUiFamily)), + cx, + ), + ) .child(self.font_size_row(true, cx)) .child(self.switch_row( "tab_close_visible", @@ -110,14 +112,16 @@ impl SettingsPane { font_picker, cx, )) - .child(self.row( - language.text(crate::i18n::Message::SettingsFontChinese), - language.text(crate::i18n::Message::SettingsFontChineseDescription), - Input::new(&self.font_family_cjk_input) - .w(px(SETTINGS_SELECT_WIDTH)) - .aria_label(language.text(crate::i18n::Message::SettingsFontChinese)), - cx, - )) + .child( + self.row( + language.text(crate::i18n::Message::SettingsFontChinese), + language.text(crate::i18n::Message::SettingsFontChineseDescription), + Input::new(&self.font_family_cjk_input) + .w(px(SETTINGS_SELECT_WIDTH)) + .aria_label(language.text(crate::i18n::Message::SettingsFontChinese)), + cx, + ), + ) .child(self.font_size_row(false, cx)) .child(self.select_row( "cell_width_mode", diff --git a/nebula_app/src/gpui_shell/settings_pane/initialization.rs b/nebula_app/src/gpui_shell/settings_pane/initialization.rs index 5bdb36e..6e4a33c 100644 --- a/nebula_app/src/gpui_shell/settings_pane/initialization.rs +++ b/nebula_app/src/gpui_shell/settings_pane/initialization.rs @@ -447,10 +447,9 @@ impl SettingsPane { InputState::new(window, cx) .default_value(runtime.ui_font_family.clone().unwrap_or_default()) }); - for (key, input) in [ - ("font_family_cjk", &font_family_cjk_input), - ("ui_font_family", &ui_font_family_input), - ] { + for (key, input) in + [("font_family_cjk", &font_family_cjk_input), ("ui_font_family", &ui_font_family_input)] + { subscriptions.push(cx.subscribe_in( input, window, From 90df5811277d9357febf9522cb8a759176df2776 Mon Sep 17 00:00:00 2001 From: kuddev Date: Sun, 13 Sep 2026 20:34:59 +0800 Subject: [PATCH 6/6] refactor(settings): share platform detection for default UI fonts --- nebula_app/src/gpui_shell/theme.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/nebula_app/src/gpui_shell/theme.rs b/nebula_app/src/gpui_shell/theme.rs index 20f5baa..0829216 100644 --- a/nebula_app/src/gpui_shell/theme.rs +++ b/nebula_app/src/gpui_shell/theme.rs @@ -678,21 +678,17 @@ fn apply_skin_tokens(chrome: NebulaTheme, cx: &mut App) { // 我们是终端,所以等宽在这里是**语义标记**而不是全局字体:路径、键帽、 // 命令、数值这类"机器读、要逐字符对齐、要能整段复制"的东西显式走 mono; // 标题和说明是给人读的,走 sans。 - #[cfg(target_os = "windows")] - { - theme.font_family = "Microsoft YaHei UI".into(); - // UI 中的等宽语义也必须稳定。终端字体由 TerminalView 单独读取; - // 若把用户字体组写进全局 theme,tab、标题和代码字面量的字宽都会 - // 随终端主字体变化,进而破坏 chrome 的既定间距。 - theme.mono_font_family = crate::font_install::REQUIRED_FONT_FAMILY.into(); - } - theme.font_family = runtime - .ui_font_family - .unwrap_or_else(|| { - if cfg!(target_os = "windows") { "Microsoft YaHei UI" } else { ".SystemUIFont" } - .to_owned() - }) - .into(); + let default_ui_font = + if crate::platform::Platform::current() == crate::platform::Platform::Windows { + // UI 中的等宽语义也必须稳定。终端字体由 TerminalView 单独读取; + // 若把用户字体组写进全局 theme,tab、标题和代码字面量的字宽都会 + // 随终端主字体变化,进而破坏 chrome 的既定间距。 + theme.mono_font_family = crate::font_install::REQUIRED_FONT_FAMILY.into(); + "Microsoft YaHei UI" + } else { + ".SystemUIFont" + }; + theme.font_family = runtime.ui_font_family.unwrap_or_else(|| default_ui_font.to_owned()).into(); theme.radius = px(crate::display::UI_CORNER_RADIUS_LOGICAL); theme.radius_lg = px(12.0);