fix(app): remove terminal debug instrumentation
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
hasPendingTerminalModifiers,
|
||||
isTerminalModifierDomKey,
|
||||
mapTerminalDataToKey,
|
||||
mergeTerminalModifiers,
|
||||
normalizeDomTerminalKey,
|
||||
normalizeTerminalTransportKey,
|
||||
shouldInterceptDomTerminalKey,
|
||||
} from "./terminal-keys";
|
||||
|
||||
describe("terminal key helpers", () => {
|
||||
it("normalizes supported DOM keys", () => {
|
||||
expect(normalizeDomTerminalKey("Esc")).toBe("Escape");
|
||||
expect(normalizeDomTerminalKey(" ")).toBe(" ");
|
||||
expect(normalizeDomTerminalKey("ArrowUp")).toBe("ArrowUp");
|
||||
expect(normalizeDomTerminalKey("F12")).toBe("F12");
|
||||
});
|
||||
|
||||
it("filters unsupported and composing DOM keys", () => {
|
||||
expect(normalizeDomTerminalKey("Dead")).toBeNull();
|
||||
expect(normalizeDomTerminalKey("Unidentified")).toBeNull();
|
||||
expect(normalizeDomTerminalKey("MediaPlayPause")).toBeNull();
|
||||
});
|
||||
|
||||
it("detects modifier DOM keys", () => {
|
||||
expect(isTerminalModifierDomKey("Control")).toBe(true);
|
||||
expect(isTerminalModifierDomKey("Shift")).toBe(true);
|
||||
expect(isTerminalModifierDomKey("a")).toBe(false);
|
||||
});
|
||||
|
||||
it("lowercases printable transport keys", () => {
|
||||
expect(normalizeTerminalTransportKey("C")).toBe("c");
|
||||
expect(normalizeTerminalTransportKey("Escape")).toBe("Escape");
|
||||
});
|
||||
|
||||
it("merges pending modifiers with native key modifiers", () => {
|
||||
expect(
|
||||
mergeTerminalModifiers({
|
||||
pendingModifiers: { ctrl: true, shift: false, alt: true },
|
||||
ctrlKey: false,
|
||||
shiftKey: true,
|
||||
altKey: false,
|
||||
metaKey: false,
|
||||
})
|
||||
).toEqual({
|
||||
ctrl: true,
|
||||
shift: true,
|
||||
alt: true,
|
||||
meta: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("intercepts special keys and modifier combos", () => {
|
||||
expect(
|
||||
shouldInterceptDomTerminalKey({
|
||||
key: "Escape",
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
pendingModifiers: { ctrl: false, shift: false, alt: false },
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldInterceptDomTerminalKey({
|
||||
key: "c",
|
||||
ctrlKey: true,
|
||||
altKey: false,
|
||||
pendingModifiers: { ctrl: false, shift: false, alt: false },
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldInterceptDomTerminalKey({
|
||||
key: "c",
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
pendingModifiers: { ctrl: false, shift: false, alt: false },
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("detects pending modifier state", () => {
|
||||
expect(hasPendingTerminalModifiers({ ctrl: false, shift: false, alt: false })).toBe(
|
||||
false
|
||||
);
|
||||
expect(hasPendingTerminalModifiers({ ctrl: true, shift: false, alt: false })).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("maps onData bytes to terminal keys for modifier fallback", () => {
|
||||
expect(mapTerminalDataToKey("c")).toBe("c");
|
||||
expect(mapTerminalDataToKey("\r")).toBe("Enter");
|
||||
expect(mapTerminalDataToKey("\t")).toBe("Tab");
|
||||
expect(mapTerminalDataToKey("\x7f")).toBe("Backspace");
|
||||
expect(mapTerminalDataToKey("\x1b")).toBe("Escape");
|
||||
expect(mapTerminalDataToKey("\x03")).toBeNull();
|
||||
expect(mapTerminalDataToKey("")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,159 @@
|
||||
export type PendingTerminalModifiers = {
|
||||
ctrl: boolean;
|
||||
shift: boolean;
|
||||
alt: boolean;
|
||||
};
|
||||
|
||||
const MODIFIER_DOM_KEYS = new Set([
|
||||
"Control",
|
||||
"Shift",
|
||||
"Alt",
|
||||
"Meta",
|
||||
"AltGraph",
|
||||
"OS",
|
||||
]);
|
||||
|
||||
const DOM_KEY_ALIASES: Record<string, string> = {
|
||||
Esc: "Escape",
|
||||
Left: "ArrowLeft",
|
||||
Right: "ArrowRight",
|
||||
Up: "ArrowUp",
|
||||
Down: "ArrowDown",
|
||||
Del: "Delete",
|
||||
Spacebar: " ",
|
||||
Space: " ",
|
||||
};
|
||||
|
||||
const SUPPORTED_SPECIAL_KEYS = new Set([
|
||||
"Enter",
|
||||
"Tab",
|
||||
"Backspace",
|
||||
"Escape",
|
||||
"ArrowUp",
|
||||
"ArrowDown",
|
||||
"ArrowLeft",
|
||||
"ArrowRight",
|
||||
"Home",
|
||||
"End",
|
||||
"Insert",
|
||||
"Delete",
|
||||
"PageUp",
|
||||
"PageDown",
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"F10",
|
||||
"F11",
|
||||
"F12",
|
||||
]);
|
||||
|
||||
export function isTerminalModifierDomKey(rawKey: string): boolean {
|
||||
return MODIFIER_DOM_KEYS.has(rawKey);
|
||||
}
|
||||
|
||||
export function normalizeDomTerminalKey(rawKey: string): string | null {
|
||||
if (!rawKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const key = DOM_KEY_ALIASES[rawKey] ?? rawKey;
|
||||
if (
|
||||
key === "Unidentified" ||
|
||||
key === "Dead" ||
|
||||
key === "Compose" ||
|
||||
key === "Process"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (key.length === 1) {
|
||||
return key;
|
||||
}
|
||||
|
||||
if (SUPPORTED_SPECIAL_KEYS.has(key)) {
|
||||
return key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeTerminalTransportKey(key: string): string {
|
||||
if (key.length === 1) {
|
||||
return key.toLowerCase();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
export function hasPendingTerminalModifiers(
|
||||
modifiers: PendingTerminalModifiers
|
||||
): boolean {
|
||||
return modifiers.ctrl || modifiers.shift || modifiers.alt;
|
||||
}
|
||||
|
||||
export function shouldInterceptDomTerminalKey(args: {
|
||||
key: string;
|
||||
ctrlKey: boolean;
|
||||
altKey: boolean;
|
||||
pendingModifiers: PendingTerminalModifiers;
|
||||
}): boolean {
|
||||
return (
|
||||
args.key.length > 1 ||
|
||||
args.ctrlKey ||
|
||||
args.altKey ||
|
||||
hasPendingTerminalModifiers(args.pendingModifiers)
|
||||
);
|
||||
}
|
||||
|
||||
export function mergeTerminalModifiers(args: {
|
||||
pendingModifiers: PendingTerminalModifiers;
|
||||
ctrlKey: boolean;
|
||||
shiftKey: boolean;
|
||||
altKey: boolean;
|
||||
metaKey: boolean;
|
||||
}): {
|
||||
ctrl: boolean;
|
||||
shift: boolean;
|
||||
alt: boolean;
|
||||
meta: boolean;
|
||||
} {
|
||||
const { pendingModifiers, ctrlKey, shiftKey, altKey, metaKey } = args;
|
||||
return {
|
||||
ctrl: ctrlKey || pendingModifiers.ctrl,
|
||||
shift: shiftKey || pendingModifiers.shift,
|
||||
alt: altKey || pendingModifiers.alt,
|
||||
meta: metaKey,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapTerminalDataToKey(data: string): string | null {
|
||||
if (!data || data.length !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data === "\r" || data === "\n") {
|
||||
return "Enter";
|
||||
}
|
||||
if (data === "\t") {
|
||||
return "Tab";
|
||||
}
|
||||
if (data === "\x7f" || data === "\b") {
|
||||
return "Backspace";
|
||||
}
|
||||
if (data === "\x1b") {
|
||||
return "Escape";
|
||||
}
|
||||
|
||||
const code = data.charCodeAt(0);
|
||||
// Only map printable ASCII for modifier fallback; keep control bytes raw.
|
||||
if (code >= 0x20 && code <= 0x7e) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user