fix: terminal font dont always load#2155
Conversation
Greptile SummaryThis PR addresses terminal fonts not reliably loading by introducing a two-phase approach:
Confidence Score: 3/5The fix improves font loading for bundled fonts but introduces a regression for remote fonts and a crash path if the terminal is disposed quickly. Two concrete defects are present on the changed path: the requestAnimationFrame callbacks in mount() access this.terminal without a null guard (the _fontReady.then() handler added in the same PR already protects itself), and injectFontFace injecting raw remote-URL CSS before loadFont prevents loadFont from ever substituting the locally-cached URI, silently breaking offline support for non-bundled fonts like JetBrains Mono or Cascadia Code. src/lib/fonts.js (remote-URL injection ordering) and src/components/terminal/terminal.js (missing null guard in mount's rAF callbacks). Important Files Changed
Sequence DiagramsequenceDiagram
participant App as main.js
participant Term as TerminalComponent
participant Fonts as fonts.js
participant Browser as Browser FontFaceSet
App->>Fonts: injectFontFace(MesloLGS NF Regular)
Fonts->>Browser: "inject @font-face CSS (bundled URL)"
Fonts-->>Browser: document.fonts.load() fire and forget
App->>Term: new TerminalComponent()
Term->>Term: init()
Term->>Fonts: injectFontFace(fontFamily)
Fonts->>Browser: "inject @font-face CSS (raw remote URL)"
Fonts-->>Browser: document.fonts.load() fire and forget
Term->>Fonts: loadFont(fontFamily) await
Fonts->>Fonts: downloadFont and replace remote URL with local URI
Note over Fonts: Skip injection - font-family already in style!
Fonts->>Browser: await document.fonts.load()
Browser-->>Fonts: resolved
Fonts-->>Term: returns
Term->>Term: _fontReady.then() apply fontFamily refresh guarded
Term->>Term: mount()
Term->>Browser: requestAnimationFrame
Browser-->>Term: frame fires apply fontFamily refresh no null check
Reviews (1): Last reviewed commit: "fix: terminal font dont always load" | Re-trigger Greptile |
| function injectFontFace(name) { | ||
| const $style = ensureStyleElement(FONT_FACE_STYLE_ID); | ||
| const css = get(name); | ||
|
|
||
| if (!css) return; | ||
|
|
||
| // Inject CSS if not already present (skip remote URL downloads - loadFont handles those) | ||
| if (!$style.textContent.includes(`font-family: '${name}'`)) { | ||
| $style.textContent = `${$style.textContent}\n${css}`; | ||
| } | ||
|
|
||
| // Kick off browser font loading without blocking | ||
| if (document.fonts?.load) { | ||
| document.fonts.load(`12px '${name}'`).catch(() => {}); | ||
| } | ||
| } |
There was a problem hiding this comment.
Remote fonts bypass local caching when
injectFontFace is called before loadFont
injectFontFace injects the raw @font-face CSS (containing the original https://acode.app/… URL) into the style element. When loadFont is subsequently called in loadTerminalFont() or terminalSettings.js, it downloads the font file to local storage and replaces the remote URL with an internal URI (content://…) — but then the "already present" guard ($style.textContent.includes(...)) is true, so the locally-URI'd CSS is never injected. The browser is left with the remote URL and the locally cached copy is never served.
For locally bundled fonts (like MesloLGS NF Regular) this is harmless, but for any remote font (e.g., JetBrains Mono Regular, Cascadia Code) this breaks offline/cached font loading — a regression from the pre-PR behaviour where only loadFont ran and always replaced the URL before injecting.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
No description provided.