From fdb7346f3a15c5a5e0df32367fd1ffd455549e26 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 10 Mar 2026 00:27:27 -0700 Subject: [PATCH 1/2] Fix kitty keyboard disambiguate mode for space, enter, tab, backspace --- src/common/input/KittyKeyboard.test.ts | 31 +++++++++++++++++++------- src/common/input/KittyKeyboard.ts | 13 +++++++---- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index e495ac591..a0422ecb1 100644 --- a/src/common/input/KittyKeyboard.test.ts +++ b/src/common/input/KittyKeyboard.test.ts @@ -100,24 +100,24 @@ describe('KittyKeyboard', () => { assert.strictEqual(result.key, '\x1b[27u'); }); - it('Enter → CSI 13 u', () => { + it('Enter → legacy \\r', () => { const result = kitty.evaluate(createEvent({ key: 'Enter' }), flags); - assert.strictEqual(result.key, '\x1b[13u'); + assert.strictEqual(result.key, '\r'); }); - it('Tab → CSI 9 u', () => { + it('Tab → legacy \\t', () => { const result = kitty.evaluate(createEvent({ key: 'Tab' }), flags); - assert.strictEqual(result.key, '\x1b[9u'); + assert.strictEqual(result.key, '\t'); }); - it('Backspace → CSI 127 u', () => { + it('Backspace → legacy \\x7f', () => { const result = kitty.evaluate(createEvent({ key: 'Backspace' }), flags); - assert.strictEqual(result.key, '\x1b[127u'); + assert.strictEqual(result.key, '\x7f'); }); - it('Space → CSI 32 u', () => { + it('Space → plain space (text-generating key)', () => { const result = kitty.evaluate(createEvent({ key: ' ' }), flags); - assert.strictEqual(result.key, '\x1b[32u'); + assert.strictEqual(result.key, ' '); }); it('Shift+Tab → CSI 9;2 u', () => { @@ -134,6 +134,21 @@ describe('KittyKeyboard', () => { const result = kitty.evaluate(createEvent({ key: 'Escape', altKey: true }), flags); assert.strictEqual(result.key, '\x1b[27;3u'); }); + + it('Ctrl+Backspace → CSI 127;5 u', () => { + const result = kitty.evaluate(createEvent({ key: 'Backspace', ctrlKey: true }), flags); + assert.strictEqual(result.key, '\x1b[127;5u'); + }); + + it('Ctrl+Space → CSI 32;5 u', () => { + const result = kitty.evaluate(createEvent({ key: ' ', ctrlKey: true }), flags); + assert.strictEqual(result.key, '\x1b[32;5u'); + }); + + it('Alt+Space → CSI 32;3 u', () => { + const result = kitty.evaluate(createEvent({ key: ' ', altKey: true }), flags); + assert.strictEqual(result.key, '\x1b[32;3u'); + }); }); describe('navigation keys', () => { diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index b296695c5..606b121c3 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -457,9 +457,11 @@ export class KittyKeyboard { } else if (reportEventTypes) { useCsiU = true; } else if (flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES) { - if (keyCode === 27 || keyCode === 127 || keyCode === 13 || keyCode === 9 || keyCode === 32) { - useCsiU = true; - } else if (isFunc) { + // Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy + // mode" and Space is a text-generating key, so these skip the isFunc fast-path + // and only get CSI u when modifiers are present (handled below). + const isDisambiguateLegacy = keyCode === 13 || keyCode === 9 || keyCode === 127; + if (isFunc && !isDisambiguateLegacy) { useCsiU = true; } else if (modifiers > 0) { if (ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.key.length === 1) { @@ -474,7 +476,10 @@ export class KittyKeyboard { result.key = this._buildCsiUSequence(ev, keyCode, modifiers, eventType, flags, isFunc, isMod); result.cancel = true; } else { - if (ev.key.length === 1 && !ev.ctrlKey && !ev.altKey && !ev.metaKey) { + const legacyByte = keyCode === 13 ? '\r' : keyCode === 9 ? '\t' : keyCode === 127 ? '\x7f' : undefined; + if (legacyByte) { + result.key = legacyByte; + } else if (ev.key.length === 1 && !ev.ctrlKey && !ev.altKey && !ev.metaKey) { result.key = ev.key; } } From 6f3ce7b0febf2de350174ffd5e9bd4fe75e13acc Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 10 Mar 2026 00:30:29 -0700 Subject: [PATCH 2/2] comment --- src/common/input/KittyKeyboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index 606b121c3..76c468cc4 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -458,7 +458,7 @@ export class KittyKeyboard { useCsiU = true; } else if (flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES) { // Per spec, Enter/Tab/Backspace "still generate the same bytes as in legacy - // mode" and Space is a text-generating key, so these skip the isFunc fast-path + // mode" and consider space to be a text-generating key, so these skip the isFunc fast-path // and only get CSI u when modifiers are present (handled below). const isDisambiguateLegacy = keyCode === 13 || keyCode === 9 || keyCode === 127; if (isFunc && !isDisambiguateLegacy) {