Merge pull request #98 from Vasanthdev2004/windows-csiu-input-fix

fix: support CSI-u printable input on Windows
This commit is contained in:
Kevin Codex
2026-04-02 13:30:36 +08:00
committed by GitHub
2 changed files with 76 additions and 1 deletions

View File

@@ -0,0 +1,49 @@
import { expect, test } from 'bun:test'
import {
INITIAL_STATE,
parseMultipleKeypresses,
type ParsedKey,
} from './parse-keypress.ts'
import { InputEvent } from './events/input-event.ts'
function parseInputEvent(sequence: string): InputEvent {
const [items] = parseMultipleKeypresses(INITIAL_STATE, sequence)
expect(items).toHaveLength(1)
const item = items[0]
expect(item?.kind).toBe('key')
return new InputEvent(item as ParsedKey)
}
test('treats CSI-u modifier 0 as unmodified printable input', () => {
const event = parseInputEvent('\x1b[47;0u')
expect(event.input).toBe('/')
expect(event.key.ctrl).toBe(false)
expect(event.key.meta).toBe(false)
expect(event.key.shift).toBe(false)
expect(event.key.super).toBe(false)
})
test('preserves printable Unicode CSI-u input', () => {
const event = parseInputEvent('\x1b[231u')
expect(event.input).toBe('ç')
expect(event.key.ctrl).toBe(false)
expect(event.key.meta).toBe(false)
expect(event.key.shift).toBe(false)
expect(event.key.super).toBe(false)
})
test('preserves printable Unicode CSI-u input with explicit modifier 0', () => {
const event = parseInputEvent('\x1b[231;0u')
expect(event.input).toBe('ç')
expect(event.key.ctrl).toBe(false)
expect(event.key.meta).toBe(false)
expect(event.key.shift).toBe(false)
expect(event.key.super).toBe(false)
})

View File

@@ -468,7 +468,10 @@ function decodeModifier(modifier: number): {
ctrl: boolean ctrl: boolean
super: boolean super: boolean
} { } {
const m = modifier - 1 // Some Windows VT stacks use 0 instead of 1 for an unmodified CSI-u key.
// Clamp to the protocol default so plain printable keys don't look like
// ctrl+meta+shift+super all at once.
const m = Math.max(modifier, 1) - 1
return { return {
shift: !!(m & 1), shift: !!(m & 1),
meta: !!(m & 2), meta: !!(m & 2),
@@ -477,6 +480,14 @@ function decodeModifier(modifier: number): {
} }
} }
function isPrivateUseCodepoint(codepoint: number): boolean {
return (
(codepoint >= 0xe000 && codepoint <= 0xf8ff) ||
(codepoint >= 0xf0000 && codepoint <= 0xffffd) ||
(codepoint >= 0x100000 && codepoint <= 0x10fffd)
)
}
/** /**
* Map keycode to key name for modifyOtherKeys/CSI u sequences. * Map keycode to key name for modifyOtherKeys/CSI u sequences.
* Handles both ASCII keycodes and Kitty keyboard protocol functional keys. * Handles both ASCII keycodes and Kitty keyboard protocol functional keys.
@@ -536,6 +547,21 @@ function keycodeToName(keycode: number): string | undefined {
if (keycode >= 32 && keycode <= 126) { if (keycode >= 32 && keycode <= 126) {
return String.fromCharCode(keycode).toLowerCase() return String.fromCharCode(keycode).toLowerCase()
} }
// CSI-u can carry printable Unicode codepoints directly on some
// Windows terminals and keyboard layouts. Keep kitty's private-use
// functional key range excluded so special keys still stay non-text.
if (
keycode > 0x1f &&
keycode !== 0x7f &&
(keycode < 0x80 || keycode > 0x9f) &&
keycode <= 0x10ffff &&
(keycode < 0xd800 || keycode > 0xdfff) &&
!isPrivateUseCodepoint(keycode)
) {
return String.fromCodePoint(keycode)
}
return undefined return undefined
} }
} }