From 91be0eba77cd42ffe0148f7ca7eb0efae4e24709 Mon Sep 17 00:00:00 2001 From: Gergely Jutasi <28388707+0xGREG@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:03:45 +0200 Subject: [PATCH] fix(tui): use Kitty flag 1 on Windows to fix Shift+letter input WezTerm/ConPTY on Windows drops Shift+letter keypresses entirely when Kitty keyboard protocol flag 4 (report alternate keys) is set. Fall back to flag 1 (disambiguate only) on Windows so shifted printable keys are delivered as raw characters instead of being silently eaten. Also adds a defensive SHIFT_MAP fallback in decodeKittyPrintable for terminals that send CSI-u sequences with the shift modifier but omit the shifted_key secondary codepoint. --- packages/tui/src/keys.ts | 37 ++++++++++++++++++++++++++++++++++-- packages/tui/src/terminal.ts | 6 ++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/tui/src/keys.ts b/packages/tui/src/keys.ts index ba2cb46c2..8d556da48 100644 --- a/packages/tui/src/keys.ts +++ b/packages/tui/src/keys.ts @@ -307,6 +307,32 @@ const KITTY_MOD_CTRL = 4; const KITTY_MOD_SUPER = 8; const KITTY_MOD_NUM_LOCK = 128; const KITTY_LOCK_MASK = 64 + KITTY_MOD_NUM_LOCK; // Caps Lock + Num Lock +const SHIFT_MAP: Record = { + // US layout: unshifted codepoint → shifted codepoint + // digits + 48: 41, // 0 → ) + 49: 33, // 1 → ! + 50: 64, // 2 → @ + 51: 35, // 3 → # + 52: 36, // 4 → $ + 53: 37, // 5 → % + 54: 94, // 6 → ^ + 55: 38, // 7 → & + 56: 42, // 8 → * + 57: 40, // 9 → ( + // symbols + 45: 95, // - → _ + 61: 43, // = → + + 91: 123, // [ → { + 93: 125, // ] → } + 92: 124, // \ → | + 59: 58, // ; → : + 39: 34, // ' → " + 96: 126, // ` → ~ + 44: 60, // , → < + 46: 62, // . → > + 47: 63, // / → ? +}; const MODIFY_OTHER_KEYS_PATTERN = /^\x1b\[27;(\d+);(\d+)~$/; const KITTY_KEYPAD_OPERATOR_TEXT: Record = { 57410: "/", @@ -430,8 +456,15 @@ function decodeKittyPrintable(data: string): string | undefined { } let effectiveCodepoint = codepoint; - if (effectiveMod & KITTY_MOD_SHIFT && typeof shiftedKey === "number") { - effectiveCodepoint = shiftedKey; + if (effectiveMod & KITTY_MOD_SHIFT) { + if (typeof shiftedKey === "number") { + effectiveCodepoint = shiftedKey; + } else if (codepoint >= 97 && codepoint <= 122) { + effectiveCodepoint = codepoint - 32; + } else { + const shifted = SHIFT_MAP[codepoint]; + if (shifted !== undefined) effectiveCodepoint = shifted; + } } if (effectiveCodepoint >= 0xe000 && effectiveCodepoint <= 0xf8ff) { diff --git a/packages/tui/src/terminal.ts b/packages/tui/src/terminal.ts index d5204fe9c..c83e0adcd 100644 --- a/packages/tui/src/terminal.ts +++ b/packages/tui/src/terminal.ts @@ -1105,6 +1105,12 @@ export class ProcessTerminal implements Terminal { // Push level-2 to keep its shortcuts reporting consistently. this.#kittyEnableSeq = "\x1b[>7u"; this.#safeWrite(this.#kittyEnableSeq); + } else if (process.platform === "win32") { + // WezTerm/ConPTY on Windows drops Shift+letter keypresses entirely + // when flag 4 (report alternate keys) is set. Use flag 1 + // (disambiguate only) to keep shifted printable keys working. + this.#kittyEnableSeq = "\x1b[>1u"; + this.#safeWrite(this.#kittyEnableSeq); } else { // Disambiguate escape codes and report base-layout keys for physical // shortcut matching, without event reporting that caused regression #3259.