Skip to content

fix: terminal font dont always load#2155

Merged
deadlyjack merged 4 commits into
mainfrom
ajit/fix-terminal-font
Jun 8, 2026
Merged

fix: terminal font dont always load#2155
deadlyjack merged 4 commits into
mainfrom
ajit/fix-terminal-font

Conversation

@deadlyjack
Copy link
Copy Markdown
Member

No description provided.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 5, 2026

Greptile Summary

This PR fixes intermittent terminal font loading by splitting the process into two phases: injectFontFace eagerly injects the raw @font-face CSS so the browser can start preloading immediately, while loadFont downloads the font file, converts the URL to a local content:// URI, and replaces the injected block — ensuring the cached copy is used. The terminal init path now fires loadTerminalFont as a non-blocking background task and re-applies the font family once it resolves, with a safety rAF in openTerminal to force correct metrics even on first paint.

  • src/lib/fonts.js: New injectFontFace function for synchronous CSS pre-injection; loadFont updated to replace (not just append) the pre-injected block using a regex replacement so the locally-cached URI wins over the remote URL.
  • src/components/terminal/terminal.js / src/settings/terminalSettings.js: Both now call injectFontFace before loadFont, and terminal mount adds a second requestAnimationFrame to re-apply fontFamily after the first render pass.
  • www/cordova.js / www/cordova_plugins.js: Platform-specific Cordova runtime files (cordova-android v15) are committed as new files — worth confirming these are intentionally tracked and not generated artifacts that belong in .gitignore.

Confidence Score: 5/5

Safe to merge; the font loading fix is well-structured and all previously flagged null-guard issues are addressed

The core logic change — eager injection followed by local-URI replacement — is correct for all built-in fonts. The only gap is that custom fonts with regex metacharacters in their name would silently skip the local-URI replacement, but this doesn't affect any currently shipped font and the font still loads (just from the remote URL). The cordova.js additions are standard Cordova runtime files and don't introduce logic risk.

src/lib/fonts.js — the regex replacement in loadFont is unguarded against metacharacters in font names; worth a one-line fix before the pattern is relied upon for more custom-font scenarios

Important Files Changed

Filename Overview
src/lib/fonts.js Adds injectFontFace for eager CSS injection and updates loadFont to replace the pre-injected block with the locally-cached URI; the regex used for replacement doesn't escape the font name, which silently misfires for custom fonts whose names contain regex metacharacters
src/components/terminal/terminal.js Font load is now deferred via _fontReady promise with proper null-guards; adds a second rAF after mount to re-apply fontFamily once the frame is ready; DEFAULT_TERMINAL_SETTINGS import is used for AXS session initialization
src/main.js Calls fonts.injectFontFace("MesloLGS NF Regular") at app startup to pre-register the default terminal font CSS so it is immediately available to the browser before any terminal is opened
src/settings/terminalSettings.js Adds fonts.injectFontFace(value) before loadFont in the fontFamily settings handler, consistent with the new two-phase loading pattern
www/cordova.js Adds platform-specific Cordova JS runtime (cordova-android, v15.0.0) as a new committed file; this is a generated file typically replaced per-platform during a Cordova build
www/cordova_plugins.js Adds the Cordova plugin registry stub; like cordova.js, this is ordinarily generated during platform build rather than committed directly

Sequence Diagram

sequenceDiagram
    participant App as main.js (onDeviceReady)
    participant TC as TerminalComponent.init()
    participant LTF as loadTerminalFont()
    participant Fonts as fonts.js
    participant Style as DOM style
    participant Browser as document.fonts

    App->>Fonts: injectFontFace("MesloLGS NF Regular")
    Fonts->>Style: "append @font-face (bundled data URI)"
    Fonts->>Browser: document.fonts.load() no-await

    Note over TC: Terminal opened
    TC->>LTF: loadTerminalFont() async non-blocking
    LTF->>Fonts: injectFontFace(fontFamily)
    Fonts->>Style: "append @font-face remote URL if not present"
    Fonts->>Browser: document.fonts.load() no-await
    LTF->>Fonts: await loadFont(fontFamily)
    Fonts->>Fonts: download remote font file to local URI
    Fonts->>Style: "replace remote @font-face with local-URI version"
    Fonts->>Browser: await document.fonts.load()
    LTF-->>TC: resolved

    TC->>TC: _fontReady.then terminal.options.fontFamily
    TC->>TC: terminal.refresh(0, rows-1)

    Note over TC: openTerminal() mount()
    TC->>TC: rAF 1 fitAddon.fit() terminal.focus()
    TC->>TC: rAF 2 safety re-apply fontFamily and refresh
Loading

Reviews (2): Last reviewed commit: "fix: replace pre-injected font-face with..." | Re-trigger Greptile

Comment thread src/components/terminal/terminal.js
Comment thread src/lib/fonts.js
Comment on lines +257 to +272
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(() => {});
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

deadlyjack and others added 2 commits June 5, 2026 20:57
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…n terminal mount

- loadFont now replaces stale @font-face blocks (pre-injected by
  injectFontFace with remote URLs) with locally-cached URIs, fixing
  offline loading regression for remote fonts like JetBrains Mono
  and Cascadia Code
- add if (!this.terminal) null guard to mount()'s first rAF and
  setTimeout callbacks to prevent crashes on early terminal dispose
@deadlyjack
Copy link
Copy Markdown
Member Author

@greptile_apps review again

@deadlyjack deadlyjack merged commit 99266b4 into main Jun 8, 2026
9 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in The Code Board - Acode Jun 8, 2026
@deadlyjack deadlyjack deleted the ajit/fix-terminal-font branch June 8, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant