From a983ae0444b92a1870419eecb96dd42fd00773d4 Mon Sep 17 00:00:00 2001 From: Gustavo Ambrozio Date: Thu, 13 Aug 2026 10:36:39 -1000 Subject: [PATCH] refactor(terminal): inject mac platform flag at mount Greptile review: the browser tests patched navigator.userAgent/platform globals to exercise mac-only key handling. Make the platform an explicit mount option (defaulting to navigator detection) so tests inject it directly and the runtime stops sniffing navigator per keydown. Co-Authored-By: Claude Fable 5 --- .../terminal-emulator-runtime.browser.test.ts | 76 +++++-------------- .../runtime/terminal-emulator-runtime.ts | 6 +- .../webview/terminal-emulator-webview-html.ts | 2 +- 3 files changed, 24 insertions(+), 60 deletions(-) diff --git a/packages/app/src/terminal/runtime/terminal-emulator-runtime.browser.test.ts b/packages/app/src/terminal/runtime/terminal-emulator-runtime.browser.test.ts index ccb88bac7e..176f6bed2d 100644 --- a/packages/app/src/terminal/runtime/terminal-emulator-runtime.browser.test.ts +++ b/packages/app/src/terminal/runtime/terminal-emulator-runtime.browser.test.ts @@ -45,11 +45,6 @@ interface MountedTerminal { const mountedTerminals: MountedTerminal[] = []; -const MAC_DESKTOP_UA = - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"; -const LINUX_DESKTOP_UA = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"; - function nextFrame(): Promise { return new Promise((resolve) => { requestAnimationFrame(() => { @@ -82,6 +77,7 @@ function createTerminalHost(input: { width: number; height: number; scrollback?: number; + isMacLikePlatform?: boolean; }): MountedTerminal { const root = document.createElement("div"); root.style.width = `${input.width}px`; @@ -127,6 +123,7 @@ function createTerminalHost(input: { host, initialSnapshot: null, scrollback: input.scrollback ?? 10_000, + isMacLikePlatform: input.isMacLikePlatform, theme: { background: "#0b0b0b", foreground: "#e6e6e6", @@ -212,35 +209,6 @@ function dispatchTerminalKey(input: { return textarea.dispatchEvent(event); } -// The runtime reads the platform per keydown, so shadowing the instance properties -// makes mac-only key handling testable from any CI host. -function withNavigatorPlatform( - input: { userAgent: string; platform: string }, - run: () => void, -): void { - const restores = (["userAgent", "platform"] as const).map((property) => { - const original = Object.getOwnPropertyDescriptor(navigator, property); - Object.defineProperty(navigator, property, { - value: input[property], - configurable: true, - }); - return () => { - if (original) { - Object.defineProperty(navigator, property, original); - } else { - Reflect.deleteProperty(navigator, property); - } - }; - }); - try { - run(); - } finally { - for (const restore of restores) { - restore(); - } - } -} - /** An OSC 8 hyperlink, the escape sequence CLIs such as gh and ls --hyperlink print. */ function hyperlink(input: { url: string; text: string }): string { return `\x1b]8;;${input.url}\x1b\\${input.text}\x1b]8;;\x1b\\`; @@ -555,18 +523,16 @@ describe("terminal emulator runtime in a real browser", () => { it("translates mac editing shortcuts into shell editing sequences", async () => { await page.viewport(900, 600); - const mounted = createTerminalHost({ width: 720, height: 360 }); + const mounted = createTerminalHost({ width: 720, height: 360, isMacLikePlatform: true }); await waitFor({ predicate: () => mounted.sizes.length > 0 }); - withNavigatorPlatform({ userAgent: MAC_DESKTOP_UA, platform: "MacIntel" }, () => { - dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39 }); - dispatchTerminalKey({ host: mounted.host, key: "ArrowLeft", keyCode: 37, metaKey: true }); - dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, metaKey: true }); - dispatchTerminalKey({ host: mounted.host, key: "ArrowLeft", keyCode: 37, altKey: true }); - dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, altKey: true }); - dispatchTerminalKey({ host: mounted.host, key: "Backspace", keyCode: 8, metaKey: true }); - }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39 }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowLeft", keyCode: 37, metaKey: true }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, metaKey: true }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowLeft", keyCode: 37, altKey: true }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, altKey: true }); + dispatchTerminalKey({ host: mounted.host, key: "Backspace", keyCode: 8, metaKey: true }); await nextFrame(); expect(mounted.inputs).toEqual([ @@ -582,18 +548,16 @@ describe("terminal emulator runtime in a real browser", () => { it("keeps cmd+shift+arrow free for app-level shortcuts", async () => { await page.viewport(900, 600); - const mounted = createTerminalHost({ width: 720, height: 360 }); + const mounted = createTerminalHost({ width: 720, height: 360, isMacLikePlatform: true }); await waitFor({ predicate: () => mounted.sizes.length > 0 }); - withNavigatorPlatform({ userAgent: MAC_DESKTOP_UA, platform: "MacIntel" }, () => { - dispatchTerminalKey({ - host: mounted.host, - key: "ArrowRight", - keyCode: 39, - metaKey: true, - shiftKey: true, - }); + dispatchTerminalKey({ + host: mounted.host, + key: "ArrowRight", + keyCode: 39, + metaKey: true, + shiftKey: true, }); await nextFrame(); @@ -603,14 +567,12 @@ describe("terminal emulator runtime in a real browser", () => { it("does not remap modified arrows on non-mac platforms", async () => { await page.viewport(900, 600); - const mounted = createTerminalHost({ width: 720, height: 360 }); + const mounted = createTerminalHost({ width: 720, height: 360, isMacLikePlatform: false }); await waitFor({ predicate: () => mounted.sizes.length > 0 }); - withNavigatorPlatform({ userAgent: LINUX_DESKTOP_UA, platform: "Linux x86_64" }, () => { - dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, metaKey: true }); - dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, altKey: true }); - }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, metaKey: true }); + dispatchTerminalKey({ host: mounted.host, key: "ArrowRight", keyCode: 39, altKey: true }); await nextFrame(); // Meta+arrow is ignored by xterm; alt+arrow takes xterm's generic encoding. diff --git a/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts b/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts index 28d3158347..49c667e7eb 100644 --- a/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts +++ b/packages/app/src/terminal/runtime/terminal-emulator-runtime.ts @@ -60,6 +60,7 @@ export interface TerminalEmulatorRuntimeMountInput { theme: ITheme; fontFamily?: string; fontSize?: number; + isMacLikePlatform?: boolean; } export interface TerminalEmulatorRuntimeCallbacks { @@ -145,7 +146,6 @@ declare global { } } -// Read at call time, not module load: keydown handling consults it per event. function detectMacLikePlatform(): boolean { return ( typeof navigator !== "undefined" && @@ -262,6 +262,7 @@ export class TerminalEmulatorRuntime { private hasUngatedWrites = false; private readonly inputModeDecoder = new TextDecoder(); private suppressInput = false; + private isMacLikePlatform = false; private readonly inputModeTracker = new TerminalInputModeTracker(); private lastInputModeState: TerminalInputModeState = this.inputModeTracker.getState(); private themeBackgroundElements: HTMLElement[] = []; @@ -453,7 +454,7 @@ export class TerminalEmulatorRuntime { terminal: Pick, event: KeyboardEvent, ): boolean { - if (!detectMacLikePlatform() || hasPendingTerminalModifiers(this.pendingModifiers)) { + if (!this.isMacLikePlatform || hasPendingTerminalModifiers(this.pendingModifiers)) { return false; } const editingShortcutData = resolveMacTerminalEditingShortcut(event); @@ -474,6 +475,7 @@ export class TerminalEmulatorRuntime { input.host.innerHTML = ""; this.lastSize = null; + this.isMacLikePlatform = input.isMacLikePlatform ?? detectMacLikePlatform(); this.inputModeTracker.reset(); this.emitInputModeChange(); diff --git a/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts b/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts index 1d23b244b5..bddf068153 100644 --- a/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts +++ b/packages/app/src/terminal/webview/terminal-emulator-webview-html.ts @@ -2,4 +2,4 @@ // Do not edit by hand. export const terminalEmulatorWebViewHtml = - '\n\n \n \n \n \n \n \n \n'; + '\n\n \n \n \n \n \n \n \n';