diff --git a/src/common/input/KittyKeyboard.test.ts b/src/common/input/KittyKeyboard.test.ts index e495ac591e..a0422ecb19 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 b296695c58..76c468cc48 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 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) { 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; } }