fix(keypress): handle unsupported Kitty CSI-u keys and recover plain text

- Add helper functions for better code organization (createPrintableKey,
  getCompleteCsiSequenceLength, parsePlainTextPrefix)
- Drop unsupported Kitty CSI-u keys without blocking subsequent input
- Recover plain text that arrives in same chunk after unsupported CSI-u keys
- Add comprehensive tests for edge cases (CAPS_LOCK, event metadata variants)

Improves robustness of Kitty keyboard protocol parsing by gracefully
handling unsupported key codes and ensuring plain text input is not lost.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
DragonnZhang 2026-03-17 14:02:41 +08:00
parent ce6be9aadd
commit 7886ec6c8d
2 changed files with 188 additions and 40 deletions

View file

@ -1367,6 +1367,75 @@ describe('KeypressContext - Kitty Protocol', () => {
}),
);
});
it('drops unsupported Kitty CSI-u keys without blocking later input', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), { wrapper });
act(() => result.current.subscribe(keyHandler));
act(() => stdin.sendKittySequence(`\x1b[57358u`)); // CAPS_LOCK
act(() =>
stdin.pressKey({
name: 'a',
ctrl: false,
meta: false,
shift: false,
paste: false,
sequence: 'a',
}),
);
expect(keyHandler).toHaveBeenCalledTimes(1);
expect(keyHandler).toHaveBeenCalledWith(
expect.objectContaining({
name: 'a',
sequence: 'a',
}),
);
});
it('recovers plain text that arrives in the same chunk after an unsupported CSI-u key', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), { wrapper });
act(() => result.current.subscribe(keyHandler));
act(() =>
stdin.pressKey({
name: '',
ctrl: false,
meta: false,
shift: false,
paste: false,
sequence: '\x1b[57358ua',
}),
);
expect(keyHandler).toHaveBeenCalledTimes(1);
expect(keyHandler).toHaveBeenCalledWith(
expect.objectContaining({
name: 'a',
sequence: 'a',
kittyProtocol: true,
}),
);
});
it('drops unsupported CSI-u variants with event metadata and keeps parsing', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), { wrapper });
act(() => result.current.subscribe(keyHandler));
act(() => stdin.sendKittySequence(`\x1b[57358;1:1u\x1b[100u`));
expect(keyHandler).toHaveBeenCalledTimes(1);
expect(keyHandler).toHaveBeenCalledWith(
expect.objectContaining({
name: 'd',
sequence: 'd',
kittyProtocol: true,
}),
);
});
});
describe('Kitty keypad private-use keys', () => {