diff --git a/src/components/terminal/terminal.js b/src/components/terminal/terminal.js index 642904664..726b01bd1 100644 --- a/src/components/terminal/terminal.js +++ b/src/components/terminal/terminal.js @@ -21,7 +21,10 @@ import confirm from "dialogs/confirm"; import fonts from "lib/fonts"; import appSettings from "lib/settings"; import LigaturesAddon from "./ligatures"; -import { getTerminalSettings } from "./terminalDefaults"; +import { + DEFAULT_TERMINAL_SETTINGS, + getTerminalSettings, +} from "./terminalDefaults"; import TerminalThemeManager from "./terminalThemeManager"; import TerminalTouchSelection from "./terminalTouchSelection"; @@ -103,8 +106,13 @@ export default class TerminalComponent { this.loadImageAddon(); } - // Load font if specified - this.loadTerminalFont(); + // Load font in background - apply when ready without blocking render + this._fontReady = this.loadTerminalFont().then(() => { + if (this.terminal) { + this.terminal.options.fontFamily = this.options.fontFamily; + this.terminal.refresh(0, this.terminal.rows - 1); + } + }); // Set up terminal event handlers this.setupEventHandlers(); @@ -570,6 +578,24 @@ export default class TerminalComponent { this.setupTouchSelection(); }, 0); } + + // Safety: re-apply fontFamily on next frame to ensure xterm + // uses correct metrics even if font wasn't ready for first paint + if (typeof requestAnimationFrame === "function") { + requestAnimationFrame(() => { + if (this.terminal) { + this.terminal.options.fontFamily = this.options.fontFamily; + this.terminal.refresh(0, this.terminal.rows - 1); + } + }); + } else { + setTimeout(() => { + if (this.terminal) { + this.terminal.options.fontFamily = this.options.fontFamily; + this.terminal.refresh(0, this.terminal.rows - 1); + } + }, 16); + } } catch (error) { console.error("Failed to mount terminal:", error); } @@ -1002,6 +1028,7 @@ export default class TerminalComponent { const fontFamily = this.options.fontFamily; if (fontFamily && fonts.get(fontFamily)) { try { + fonts.injectFontFace(fontFamily); await fonts.loadFont(fontFamily); } catch (error) { console.warn(`Failed to load terminal font ${fontFamily}:`, error); diff --git a/src/lib/fonts.js b/src/lib/fonts.js index c7f5faa0f..653ee6771 100644 --- a/src/lib/fonts.js +++ b/src/lib/fonts.js @@ -254,6 +254,23 @@ async function downloadFont(name, link) { return FONT_FILE; } +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(() => {}); + } +} + async function loadFont(name) { const $style = ensureStyleElement(FONT_FACE_STYLE_ID); let css = get(name); @@ -279,6 +296,15 @@ async function loadFont(name) { $style.textContent = `${$style.textContent}\n${css}`; } + // Ensure the browser has actually parsed and loaded the font + if (document.fonts?.load) { + try { + await document.fonts.load(`12px '${name}'`); + } catch { + // document.fonts.load may reject if font is unavailable + } + } + return css; } @@ -303,4 +329,5 @@ export default { setEditorFont, setAppFont, loadFont, + injectFontFace, }; diff --git a/src/main.js b/src/main.js index f18cae9bb..c54a27a17 100644 --- a/src/main.js +++ b/src/main.js @@ -43,6 +43,7 @@ import config from "lib/config"; import EditorFile from "lib/editorFile"; import EditorManager from "lib/editorManager"; import { initFileList } from "lib/fileList"; +import fonts from "lib/fonts"; import lang from "lib/lang"; import loadPlugins from "lib/loadPlugins"; import Logger from "lib/logger"; @@ -254,6 +255,9 @@ async function onDeviceReady() { themes.init(); initHighlighting(); + // Inject default terminal font face early so browser preloads it + fonts.injectFontFace("MesloLGS NF Regular"); + registerPrettierFormatter(); acode.setLoadingMessage("Loading language..."); diff --git a/src/settings/terminalSettings.js b/src/settings/terminalSettings.js index b33f1d44b..505b2204b 100644 --- a/src/settings/terminalSettings.js +++ b/src/settings/terminalSettings.js @@ -391,6 +391,7 @@ export async function updateActiveTerminals(key, value) { case "fontFamily": // Load font if it's not already loaded try { + fonts.injectFontFace(value); await fonts.loadFont(value); } catch (error) { console.warn(`Failed to load font ${value}:`, error);