diff --git a/packages/cli/src/ui/contexts/KeypressContext.test.tsx b/packages/cli/src/ui/contexts/KeypressContext.test.tsx index f5b303128b..d2107ff40b 100644 --- a/packages/cli/src/ui/contexts/KeypressContext.test.tsx +++ b/packages/cli/src/ui/contexts/KeypressContext.test.tsx @@ -956,127 +956,3 @@ describe('Drag and Drop Handling', () => { }); }); }); - -describe('Terminal-specific Alt+key combinations', () => { - let stdin: MockStdin; - const mockSetRawMode = vi.fn(); - - const wrapper = ({ children }: { children: React.ReactNode }) => ( - {children} - ); - - beforeEach(() => { - vi.clearAllMocks(); - stdin = new MockStdin(); - (useStdin as Mock).mockReturnValue({ - stdin, - setRawMode: mockSetRawMode, - }); - }); - - // Terminals to test - const terminals = ['iTerm2', 'Ghostty', 'MacTerminal', 'VSCodeTerminal']; - - // Key mappings: letter -> [keycode, accented character, shouldHaveMeta] - // Note: µ (mu) is sent with meta:false on iTerm2/VSCode - const keys: Record = { - a: [97, 'å', true], - o: [111, 'ø', true], - m: [109, 'µ', false], - }; - - it.each( - terminals.flatMap((terminal) => - Object.entries(keys).map( - ([key, [keycode, accentedChar, shouldHaveMeta]]) => { - if (terminal === 'Ghostty') { - // Ghostty uses kitty protocol sequences - return { - terminal, - key, - kittySequence: `\x1b[${keycode};3u`, - expected: { - name: key, - ctrl: false, - meta: true, - shift: false, - paste: false, - kittyProtocol: true, - }, - }; - } else if (terminal === 'MacTerminal') { - // Mac Terminal sends ESC + letter - return { - terminal, - key, - input: { - sequence: `\x1b${key}`, - name: key, - ctrl: false, - meta: true, - shift: false, - paste: false, - }, - expected: { - sequence: `\x1b${key}`, - name: key, - ctrl: false, - meta: true, - shift: false, - paste: false, - }, - }; - } else { - // iTerm2 and VSCode send accented characters (å, ø, µ) - // Note: µ comes with meta:false but gets converted to m with meta:true - return { - terminal, - key, - input: { - name: key, - ctrl: false, - meta: shouldHaveMeta, - shift: false, - paste: false, - sequence: accentedChar, - }, - expected: { - name: key, - ctrl: false, - meta: true, // Always expect meta:true after conversion - shift: false, - paste: false, - sequence: accentedChar, - }, - }; - } - }, - ), - ), - )( - 'should handle Alt+$key in $terminal', - ({ - kittySequence, - input, - expected, - }: { - kittySequence?: string; - input?: Partial; - expected: Partial; - }) => { - const keyHandler = vi.fn(); - const { result } = renderHook(() => useKeypressContext(), { wrapper }); - act(() => result.current.subscribe(keyHandler)); - - if (kittySequence) { - act(() => stdin.sendKittySequence(kittySequence)); - } else if (input) { - act(() => stdin.pressKey(input)); - } - - expect(keyHandler).toHaveBeenCalledWith( - expect.objectContaining(expected), - ); - }, - ); -}); diff --git a/packages/cli/src/ui/contexts/KeypressContext.tsx b/packages/cli/src/ui/contexts/KeypressContext.tsx index b8b89dceeb..4930f7101d 100644 --- a/packages/cli/src/ui/contexts/KeypressContext.tsx +++ b/packages/cli/src/ui/contexts/KeypressContext.tsx @@ -45,36 +45,6 @@ export const DRAG_COMPLETION_TIMEOUT_MS = 100; // Broadcast full path after 100m export const SINGLE_QUOTE = "'"; export const DOUBLE_QUOTE = '"'; -const ALT_KEY_CHARACTER_MAP: Record = { - '\u00E5': 'a', - '\u222B': 'b', - '\u00E7': 'c', - '\u2202': 'd', - '\u00B4': 'e', - '\u0192': 'f', - '\u00A9': 'g', - '\u02D9': 'h', - '\u02C6': 'i', - '\u2206': 'j', - '\u02DA': 'k', - '\u00AC': 'l', - '\u00B5': 'm', - '\u02DC': 'n', - '\u00F8': 'o', - '\u03C0': 'p', - '\u0153': 'q', - '\u00AE': 'r', - '\u00DF': 's', - '\u2020': 't', - '\u00A8': 'u', - '\u221A': 'v', - '\u2211': 'w', - '\u2248': 'x', - '\u00A5': 'y', - '\\': 'y', - '\u03A9': 'z', -}; - export interface Key { name: string; ctrl: boolean; @@ -357,9 +327,9 @@ export function KeypressProvider({ }; } - // Ctrl+letters and Alt+letters + // Ctrl+letters if ( - (ctrl || alt) && + ctrl && keyCode >= 'a'.charCodeAt(0) && keyCode <= 'z'.charCodeAt(0) ) { @@ -367,7 +337,7 @@ export function KeypressProvider({ return { key: { name: letter, - ctrl, + ctrl: true, meta: alt, shift, paste: false, @@ -465,19 +435,6 @@ export function KeypressProvider({ return; } - const mappedLetter = ALT_KEY_CHARACTER_MAP[key.sequence]; - if (mappedLetter && !key.meta) { - broadcast({ - name: mappedLetter, - ctrl: false, - meta: true, - shift: false, - paste: isPaste, - sequence: key.sequence, - }); - return; - } - if (key.name === 'return' && waitingForEnterAfterBackslash) { if (backslashTimeout) { clearTimeout(backslashTimeout);