From f3554dfedc321c67fe676ce593f98c38e816576b Mon Sep 17 00:00:00 2001 From: dengmik-commits <270912164+dengmik-commits@users.noreply.github.com> Date: Fri, 15 May 2026 10:30:20 +0800 Subject: [PATCH 1/2] fix: add Kitty keyboard protocol Shift+Enter sequences for broader terminal compatibility SHIFT_RETURN_SEQUENCES now covers both xterm modifyOtherKeys (Shift=2) and Kitty keyboard protocol (Shift=1) encodings. Also clear input when key.return is true to prevent escape sequence artifacts from leaking into the text buffer. --- src/tests/promptInputKeys.test.ts | 2 +- src/ui/prompt/useTerminalInput.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tests/promptInputKeys.test.ts b/src/tests/promptInputKeys.test.ts index 69d2075..372dfc7 100644 --- a/src/tests/promptInputKeys.test.ts +++ b/src/tests/promptInputKeys.test.ts @@ -80,7 +80,7 @@ test("parseTerminalInput keeps BS payload for meta+backspace", () => { test("parseTerminalInput recognizes shifted return sequences", () => { const { input, key } = parseTerminalInput("\u001B\r"); - assert.equal(input, "\r"); + assert.equal(input, ""); assert.equal(key.return, true); assert.equal(key.shift, true); assert.equal(key.meta, false); diff --git a/src/ui/prompt/useTerminalInput.ts b/src/ui/prompt/useTerminalInput.ts index 8013ff6..ea368b5 100644 --- a/src/ui/prompt/useTerminalInput.ts +++ b/src/ui/prompt/useTerminalInput.ts @@ -26,7 +26,15 @@ const BACKSPACE_BYTES = new Set(["\u007F", "\b"]); const FORWARD_DELETE_SEQUENCES = new Set(["\u001B[3~", "\u001B[P"]); const HOME_SEQUENCES = new Set(["\u001B[H", "\u001B[1~", "\u001B[7~", "\u001BOH"]); const END_SEQUENCES = new Set(["\u001B[F", "\u001B[4~", "\u001B[8~", "\u001BOF"]); -const SHIFT_RETURN_SEQUENCES = new Set(["\u001B\r", "\u001B[13;2u", "\u001B[13;2~", "\u001B[27;2;13~"]); +const SHIFT_RETURN_SEQUENCES = new Set([ + "\u001B\r", + "\u001B[13;2u", // xterm modifyOtherKeys: keycode=13 (Enter), modifier=2 (Shift) + "\u001B[13;1u", // Kitty keyboard protocol: keycode=13, modifier=1 (Shift) + "\u001B[13;2~", + "\u001B[13;1~", // tmux / alternate terminals may use ~ terminator + "\u001B[27;2;13~", + "\u001B[27;1;13~", // extended format, Kitty encoding +]); const META_RETURN_SEQUENCES = new Set(["\u001B[13;3u", "\u001B[13;4u"]); const CTRL_LEFT_SEQUENCES = new Set(["\u001B[1;5D", "\u001B[5D"]); const CTRL_RIGHT_SEQUENCES = new Set(["\u001B[1;5C", "\u001B[5C"]); @@ -162,7 +170,7 @@ export function parseTerminalInput(data: Buffer | string): { input: string; key: key.shift = true; } - if (key.tab || key.backspace || key.delete) { + if (key.tab || key.backspace || key.delete || key.return) { input = ""; } From efde004299e5892b132412845d5a3f08e13242d2 Mon Sep 17 00:00:00 2001 From: dengmik-commits <270912164+dengmik-commits@users.noreply.github.com> Date: Fri, 15 May 2026 11:25:43 +0800 Subject: [PATCH 2/2] fix: use dynamic modifier parsing for Shift+Enter recognition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace exact string matching in SHIFT_RETURN_SEQUENCES with CSI parameter parsing that checks modifier bits. Windows Terminal sends ESC[13;130u (modifier=128+2) where 128 is a terminal-specific flag — the old code only matched modifier=2 exactly. Also enable Kitty progressive enhancement (ESC[>1u) alongside xterm modifyOtherKeys, since Windows Terminal requires the Kitty protocol to report modified keys. --- src/tests/promptInputKeys.test.ts | 4 +-- src/ui/prompt/cursor.ts | 6 ++-- src/ui/prompt/useTerminalInput.ts | 52 +++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/tests/promptInputKeys.test.ts b/src/tests/promptInputKeys.test.ts index 372dfc7..8952a3d 100644 --- a/src/tests/promptInputKeys.test.ts +++ b/src/tests/promptInputKeys.test.ts @@ -108,8 +108,8 @@ test("parseTerminalInput recognizes alternate shifted return sequences", () => { }); test("terminal extended key helpers request and restore modifyOtherKeys mode", () => { - assert.equal(enableTerminalExtendedKeys(), "\u001B[>4;1m"); - assert.equal(disableTerminalExtendedKeys(), "\u001B[>4;0m"); + assert.equal(enableTerminalExtendedKeys(), "\u001B[>4;1m\u001B[>1u"); + assert.equal(disableTerminalExtendedKeys(), "\u001B[>4;0m\u001B[ { diff --git a/src/ui/prompt/cursor.ts b/src/ui/prompt/cursor.ts index 2668470..5eff1de 100644 --- a/src/ui/prompt/cursor.ts +++ b/src/ui/prompt/cursor.ts @@ -40,12 +40,14 @@ function disableTerminalFocusReporting(): string { return "\u001B[?1004l"; } +// xterm modifyOtherKeys + Kitty progressive enhancement. +// Both are needed: some terminals (incl. Windows Terminal) only respond to Kitty. export function enableTerminalExtendedKeys(): string { - return "\u001B[>4;1m"; + return "\u001B[>4;1m\u001B[>1u"; } export function disableTerminalExtendedKeys(): string { - return "\u001B[>4;0m"; + return "\u001B[>4;0m\u001B[1u) sends plain Enter as ESC[13u +// or ESC[13;NUMBERu with extra flags; xterm sends ESC[13;2u for Shift. +const CSI_RETURN_RE = /^\u001B\[13;(\d+)[u~]$/; +const CSI_EXTENDED_RETURN_RE = /^\u001B\[27;(\d+);13~$/; + +function isReturn(raw: string): boolean { + if (raw === "\r") return true; + if (SHIFT_RETURN_SEQUENCES.has(raw)) return true; + if (META_RETURN_SEQUENCES.has(raw)) return true; + return CSI_RETURN_RE.test(raw) || CSI_EXTENDED_RETURN_RE.test(raw); +} const META_RETURN_SEQUENCES = new Set(["\u001B[13;3u", "\u001B[13;4u"]); const CTRL_LEFT_SEQUENCES = new Set(["\u001B[1;5D", "\u001B[5D"]); const CTRL_RIGHT_SEQUENCES = new Set(["\u001B[1;5C", "\u001B[5C"]); @@ -121,10 +161,10 @@ export function parseTerminalInput(data: Buffer | string): { input: string; key: end: END_SEQUENCES.has(raw), pageDown: raw === "\u001B[6~", pageUp: raw === "\u001B[5~", - return: raw === "\r" || SHIFT_RETURN_SEQUENCES.has(raw) || META_RETURN_SEQUENCES.has(raw), + return: isReturn(raw), escape: raw === "\u001B", ctrl: CTRL_LEFT_SEQUENCES.has(raw) || CTRL_RIGHT_SEQUENCES.has(raw), - shift: SHIFT_RETURN_SEQUENCES.has(raw), + shift: isShiftReturn(raw), tab: raw === "\t" || raw === "\u001B[Z", backspace: BACKSPACE_BYTES.has(raw), delete: FORWARD_DELETE_SEQUENCES.has(raw),