fix(terminal): 补全 Nerd Font 图标字体回退,修复提示符图标显示为豆腐块

starship / powerlevel10k 等提示符的图标取自 Nerd Font 私有区。Chromium 的
隐式系统字体回退能覆盖 BMP PUA(U+E000–U+F8FF,如 U+E0B0 powerline 分隔符),
但覆盖不到补充平面 PUA-B(U+F0000+)——Nerd Fonts v3 把 Material Design 图标集
迁到了这里,于是这部分图标渲染为豆腐块。经排查用户字体本身含完整字形(format-12
cmap 覆盖上述码点),而 macOS 系统等宽字体(Menlo / SF Mono / Monaco)对这些
码点零覆盖,根因是插件的 fontFamily 栈未显式列出任何 Nerd Font。

修复:新增 withIconFontFallbacks(),在 resolveTerminalFont 出口为胜出的基础
字体追加 Nerd Font 图标族(显式命名可使浏览器逐字符查询,两个平面都能命中)。
基础字体优先级链(用户 pref > 主题 > 内置栈)保持不变。

三条约束:
- 只追加不前置:xterm 用字体栈首项测量单元格宽度,基础字体须留在最前;
- 插入到第一个通用族(monospace 等)之前:通用族是 catch-all,其后的字体
  永不被查询,仅判断尾项会让主题给出的 `ui-monospace, Menlo, monospace, …`
  这类栈静默失效;
- 按族名去重(引号/大小写/空白不敏感),用户自列的字体保持原有优先级。

另外:
- 过滤 CSS 全局关键字(inherit/initial/unset/revert/revert-layer)。主题字体
  取自原始 token 值,皮肤确实会设成这类值,追加后会得到非法 font-family 被
  CSSOM 静默丢弃,导致主题字体与图标字体一起失效,现改为回退到下一级;
- 字体栈按顶层逗号切分并感知引号与括号,避免拆坏 "Foo,Bar" 与 var(--x, y);
- 不列彩色 emoji 字体:本问题的图标全为 PUA 且真 emoji 有独立回退路径,而
  彩色字体会捕获提示符依赖等宽的 BMP 符号(U+26A0、U+2714 等)破坏网格。

单测 12 → 19 例,补齐中间通用族回归守卫、CSS 全局关键字、多通用族、幂等性与
回退顺序契约;全量 594 例通过。
This commit is contained in:
ShelbyTang
2026-08-18 10:50:30 +08:00
parent 6ee1ba11d0
commit dd5a3dda07
3 changed files with 321 additions and 8 deletions
@@ -85,6 +85,12 @@ export function resolveTerminalFont(prefs, themeFontFamily):
- `TerminalView` 构造 Terminal 时用解析值;主 effect 内 `store.subscribe` 回调 diff `term.options.fontFamily/fontSize`,变化则更新 options + `fit.fit()` + `sendResize()`(网格尺寸随字体变化);cleanup 退订。懒加载 chunk 无新增依赖(store 为运行时 prop)。
> **实施偏差(2026-08-17)**:`resolveTerminalFont` 不再**逐字**返回胜出的基础字体,而是经 `withIconFontFallbacks()` 追加 Nerd Font 图标族后返回。
>
> 起因:starship / powerlevel10k 等提示符的图标取自 Nerd Font 私有区,Chromium 的隐式系统回退能覆盖 BMP PUA(`U+E000–U+F8FF`,如 `U+E0B0`)但覆盖不到补充平面 PUA-B(`U+F0000+`,Nerd Fonts v3 把 Material Design 图标集迁到了这里),导致后者渲染为豆腐块。显式列出字体族可使浏览器逐字符查询,两个平面都能命中。
>
> 三条约束:①**只追加不前置**——xterm 用字体栈首项测量单元格宽度;②插入到**第一个**通用族(`monospace` 等)之前——通用族是 catch-all,其后的字体永不被查询;③按族名去重(引号/大小写/空白不敏感),用户自列的字体保持其原有优先级。基础字体的优先级链(用户 pref > 主题 > 内置栈)不变,另新增 CSS 全局关键字(`inherit`/`initial`/`unset`/`revert`/`revert-layer`)过滤——这类值只在作为整条声明值时合法,追加后会得到非法 `font-family` 而被 CSSOM 静默丢弃。
### 4.6 i18n(`locales.ts`)
zh/en 各新增:`settingsFontFamilyTitle/Desc/Placeholder`、`settingsFontSizeTitle/Desc/Suffix`(px)。