From f15f4b0306e0e6906d06fac2a748d78d90ce825f Mon Sep 17 00:00:00 2001 From: Nate Woolls Date: Mon, 5 Oct 2026 21:20:12 -0400 Subject: [PATCH 1/3] Render Nerd Font prompt glyphs in the terminal - Give the terminal its own --font-terminal stack led by JetBrains Mono Nerd Font - Name common Nerd Fonts as fallbacks since WebKit skips user icon fonts for PUA glyphs - Leave --font-mono unchanged for the editor and other monospace UI - Add coverage that the terminal reads --font-terminal Co-Authored-By: Claude Opus 5.5 --- src/features/terminal/ui/TerminalView.test.ts | 25 +++++++++++++++++++ src/features/terminal/ui/TerminalView.tsx | 6 ++--- src/styles/index.css | 11 ++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/features/terminal/ui/TerminalView.test.ts b/src/features/terminal/ui/TerminalView.test.ts index 55b23251b..d2eccbfe1 100644 --- a/src/features/terminal/ui/TerminalView.test.ts +++ b/src/features/terminal/ui/TerminalView.test.ts @@ -12,6 +12,7 @@ const pty = vi.hoisted(() => ({ getPtyStatus: vi.fn(async () => ({ foreground: null })), })); vi.mock("../../../platform/tauri/pty", () => pty); +const xterm = vi.hoisted(() => ({ options: [] as { fontFamily?: string }[] })); vi.mock("../model/terminalLayout", () => ({ fitTerminal: () => null, applyTerminalChrome: () => {}, @@ -19,6 +20,9 @@ vi.mock("../model/terminalLayout", () => ({ })); vi.mock("@xterm/xterm", () => ({ Terminal: class { + constructor(options: { fontFamily?: string }) { + xterm.options.push(options); + } cols = 80; rows = 24; options = {}; @@ -44,6 +48,7 @@ vi.mock("@xterm/xterm", () => ({ import { TerminalView } from "./TerminalView"; afterEach(() => { + xterm.options.length = 0; vi.clearAllMocks(); vi.unstubAllGlobals(); }); @@ -176,3 +181,23 @@ it("does not hold a different terminal behind another one's teardown", async () host.remove(); } }); + +it("uses the terminal-specific font stack", async () => { + const { host, root } = setup(); + const stack = '"Test Nerd Font", monospace'; + document.documentElement.style.setProperty("--font-terminal", stack); + try { + await act(async () => { + root.render( + createElement(TerminalView, { id: "font", cwd: "/tmp", active: true }), + ); + }); + expect(xterm.options[0]?.fontFamily).toBe(stack); + } finally { + await act(async () => { + root.unmount(); + }); + host.remove(); + document.documentElement.style.removeProperty("--font-terminal"); + } +}); diff --git a/src/features/terminal/ui/TerminalView.tsx b/src/features/terminal/ui/TerminalView.tsx index 4a888c5bd..58ad22711 100644 --- a/src/features/terminal/ui/TerminalView.tsx +++ b/src/features/terminal/ui/TerminalView.tsx @@ -113,9 +113,9 @@ function terminalTheme(light: boolean) { }; } -function monoFont(): string { +function terminalFont(): string { const fromCss = getComputedStyle(document.documentElement) - .getPropertyValue("--font-mono") + .getPropertyValue("--font-terminal") .trim(); return fromCss || "ui-monospace, SFMono-Regular, Menlo, Monaco, monospace"; } @@ -163,7 +163,7 @@ export function TerminalView({ id, cwd, active, onMetaChange }: Props) { const term = new Terminal({ cursorBlink: true, cursorStyle: "bar", - fontFamily: monoFont(), + fontFamily: terminalFont(), fontSize: 13, lineHeight: 1, letterSpacing: 0, diff --git a/src/styles/index.css b/src/styles/index.css index d595b839c..fb70312b8 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -66,6 +66,17 @@ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* Terminal-only stack. JetBrains Mono Nerd Font leads so prompt text and + icons share one face; the Nerd Font families after the system monos fill + Private Use Area glyphs (powerline arrows, git/folder icons) because + WebKit's fallback never reaches user-installed icon fonts on its own. */ + --font-terminal: + "JetBrainsMono NFM", "JetBrainsMono NF", "JetBrainsMono Nerd Font Mono", + ui-monospace, SFMono-Regular, Menlo, Monaco, "Symbols Nerd Font Mono", + "Symbols Nerd Font", "MesloLGS NF", "MesloLGS Nerd Font Mono", + "FiraCode Nerd Font Mono", "Hack Nerd Font Mono", + "CaskaydiaCove Nerd Font Mono", Consolas, "Liberation Mono", "Courier New", + monospace; --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; From b7920ca5a6fa557a8a6c68456b77b299983fcc5e Mon Sep 17 00:00:00 2001 From: Nate Woolls Date: Mon, 5 Oct 2026 21:49:47 -0400 Subject: [PATCH 2/3] Prefer JetBrainsMono Nerd Font Mono over the proportional NF variant Co-Authored-By: Claude Opus 5.5 --- src/styles/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/index.css b/src/styles/index.css index fb70312b8..5f58fec56 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -71,7 +71,7 @@ Private Use Area glyphs (powerline arrows, git/folder icons) because WebKit's fallback never reaches user-installed icon fonts on its own. */ --font-terminal: - "JetBrainsMono NFM", "JetBrainsMono NF", "JetBrainsMono Nerd Font Mono", + "JetBrainsMono NFM", "JetBrainsMono Nerd Font Mono", "JetBrainsMono NF", ui-monospace, SFMono-Regular, Menlo, Monaco, "Symbols Nerd Font Mono", "Symbols Nerd Font", "MesloLGS NF", "MesloLGS Nerd Font Mono", "FiraCode Nerd Font Mono", "Hack Nerd Font Mono", From 363949c9737c20b951f4f09fd96d17b7b5ba2c30 Mon Sep 17 00:00:00 2001 From: Nate Woolls Date: Mon, 5 Oct 2026 22:23:03 -0400 Subject: [PATCH 3/3] Keep the terminal font stack in emitted CSS Only xterm reads --font-terminal, from JS, so Tailwind could tree-shake it out of the build. Declare it in an @theme static block. Co-Authored-By: Claude Opus 5.5 --- src/styles/index.css | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/styles/index.css b/src/styles/index.css index 5f58fec56..244d1865d 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -66,17 +66,6 @@ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* Terminal-only stack. JetBrains Mono Nerd Font leads so prompt text and - icons share one face; the Nerd Font families after the system monos fill - Private Use Area glyphs (powerline arrows, git/folder icons) because - WebKit's fallback never reaches user-installed icon fonts on its own. */ - --font-terminal: - "JetBrainsMono NFM", "JetBrainsMono Nerd Font Mono", "JetBrainsMono NF", - ui-monospace, SFMono-Regular, Menlo, Monaco, "Symbols Nerd Font Mono", - "Symbols Nerd Font", "MesloLGS NF", "MesloLGS Nerd Font Mono", - "FiraCode Nerd Font Mono", "Hack Nerd Font Mono", - "CaskaydiaCove Nerd Font Mono", Consolas, "Liberation Mono", "Courier New", - monospace; --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; @@ -110,6 +99,21 @@ ); } +/* Static so Tailwind always emits it: only xterm reads it, via JS. */ +@theme static { + /* Terminal-only stack. JetBrains Mono Nerd Font leads so prompt text and + icons share one face; the Nerd Font families after the system monos fill + Private Use Area glyphs (powerline arrows, git/folder icons) because + WebKit's fallback never reaches user-installed icon fonts on its own. */ + --font-terminal: + "JetBrainsMono NFM", "JetBrainsMono Nerd Font Mono", "JetBrainsMono NF", + ui-monospace, SFMono-Regular, Menlo, Monaco, "Symbols Nerd Font Mono", + "Symbols Nerd Font", "MesloLGS NF", "MesloLGS Nerd Font Mono", + "FiraCode Nerd Font Mono", "Hack Nerd Font Mono", + "CaskaydiaCove Nerd Font Mono", Consolas, "Liberation Mono", "Courier New", + monospace; +} + :root { --motion-reorder-duration: 160ms; --motion-tab-close-duration: 200ms;