mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 23:44:41 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7dc2a3568c
commit
a983ae0444
@@ -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<void> {
|
||||
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.
|
||||
|
||||
@@ -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<Terminal, "input" | "scrollToBottom">,
|
||||
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();
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user