feat: bundled Nerd Font symbols fallback + per-device terminal font setting

Shell prompts using Nerd Font glyphs (powerline, p10k/starship folder and
git icons) rendered as missing-glyph boxes: the built-in xterm stack has no
private-use-area symbols, and phones have no Nerd Fonts installed at all.

- Bundle Symbols Nerd Font Mono (icons-only, MIT, 1.2MB woff2) served from
  fonts/ and appended to the terminal stack before monospace — browsers fall
  back per glyph, so icons render everywhere while text stays in the text
  fonts. font-display: block + preload keep tofu out of xterm's glyph atlas.
- New per-device terminalFontFamily setting (App Settings > Terminal &
  Input > Font): prepended to the built-in stack, never a replacement, so
  the symbols fallback and final monospace always survive. Applied live on
  save (refit + echo-overlay refreshFont, mirroring setFontSize).
- Single source for both xterm surfaces: TERMINAL_FONT_DEFAULT_STACK +
  resolveTerminalFontFamily() in constants.js, unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VjnbbZRBuvR5E3SDouwXr9
This commit is contained in:
Tobias Weber
2026-08-19 00:45:19 +02:00
co-authored by Claude Fable 5
parent 09bf00c815
commit 108c00e78d
10 changed files with 176 additions and 2 deletions
+16
View File
@@ -24,6 +24,9 @@
<!-- Preload critical resources — lets browser discover these during HTML parse
instead of waiting until <script> tags at bottom-of-body are reached. -->
<link rel="preload" href="vendor/xterm.min.js" as="script">
<!-- Symbols font before the terminal first paints — a late-loading icon font
leaves tofu in xterm's glyph atlas until something forces a re-render. -->
<link rel="preload" href="fonts/symbols-nerd-font-mono.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="constants.js" as="script">
<link rel="preload" href="app.js" as="script">
<!-- Self-hosted xterm.js — eliminates CDN DNS/TLS latency (~100ms).
@@ -1624,6 +1627,19 @@
</div>
</div>
<div class="set-group">
<div class="set-group-head"><h4>Font</h4><span class="set-scope">device</span></div>
<div class="set-group-body">
<div class="set-row has-field" data-search="terminal font family nerd custom typeface">
<div class="set-row-text">
<span class="set-row-label">Terminal font</span>
<span class="set-row-desc">Prepended to the built-in stack, so fallbacks (including bundled Nerd Font symbols) keep working. Must be installed on this device. Leave empty for the default.</span>
</div>
<input type="text" id="appSettingsTerminalFont" class="set-input" placeholder='e.g. JetBrainsMono Nerd Font'>
</div>
</div>
</div>
<div class="set-group">
<div class="set-group-head"><h4>Scrolling &amp; rendering</h4></div>
<div class="set-group-body">