From 36dcb891b02c887f8635d42fc8ccc115a590253a Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Sun, 4 Jan 2026 19:19:34 -0800 Subject: [PATCH] fix: patch release event leaks (#463) Co-authored-by: Letta --- src/cli/components/PasteAwareTextInput.tsx | 33 ++++------------------ src/cli/utils/kittyProtocolDetector.ts | 11 +++++--- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/cli/components/PasteAwareTextInput.tsx b/src/cli/components/PasteAwareTextInput.tsx index ceed5a3..7b2cf17 100644 --- a/src/cli/components/PasteAwareTextInput.tsx +++ b/src/cli/components/PasteAwareTextInput.tsx @@ -461,42 +461,21 @@ export function PasteAwareTextInput({ // in use-input.js, which parses it as return + shift/ctrl/meta flags. // The useInput handler at line 186 then handles the newline insertion. - // Kitty keyboard protocol: Arrow keys - // Format: ESC[1;modifier:event_typeX where X is A/B/C/D for up/down/right/left - // Event types: 1=press, 2=repeat, 3=release - // Handle press AND repeat events, ignore release - { - // Match ESC[1;N:1X or ESC[1;N:2X (press or repeat) - // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC sequence matching - const arrowMatch = sequence.match(/^\x1b\[1;\d+:[12]([ABCD])$/); - if (arrowMatch) { - // Emit standard arrow key sequence - internal_eventEmitter.emit("input", `\x1b[${arrowMatch[1]}`); - return; - } - // Ignore arrow key release events only - // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC sequence matching - if (/^\x1b\[1;\d+:3[ABCD]$/.test(sequence)) { - return; - } - } + // Note: Arrow keys with modifiers are now handled natively by parseKeypress + // since we use kitty protocol flag 1 only (no event types). + // With flag 1, arrows come as ESC[1;modifierD which parseKeypress recognizes. + // Previously we handled ESC[1;modifier:eventD format (with flag 7) here. // fn+Delete (forward delete): ESC[3~ - standard ANSI escape sequence - // Also handle kitty extended format: ESC[3;modifier:event_type~ - // Event types: 1=press, 2=repeat, 3=release + // With kitty flag 1, modifiers come as ESC[3;modifier~ (no event type). // Use caretOffsetRef which is updated synchronously via onCursorOffsetChange // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC sequence matching - if (sequence === "\x1b[3~" || /^\x1b\[3;\d+:[12]~$/.test(sequence)) { + if (sequence === "\x1b[3~" || /^\x1b\[3;\d+~$/.test(sequence)) { // Set timestamp so ink-text-input skips its delete handling globalThis.__lettaForwardDeleteTimestamp = Date.now(); forwardDeleteAtCursor(caretOffsetRef.current); return; } - // Ignore forward delete release events - // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC sequence matching - if (/^\x1b\[3;\d+:3~$/.test(sequence)) { - return; - } // Option+Delete sequences (check first as they're exact matches) // - iTerm2/some terminals: ESC + DEL (\x1b\x7f) diff --git a/src/cli/utils/kittyProtocolDetector.ts b/src/cli/utils/kittyProtocolDetector.ts index c82e05b..23530fd 100644 --- a/src/cli/utils/kittyProtocolDetector.ts +++ b/src/cli/utils/kittyProtocolDetector.ts @@ -137,10 +137,13 @@ export function isKittyProtocolEnabled(): boolean { function enableKittyKeyboardProtocol() { try { - // Enable keyboard progressive enhancement flags. - // Use 7 (=1|2|4): DISAMBIGUATE_ESCAPE_CODES | REPORT_EVENT_TYPES | REPORT_ALTERNATE_KEYS - // This matches what crossterm-based TUIs (e.g., codex) request. - fs.writeSync(process.stdout.fd, "\x1b[>7u"); + // Enable keyboard progressive enhancement with flag 1 (DISAMBIGUATE_ESCAPE_CODES) only. + // Previously used flag 7 (1|2|4) but flag 2 (REPORT_EVENT_TYPES) causes release events + // that leak into input when typing fast in iTerm2/Kitty/etc. + // Flag 4 (REPORT_ALTERNATE_KEYS) provides data we don't use. + // Gemini CLI uses flag 1 only - this is the proven approach. + // See: .notes/csi-u-release-events-fix.md for full analysis. + fs.writeSync(process.stdout.fd, "\x1b[>1u"); kittyEnabled = true; } catch { // Ignore errors