From d2276ab65466277e092c208ea646fe57ca235266 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 19 Dec 2025 22:08:36 +0100 Subject: [PATCH] fix(tui): fix input buffering in iTerm2 causing delayed keypresses The cell size query response parser was incorrectly holding back Kitty keyboard protocol sequences, thinking they might be incomplete cell size responses. This caused Ctrl+C/Ctrl+D to require multiple presses in iTerm2. Fixed by only waiting for more data if the buffer doesn't end with a terminal escape sequence terminator character. --- packages/tui/src/tui.ts | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/tui/src/tui.ts b/packages/tui/src/tui.ts index 0bf2e70d..61474f8a 100644 --- a/packages/tui/src/tui.ts +++ b/packages/tui/src/tui.ts @@ -166,24 +166,20 @@ export class TUI extends Container { this.cellSizeQueryPending = false; } - // Check if we have a partial response starting (wait for more data) - // ESC [ 6 ; ... could be incomplete - const partialPattern = /\x1b\[6;[\d;]*$/; - if (partialPattern.test(this.inputBuffer)) { - return ""; // Wait for more data + // Check if we have a partial cell size response starting (wait for more data) + // Patterns that could be incomplete cell size response: \x1b, \x1b[, \x1b[6, \x1b[6;...(no t yet) + const partialCellSizePattern = /\x1b(\[6?;?[\d;]*)?$/; + if (partialCellSizePattern.test(this.inputBuffer)) { + // Check if it's actually a complete different escape sequence (ends with a letter) + // Cell size response ends with 't', Kitty keyboard ends with 'u', arrows end with A-D, etc. + const lastChar = this.inputBuffer[this.inputBuffer.length - 1]; + if (!/[a-zA-Z~]/.test(lastChar)) { + // Doesn't end with a terminator, might be incomplete - wait for more + return ""; + } } - // Check for any ESC that might be start of response - const escIndex = this.inputBuffer.lastIndexOf("\x1b"); - if (escIndex !== -1 && escIndex > this.inputBuffer.length - 10) { - // Might be incomplete escape sequence, wait a bit - // But return any data before it - const before = this.inputBuffer.substring(0, escIndex); - this.inputBuffer = this.inputBuffer.substring(escIndex); - return before; - } - - // No response found, return buffered data as user input + // No cell size response found, return buffered data as user input const result = this.inputBuffer; this.inputBuffer = ""; this.cellSizeQueryPending = false; // Give up waiting