Add kitty keyboard protocol support to vue-tui, matching Ink's implementation. This covers three layers: protocol lifecycle (enable/disable/auto-detect), useInput Key interface extension, and comprehensive test backfill. Parser support covers the default `disambiguateEscapeCodes` and `reportEventTypes` flags; advanced flags are accepted but experimental.
The kitty keyboard protocol is an opt-in terminal enhancement that provides disambiguated key events, additional modifiers (super, hyper, capsLock, numLock), event types (press/repeat/release), and text-as-codepoints fields. vue-tui already has the parsing layer (parse-keypress.ts) but lacks the terminal handshake and useInput integration.
**Parser support note**: The current parse-keypress.ts parser fully supports `disambiguateEscapeCodes` (the default and most useful flag). The `reportEventTypes` flag is also supported (press/repeat/release). The `reportAlternateKeys`, `reportAllKeysAsEscapeCodes`, and `reportAssociatedText` flags are accepted in the options but the parser may not handle all edge forms they produce (e.g., colon-separated alternate key fields). These flags are exposed for forward compatibility but users should treat them as experimental until parser coverage is verified.
Functions for detecting terminal responses to the `\x1b[?u` capability query:
-`matchKittyQueryResponse(buffer, startIndex)` — detects `\x1b[?<digits>u` pattern in a byte buffer. Returns `{state: 'complete', endIndex}` or `{state: 'partial'}` or `undefined`.
-`hasCompleteKittyQueryResponse(buffer)` — scans entire buffer for any complete response.
-`stripKittyQueryResponsesAndTrailingPartial(buffer)` — removes complete responses and trailing partial sequences, returns remaining bytes to re-emit to the input pipeline. A "partial" sequence is `\x1b[?` followed by at least one digit but no terminator (`\x1b[?1` without `u`). The prefix `\x1b[?` alone (no digits) is NOT considered partial — it's not a query response at all and is preserved in the output.
**Raw mode ownership**: The controller does NOT acquire or release raw mode. It attaches a temporary `data` listener to stdin for detection, matching Ink's approach. Raw mode is managed exclusively by `createStdinController` / `useInput`. In practice, if useInput hasn't enabled raw mode yet, the terminal query response may be buffered by the kernel's line discipline and detection times out — this is acceptable because `mode: 'auto'` gracefully degrades to "no kitty support" on timeout. Forced mode (`mode: 'enabled'`) bypasses detection entirely.
The key change: when kitty protocol is active, non-printable keys (capslock, media keys, F13+, modifier-only keys) produce empty input instead of leaking raw escape sequences. The `nonAlphanumericKeys` filter only applies to legacy sequences.
Tests kitty parsing in isolation — no rendering, no Vue. Each test calls `parseKeypress()` directly with a CSI u sequence and asserts the returned keypress object.
Helper function `kittyKey(codepoint, modifiers?, eventType?, textCodepoints?)` constructs CSI u sequences for testing.
**Basic character + modifier parsing (11 tests):**
- Simple character 'a' (`\x1b[97u`)
- Uppercase with shift (`\x1b[65;2u`)
- Ctrl modifier (`\x1b[97;5u`)
- Alt/option modifier (`\x1b[97;3u`)
- Super modifier (`\x1b[97;9u`)
- Hyper modifier (`\x1b[97;17u`)
- Meta modifier (`\x1b[97;33u`)
- Caps lock flag (`\x1b[97;65u`)
- Num lock flag (`\x1b[97;129u`)
- Combined: ctrl+shift (`\x1b[97;6u`)
- Combined: super+ctrl (`\x1b[97;13u`)
**Special keys (7 tests):**
- Escape (codepoint 27)
- Return/enter (codepoint 13)
- Tab (codepoint 9)
- Backspace (codepoint 8)
- Backspace (codepoint 127)
- Legacy meta+backspace (0x1b 0x7f)
- Space (codepoint 32)
**Event types (3 tests):**
- Press (eventType 1)
- Repeat (eventType 2)
- Release (eventType 3)
**Text & unicode (8 tests):**
- Number keys
- Special character (@)
- Ctrl+letter via codepoint 1-26
- Sequence and raw preservation
- Text-as-codepoints: single, multiple, supplementary unicode
- Text defaults to character from codepoint
**Arrow & function keys (5 tests):**
- Arrow keys with event type (CSI enhanced special key format)
- Arrow keys with modifiers
- Home and end keys
- Tilde-terminated special keys (delete, insert, pageup, f5)
- Terminal responds after 200ms timeout — protocol not enabled (late response bytes flow to normal input pipeline as an unknown escape sequence, which is harmless since `\x1b[?1u` does not match the kitty CSI u parser regex)
- The kitty-keyboard.ts `kittyModifiers` constant already exists in parse-keypress.ts. The new module only needs the flag constants and lifecycle logic; it imports nothing from parse-keypress.ts.
- The `isKittyProtocol`, `isPrintable`, `text`, `super`, `hyper`, `capsLock`, `numLock`, `eventType` fields are already set by `parseKittyKeypress()` in parse-keypress.ts. No changes needed to the parser.
- Auto-detect's stdin `data` listener is temporary (removed after detection completes or times out). It runs during the brief init window. See the race condition note below for the overlap scenario with useInput's listener.
- The `stdin.unshift()` call to re-emit non-query bytes pushes them back to the front of the readable stream. After the kitty detection listener is removed, the re-emitted bytes are picked up by the normal input pipeline (createStdinController's handleData) on the next read.
- **Known race condition (matches Ink)**: The detection `data` listener and useInput's `data` listener can briefly coexist if a component mounts and calls `acquireRawMode()` within the 200ms detection window. In this scenario: (a) query response bytes could leak to useInput (they'd be treated as unknown escape sequences), (b) user input bytes could be delivered twice (once by each listener, plus once more after unshift). This same race exists in Ink's implementation. In practice, most terminals respond to the query synchronously or within a few ms, so detection completes before useInput effects fire. The mitigation is ordering: `kittyController.init()` runs before `originalMount()`, so detection starts before Vue components mount. An end-to-end test must verify that user bytes sent during detection are delivered to useInput exactly once. If the race proves problematic in practice, the fix is to integrate detection into the stdin controller's input pipeline (filter query responses inline rather than using a separate listener).