diff --git a/src/web/public/constants.js b/src/web/public/constants.js index 20df330a..4a126e81 100644 --- a/src/web/public/constants.js +++ b/src/web/public/constants.js @@ -527,6 +527,41 @@ function sortSessionsByActivity(rows) { return (Array.isArray(rows) ? rows.slice() : []).sort(compareSessionActivity); } +// Terminal font stack — the single source for every xterm surface (the main +// terminal in terminal-ui.js, the log-viewer terminal in panels-ui.js). +// "Symbols Nerd Font Mono" is a bundled icons-only webfont (fonts/ + +// @font-face in styles.css): browsers fall back PER GLYPH, so Nerd Font +// prompt icons (powerline segments, folder/git glyphs from p10k, starship, +// oh-my-posh) render even though the text fonts carry no private-use-area +// symbols — while all readable text keeps coming from the text fonts. +const TERMINAL_FONT_DEFAULT_STACK = + '"Fira Code", "Cascadia Code", "JetBrains Mono", "SF Mono", Monaco, "Symbols Nerd Font Mono", monospace'; + +/** + * Resolve the xterm fontFamily from the per-device `terminalFontFamily` + * setting. A user-set family (or comma-separated list) is PREPENDED to the + * built-in stack, never a replacement — the symbols fallback and a final + * `monospace` must survive whatever the user types. Blank input yields the + * default. Unquoted names that need quoting for CSS (spaces, digits leading, + * etc.) are quoted; embedded quotes are stripped rather than escaped, since + * a font name cannot contain them anyway. + */ +function resolveTerminalFontFamily(custom) { + const raw = typeof custom === 'string' ? custom.trim() : ''; + if (!raw) return TERMINAL_FONT_DEFAULT_STACK; + const families = raw + .split(',') + .map((f) => f.trim().replace(/^["']|["']$/g, '').replace(/["']/g, '').trim()) + .filter(Boolean) + // Drop generic families the user may append — the default stack already + // ends in `monospace`, and a duplicate earlier entry would shadow the + // symbols fallback behind it. + .filter((f) => !/^(monospace|serif|sans-serif|system-ui)$/i.test(f)) + .map((f) => (/^[A-Za-z][A-Za-z0-9-]*$/.test(f) ? f : `"${f}"`)); + if (!families.length) return TERMINAL_FONT_DEFAULT_STACK; + return `${families.join(', ')}, ${TERMINAL_FONT_DEFAULT_STACK}`; +} + if (typeof window !== 'undefined') { window.WEBGL_FALLBACK = WEBGL_FALLBACK; window.evaluateWebGLLongTaskTrip = evaluateWebGLLongTaskTrip; @@ -560,6 +595,10 @@ if (typeof window !== 'undefined') { compare: compareSessionActivity, sort: sortSessionsByActivity, }; + window.CodemanTerminalFont = { + DEFAULT_STACK: TERMINAL_FONT_DEFAULT_STACK, + resolve: resolveTerminalFontFamily, + }; } // Scheduler API — prioritize terminal writes over background UI updates. diff --git a/src/web/public/fonts/LICENSE-nerd-fonts.txt b/src/web/public/fonts/LICENSE-nerd-fonts.txt new file mode 100644 index 00000000..06eb073d --- /dev/null +++ b/src/web/public/fonts/LICENSE-nerd-fonts.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Ryan L McIntyre + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/web/public/fonts/symbols-nerd-font-mono.woff2 b/src/web/public/fonts/symbols-nerd-font-mono.woff2 new file mode 100644 index 00000000..4762129c Binary files /dev/null and b/src/web/public/fonts/symbols-nerd-font-mono.woff2 differ diff --git a/src/web/public/i18n.js b/src/web/public/i18n.js index 4c2b391d..a321b1a9 100644 --- a/src/web/public/i18n.js +++ b/src/web/public/i18n.js @@ -326,6 +326,10 @@ // Input settings Input: '输入', + Font: '字体', + 'Terminal font': '终端字体', + '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.': + '置于内置字体栈之前,回退字体(包括内置的 Nerd Font 图标)仍然生效。需已安装在本设备上。留空使用默认值。', 'Local Echo': '本地回显', 'CJK Input': '中日韩输入', 'Extended Keyboard Bar': '扩展键盘栏', diff --git a/src/web/public/index.html b/src/web/public/index.html index cdb3110f..480d287f 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -24,6 +24,9 @@ + +