Compare commits

...
Author SHA1 Message Date
bincxz 25f4a4ec30 fix(terminal): alias diagnostics_channel in Electron live bundles
The background-rendering live test bundles the terminal with esbuild,
which does not use the Vite alias. The ligatures addon then fails to
resolve node:diagnostics_channel, and Electron keeps that process alive
until the job timeout. Point the import at the renderer stub and exit
if the bundle fails.
2026-09-29 17:21:24 +08:00
bincxz a17186beb1 fix(terminal): bundle ligatures without diagnostics_channel
The published addon pulls lru-cache's node:diagnostics_channel import
into the renderer. Vite externalizes that builtin and the package build
fails, so alias it to a channel that never publishes metrics.

Settings updates call syncFontLigatures on the current runtime. The
cursor lifecycle test used a partial runtime and threw, which failed
lint-and-test. Give that double the method.
2026-09-29 16:47:37 +08:00
bincxz ae171a5408 fix(terminal): enable font ligatures from the terminal setting
fontLigatures already defaulted to on, but the terminal never loaded
the ligatures addon, so Fira Code and Maple Mono kept drawing != as two
cells. Activate the addon after open and before WebGL, and rebuild the
WebGL atlas when the setting changes so glyphs are not rasterized
without the calt feature.

Closes #3543
2026-09-29 16:33:48 +08:00
18 changed files with 409 additions and 20 deletions

No files matched your search

+2
View File
@@ -614,6 +614,8 @@ Highlight the focused split pane:
'settings.terminal.font.weightBold.desc': 'Weight for bold text (100-900)',
'settings.terminal.font.smoothing': 'Font smoothing',
'settings.terminal.font.smoothing.desc': 'Use native macOS font anti-aliasing',
'settings.terminal.font.ligatures': 'Font ligatures',
'settings.terminal.font.ligatures.desc': 'Render ligatures such as != when the terminal font includes them',
'settings.terminal.font.linePadding': 'Line padding',
'settings.terminal.font.linePadding.desc': 'Additional space between lines (0-10)',
'settings.terminal.font.emulationType': 'Terminal emulation type',
+2
View File
@@ -529,6 +529,8 @@ Resalta el panel dividido enfocado:
'settings.terminal.font.weightBold.desc': 'Peso del texto en negrita (100-900)',
'settings.terminal.font.smoothing': 'Suavizado de fuente',
'settings.terminal.font.smoothing.desc': 'Usar el anti-aliasing nativo de fuentes de macOS',
'settings.terminal.font.ligatures': 'Ligaduras de fuente',
'settings.terminal.font.ligatures.desc': 'Mostrar ligaduras como != cuando la fuente del terminal las incluye',
'settings.terminal.font.linePadding': 'Espaciado de líneas',
'settings.terminal.font.linePadding.desc': 'Espacio adicional entre líneas (0-10)',
'settings.terminal.font.emulationType': 'Tipo de emulación de terminal',
+2
View File
@@ -582,6 +582,8 @@ export const ruCoreMessages: Messages = {
'settings.terminal.font.weightBold.desc': 'Толщина жирного текста (100-900)',
'settings.terminal.font.smoothing': 'Сглаживание шрифта',
'settings.terminal.font.smoothing.desc': 'Использовать родное сглаживание шрифтов macOS',
'settings.terminal.font.ligatures': 'Лигатуры шрифта',
'settings.terminal.font.ligatures.desc': 'Показывать лигатуры вроде !=, если шрифт терминала их содержит',
'settings.terminal.font.linePadding': 'Межстрочный отступ',
'settings.terminal.font.linePadding.desc': 'Дополнительное пространство между строками (0-10)',
'settings.terminal.font.emulationType': 'Тип эмуляции терминала',
@@ -278,6 +278,8 @@ export const zhCNTerminalMessages: Messages = {
'settings.terminal.font.weightBold.desc': '粗体文本字重 (100-900)',
'settings.terminal.font.smoothing': '字体平滑',
'settings.terminal.font.smoothing.desc': '使用 macOS 原生字体抗锯齿',
'settings.terminal.font.ligatures': '字体连字',
'settings.terminal.font.ligatures.desc': '支持连字的字体把 != 这类字符显示成一个符号',
'settings.terminal.font.linePadding': '行间距',
'settings.terminal.font.linePadding.desc': '行之间的额外间距 (0-10)',
'settings.terminal.font.emulationType': '终端仿真类型',
@@ -278,6 +278,8 @@ export const zhTWTerminalMessages: Messages = {
'settings.terminal.font.weightBold.desc': '粗體文字字重 (100-900)',
'settings.terminal.font.smoothing': '字型平滑',
'settings.terminal.font.smoothing.desc': '使用 macOS 原生字型抗鋸齒',
'settings.terminal.font.ligatures': '字型連字',
'settings.terminal.font.ligatures.desc': '支援連字的字型把 != 這類字元顯示成一個符號',
'settings.terminal.font.linePadding': '行間距',
'settings.terminal.font.linePadding.desc': '行之間的額外間距 (0-10)',
'settings.terminal.font.emulationType': '終端模擬型別',
@@ -602,6 +602,17 @@ function SettingsTerminalTab(props: {
/>
</SettingRow>
<SettingRow
anchorId="terminal-font-ligatures"
label={t("settings.terminal.font.ligatures")}
description={t("settings.terminal.font.ligatures.desc")}
>
<Toggle
checked={terminalSettings.fontLigatures}
onChange={(v) => updateTerminalSetting("fontLigatures", v)}
/>
</SettingRow>
<SettingRow
anchorId="terminal-font-line-padding"
label={t("settings.terminal.font.linePadding")}
@@ -3,6 +3,7 @@ import { stringCellWidth } from "../autocomplete/terminalStringCellWidth";
import type { TerminalBroadcastInputOptions } from "../terminalHelpers";
import { FitAddon } from "@xterm/addon-fit";
import { ImageAddon } from "@xterm/addon-image";
import { LigaturesAddon } from "@xterm/addon-ligatures";
import { SearchAddon } from "@xterm/addon-search";
import { SerializeAddon } from "@xterm/addon-serialize";
import { UnicodeGraphemesAddon } from "@xterm/addon-unicode-graphemes";
@@ -99,6 +100,10 @@ import { optionYankLastArgSequence } from "./optionYankLastArg";
import { watchDevicePixelRatio } from "./rendererDprWatch";
import { dispatchWin32InputModeEvent } from "./win32InputMode";
import { shouldDeferWebglUntilVisible } from "./webglRendererPolicy";
import {
createTerminalLigatureController,
terminalFontLigaturesEnabled,
} from "./terminalFontLigatures";
import { createWebglRendererController } from "./webglRendererController";
import {
captureMiddleClickTerminalMouseEvent,
@@ -277,6 +282,11 @@ export type XTermRuntime = {
ensureWebglRenderer: () => void;
/** Drop the WebGL addon while keeping the terminal alive (soft-hide). */
suspendWebglRenderer: () => void;
/**
* Turn programming ligatures on or off. An active WebGL renderer is rebuilt
* because its glyph atlas captures font-feature-settings only at creation.
*/
syncFontLigatures: (enabled: boolean) => void;
/**
* True while this terminal holds decoded inline images (Kitty / SIXEL / IIP).
* Hibernate snapshots are text-only, so a session that reports true must not
@@ -850,6 +860,26 @@ export const createXTermRuntime = (ctx: CreateXTermRuntimeContext): XTermRuntime
});
const loadWebglRenderer = webglController.ensure;
const suspendWebglRenderer = webglController.suspend;
const reloadWebglRenderer = () => {
if (!webglLoaded) return;
suspendWebglRenderer();
loadWebglRenderer();
};
// The ligatures addon sets font-feature-settings on the terminal element.
// WebGL's glyph atlas inherits that only when the atlas is created, so this
// has to run before the first WebGL load. Later toggles rebuild WebGL.
const ligatures = createTerminalLigatureController({
createAddon: () => new LigaturesAddon(),
loadAddon: (addon) => term.loadAddon(addon),
isWebglActive: () => webglLoaded,
recreateWebgl: reloadWebglRenderer,
repaint: repaintTerminal,
warn: (message, error) => {
if (error === undefined) logger.warn(message);
else logger.warn(message, error);
},
});
ligatures.apply(terminalFontLigaturesEnabled(settings));
if (!performanceConfig.useWebGLAddon) {
logger.info(
@@ -3325,6 +3355,9 @@ export const createXTermRuntime = (ctx: CreateXTermRuntimeContext): XTermRuntime
clearTextureAtlas: clearWebglTextureAtlas,
ensureWebglRenderer: loadWebglRenderer,
suspendWebglRenderer,
syncFontLigatures: (enabled: boolean) => {
ligatures.apply(enabled);
},
hasInlineImages,
resetKittyConnectionInputState: clearKittyConnectionInputState,
flushKittyKeyboardReleases: clearKittyTransientInputState,
@@ -3344,6 +3377,7 @@ export const createXTermRuntime = (ctx: CreateXTermRuntimeContext): XTermRuntime
pendingLinePastes.clear();
clearTerminalBroadcastUserInput(ctx.sessionId);
resizeScheduler.dispose();
ligatures.dispose();
webglController.dispose();
term.element?.removeEventListener("copy", handleNativeCopy, true);
ctx.container.removeEventListener("copy", handleNativeCopy, true);
@@ -0,0 +1,149 @@
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import { LigaturesAddon } from "@xterm/addon-ligatures";
import {
createTerminalLigatureController,
terminalFontLigaturesEnabled,
type TerminalLigatureAddon,
} from "./terminalFontLigatures";
class FakeAddon implements TerminalLigatureAddon {
disposeCalls = 0;
dispose() {
this.disposeCalls += 1;
}
}
function createHarness(webglActive = false) {
const events: string[] = [];
const addons: FakeAddon[] = [];
const warnings: string[] = [];
let failLoad = false;
const controller = createTerminalLigatureController({
createAddon: () => {
const addon = new FakeAddon();
addons.push(addon);
events.push("create");
return addon;
},
loadAddon: () => {
events.push("load");
if (failLoad) throw new Error("load failed");
},
isWebglActive: () => webglActive,
recreateWebgl: () => {
events.push("recreate");
},
repaint: () => {
events.push("repaint");
},
warn: (message) => warnings.push(message),
});
return {
controller,
events,
addons,
warnings,
setWebglActive(active: boolean) {
webglActive = active;
},
failNextLoad() {
failLoad = true;
},
};
}
test("the ligatures addon can be constructed for this xterm version", () => {
const addon = new LigaturesAddon();
assert.equal(typeof addon.activate, "function");
assert.equal(typeof addon.dispose, "function");
addon.dispose();
});
test("font ligatures follow the terminal setting and default on", () => {
assert.equal(terminalFontLigaturesEnabled(undefined), true);
assert.equal(terminalFontLigaturesEnabled({}), true);
assert.equal(terminalFontLigaturesEnabled({ fontLigatures: true }), true);
assert.equal(terminalFontLigaturesEnabled({ fontLigatures: false }), false);
});
test("enabling ligatures before WebGL exists does not rebuild the renderer", () => {
const harness = createHarness(false);
harness.controller.apply(false);
assert.deepEqual(harness.events, []);
harness.controller.apply(true);
assert.deepEqual(harness.events, ["create", "load", "repaint"]);
harness.controller.apply(true);
assert.deepEqual(harness.events, ["create", "load", "repaint"]);
});
test("toggling ligatures rebuilds an active WebGL renderer after the addon updates", () => {
const harness = createHarness(true);
harness.controller.apply(true);
assert.deepEqual(harness.events, ["create", "load", "recreate", "repaint"]);
harness.controller.apply(false);
assert.equal(harness.addons[0]?.disposeCalls, 1);
assert.deepEqual(harness.events, [
"create",
"load",
"recreate",
"repaint",
"recreate",
"repaint",
]);
});
test("a failed ligature load leaves WebGL untouched so the terminal can still render", () => {
const harness = createHarness(true);
harness.failNextLoad();
harness.controller.apply(true);
assert.equal(harness.addons[0]?.disposeCalls, 1);
assert.deepEqual(harness.events, ["create", "load"]);
assert.deepEqual(harness.warnings, ["[XTerm] Ligatures addon failed to load"]);
});
test("the terminal runtime activates ligatures before the first WebGL load", () => {
const runtimeSource = readFileSync(new URL("./createXTermRuntime.ts", import.meta.url), "utf8");
const effectsSource = readFileSync(new URL("../useTerminalEffects.ts", import.meta.url), "utf8");
const settingsSource = readFileSync(
new URL("../../settings/tabs/SettingsTerminalTab.tsx", import.meta.url),
"utf8",
);
const applyIndex = runtimeSource.indexOf(
"ligatures.apply(terminalFontLigaturesEnabled(settings))",
);
const firstWebglLoad = runtimeSource.indexOf("loadWebglRenderer()", applyIndex);
assert.notEqual(applyIndex, -1);
assert.notEqual(firstWebglLoad, -1);
assert.ok(
firstWebglLoad > applyIndex,
"font-feature-settings must be on the terminal element before WebGL creates its glyph atlas",
);
const disposeBodyStart = runtimeSource.indexOf("dispose: () => {");
const disposeBody = runtimeSource.slice(disposeBodyStart);
const ligatureDispose = disposeBody.indexOf("ligatures.dispose()");
const termDispose = disposeBody.indexOf("term.dispose()");
assert.notEqual(ligatureDispose, -1);
assert.ok(ligatureDispose < termDispose, "release the joiner before xterm tears down the element");
assert.match(
effectsSource,
/syncFontLigatures\(\s*terminalFontLigaturesEnabled\(terminalSettings\),\s*\)/,
);
assert.match(settingsSource, /updateTerminalSetting\("fontLigatures", v\)/);
});
@@ -0,0 +1,82 @@
export interface TerminalLigatureAddon {
dispose(): void;
}
export function terminalFontLigaturesEnabled(
settings: { fontLigatures?: boolean } | null | undefined,
): boolean {
return settings?.fontLigatures !== false;
}
interface TerminalLigatureControllerOptions {
createAddon: () => TerminalLigatureAddon;
loadAddon: (addon: TerminalLigatureAddon) => void;
isWebglActive: () => boolean;
recreateWebgl: () => void;
repaint: () => void;
warn?: (message: string, error?: unknown) => void;
}
/**
* Owns the ligatures addon for one terminal.
*
* The addon writes `font-feature-settings` onto the terminal element. WebGL
* copies that onto its glyph atlas only when the atlas canvas is created, so
* an already-running WebGL renderer has to be rebuilt after the setting
* changes. Hidden panes that have not created WebGL yet just keep the CSS in
* place for the deferred load.
*/
export function createTerminalLigatureController(
options: TerminalLigatureControllerOptions,
) {
let addon: TerminalLigatureAddon | null = null;
let enabled = false;
const apply = (nextEnabled: boolean) => {
if (nextEnabled === enabled) return;
if (nextEnabled) {
const created = options.createAddon();
try {
options.loadAddon(created);
} catch (error) {
try {
created.dispose();
} catch {
// Keep the original load error.
}
options.warn?.("[XTerm] Ligatures addon failed to load", error);
return;
}
addon = created;
enabled = true;
} else {
try {
addon?.dispose();
} catch (error) {
options.warn?.("[XTerm] Ligatures addon failed to dispose", error);
}
addon = null;
enabled = false;
}
if (options.isWebglActive()) {
options.recreateWebgl();
}
options.repaint();
};
return {
apply,
dispose: () => {
const current = addon;
addon = null;
enabled = false;
try {
current?.dispose();
} catch (error) {
options.warn?.("[XTerm] Ligatures addon failed to dispose", error);
}
},
};
}
@@ -65,6 +65,7 @@ const createContext = (term: ReturnType<typeof createTerm>, terminalSettings: Re
keywordHighlighter: { setRules: noop },
cursorLineHighlighter: { setBackgroundColor: noop, setEnabled: noop },
clearTextureAtlas: noop,
syncFontLigatures: noop,
ensureWebglRenderer: async () => undefined,
dispose: noop,
};
@@ -7,6 +7,7 @@ import { resolveFontWeightBold } from '../../lib/fontWeightAvailability';
import { bundledFamiliesInStack } from '../../lib/fontAvailability';
import { isMacPlatform } from '../../lib/utils';
import { resolveXTermScrollback } from '../../infrastructure/config/xtermPerformance';
import { terminalFontLigaturesEnabled } from './runtime/terminalFontLigatures';
import {
createMacOptionForcedSelectionMouseEvent,
createRightClickMouseTrackingPressClaim,
@@ -1190,6 +1191,9 @@ export function useTerminalEffects(ctx: TerminalEffectsContext) {
termRef.current.options.altClickMovesCursor = altKeyOpts.altClickMovesCursor;
termRef.current.options.wordSeparator = terminalSettings.wordSeparators;
termRef.current.options.ignoreBracketedPasteMode = terminalSettings.disableBracketedPaste ?? false;
xtermRuntimeRef.current?.syncFontLigatures(
terminalFontLigaturesEnabled(terminalSettings),
);
}
// Changing the font can leave the WebGL renderer drawing stale glyphs from
+8
View File
@@ -177,6 +177,14 @@ test("normalizeTerminalSettings enables font smoothing by default", () => {
assert.equal(normalizeTerminalSettings().fontSmoothing, true);
});
test("normalizeTerminalSettings enables font ligatures by default", () => {
assert.equal(normalizeTerminalSettings().fontLigatures, true);
});
test("normalizeTerminalSettings preserves disabled font ligatures", () => {
assert.equal(normalizeTerminalSettings({ fontLigatures: false }).fontLigatures, false);
});
test("normalizeTerminalSettings enables terminal auto-close by default", () => {
assert.equal(normalizeTerminalSettings().autoCloseOnExit, true);
});
@@ -0,0 +1,24 @@
// @xterm/addon-ligatures bundles lru-cache, which imports
// node:diagnostics_channel for optional metrics. The renderer has no Node
// builtins. Nothing subscribes, so publish and tracePromise stay unused.
const silentChannel = {
hasSubscribers: false,
publish() {},
};
export function channel(): typeof silentChannel {
return silentChannel;
}
export function tracingChannel(): {
hasSubscribers: boolean;
tracePromise: () => Promise<void>;
} {
return {
hasSubscribers: false,
tracePromise() {
return Promise.resolve();
},
};
}
+35
View File
@@ -47,6 +47,7 @@
"@withfig/autocomplete": "^2.692.3",
"@xterm/addon-fit": "0.12.0-beta.292",
"@xterm/addon-image": "0.10.0-beta.292",
"@xterm/addon-ligatures": "0.11.0-beta.292",
"@xterm/addon-search": "0.17.0-beta.292",
"@xterm/addon-serialize": "0.15.0-beta.292",
"@xterm/addon-unicode-graphemes": "0.5.0-beta.292",
@@ -9218,6 +9219,31 @@
"@xterm/xterm": "^6.1.0-beta.292"
}
},
"node_modules/@xterm/addon-ligatures": {
"version": "0.11.0-beta.292",
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.292.tgz",
"integrity": "sha512-C9DaNn/E1SJh+gULgtT8Js2fz/8QIJ1LvmDXsMMayzvrL72g1kvFOYZSXAcmYveID3eIOIQKyecStfvFR7HU6A==",
"license": "MIT",
"dependencies": {
"lru-cache": "^11.3.6",
"opentype.js": "^2.0.0"
},
"engines": {
"node": ">8.0.0"
},
"peerDependencies": {
"@xterm/xterm": "^6.1.0-beta.292"
}
},
"node_modules/@xterm/addon-ligatures/node_modules/lru-cache": {
"version": "11.5.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz",
"integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==",
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@xterm/addon-search": {
"version": "0.17.0-beta.292",
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.292.tgz",
@@ -16505,6 +16531,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/opentype.js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-2.0.0.tgz",
"integrity": "sha512-kCyjv6xdDY1W/jLWZ/L3QhhTlKUqDZMQ5+Jdlw12b3dXkKNpYBqqlMMj0YDQPShWFTMwgZI1hG14kN3XUDSg/A==",
"license": "MIT",
"bin": {
"ot": "bin/ot"
}
},
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+1
View File
@@ -107,6 +107,7 @@
"@withfig/autocomplete": "^2.692.3",
"@xterm/addon-fit": "0.12.0-beta.292",
"@xterm/addon-image": "0.10.0-beta.292",
"@xterm/addon-ligatures": "0.11.0-beta.292",
"@xterm/addon-search": "0.17.0-beta.292",
"@xterm/addon-serialize": "0.15.0-beta.292",
"@xterm/addon-unicode-graphemes": "0.5.0-beta.292",
@@ -20,18 +20,30 @@ if (!process.versions.electron) {
fs.rmSync(userData, { recursive: true, force: true });
app.exit(code);
};
const bundle = esbuild.buildSync({
stdin: {
contents: [
'export {createXTermRuntime} from "./components/terminal/runtime/createXTermRuntime";',
'export {DEFAULT_TERMINAL_SETTINGS} from "./domain/models/terminal";',
'export {resolveInactiveTerminalPaneStyle} from "./components/terminalPaneVisibility";',
].join("\n"),
loader: "ts", resolveDir: root,
},
bundle: true, format: "cjs", platform: "browser", target: "chrome148", write: false,
define: { "import.meta.env.DEV": "false", "import.meta.env.PROD": "true", "import.meta": "{}" },
}).outputFiles[0].text;
let bundle;
try {
bundle = esbuild.buildSync({
stdin: {
contents: [
'export {createXTermRuntime} from "./components/terminal/runtime/createXTermRuntime";',
'export {DEFAULT_TERMINAL_SETTINGS} from "./domain/models/terminal";',
'export {resolveInactiveTerminalPaneStyle} from "./components/terminalPaneVisibility";',
].join("\n"),
loader: "ts", resolveDir: root,
},
bundle: true, format: "cjs", platform: "browser", target: "chrome148", write: false,
// addon-ligatures bundles lru-cache's node:diagnostics_channel import.
alias: {
"node:diagnostics_channel": path.join(root, "infrastructure/shims/nodeDiagnosticsChannel.ts"),
"diagnostics_channel": path.join(root, "infrastructure/shims/nodeDiagnosticsChannel.ts"),
},
define: { "import.meta.env.DEV": "false", "import.meta.env.PROD": "true", "import.meta": "{}" },
}).outputFiles[0].text;
} catch (error) {
console.error(error);
fs.rmSync(userData, { recursive: true, force: true });
process.exit(1);
}
void app.whenReady().then(async () => {
win = new BrowserWindow({
show: true, width: 1100, height: 740,
+21 -8
View File
@@ -18,14 +18,27 @@ if (!process.versions.electron || process.platform !== "darwin") {
fs.rmSync(userData, { recursive: true, force: true });
app.exit(code);
};
const bundle = require("esbuild").buildSync({
stdin: {
contents: 'export {createXTermRuntime} from "./components/terminal/runtime/createXTermRuntime"; export {DEFAULT_TERMINAL_SETTINGS} from "./domain/models/terminal"; export {dispatchKittyKeyboardBroadcastInput} from "./components/terminal/runtime/kittyKeyboardBroadcast";',
loader: "ts", resolveDir: path.resolve(__dirname, ".."),
},
bundle: true, format: "cjs", platform: "browser", write: false,
define: { "import.meta.env.DEV": "false", "import.meta.env.PROD": "true", "import.meta": "{}" },
}).outputFiles[0].text;
const root = path.resolve(__dirname, "..");
let bundle;
try {
bundle = require("esbuild").buildSync({
stdin: {
contents: 'export {createXTermRuntime} from "./components/terminal/runtime/createXTermRuntime"; export {DEFAULT_TERMINAL_SETTINGS} from "./domain/models/terminal"; export {dispatchKittyKeyboardBroadcastInput} from "./components/terminal/runtime/kittyKeyboardBroadcast";',
loader: "ts", resolveDir: root,
},
bundle: true, format: "cjs", platform: "browser", write: false,
// addon-ligatures bundles lru-cache's node:diagnostics_channel import.
alias: {
"node:diagnostics_channel": path.join(root, "infrastructure/shims/nodeDiagnosticsChannel.ts"),
"diagnostics_channel": path.join(root, "infrastructure/shims/nodeDiagnosticsChannel.ts"),
},
define: { "import.meta.env.DEV": "false", "import.meta.env.PROD": "true", "import.meta": "{}" },
}).outputFiles[0].text;
} catch (error) {
console.error(error);
fs.rmSync(userData, { recursive: true, force: true });
process.exit(1);
}
void app.whenReady().then(async () => {
win = new BrowserWindow({
show: true, width: 1000, height: 600,
+5
View File
@@ -104,6 +104,7 @@ export default defineConfig(() => {
'@xterm/xterm',
'@xterm/addon-fit',
'@xterm/addon-image',
'@xterm/addon-ligatures',
'@xterm/addon-search',
'@xterm/addon-serialize',
'@xterm/addon-web-links',
@@ -136,6 +137,10 @@ export default defineConfig(() => {
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
// The ligatures addon pulls lru-cache's node:diagnostics_channel import
// into the renderer bundle. Stub it; the cache never publishes metrics.
'node:diagnostics_channel': path.resolve(__dirname, 'infrastructure/shims/nodeDiagnosticsChannel.ts'),
'diagnostics_channel': path.resolve(__dirname, 'infrastructure/shims/nodeDiagnosticsChannel.ts'),
}
}
};