|
1 | 1 | /** |
2 | | - * useInput compat wrapper — Phase 1 |
| 2 | + * useInput compat wrapper — Phase 2 |
3 | 3 | * |
4 | 4 | * Wraps ink v7's useInput (which provides 2 args: input, key) to provide |
5 | 5 | * a 3-argument handler (input, key, event) expected by CA's CLI code. |
|
8 | 8 | * + isPasted flag) that textInput.tsx and other CA components depend on. |
9 | 9 | * Without this wrapper, `event` is `undefined` and any access to |
10 | 10 | * `event.keypress` throws TypeError, crashing the React tree. |
| 11 | + * |
| 12 | + * ## SGR Mouse Filtering |
| 13 | + * SGR extended mouse escape sequences (CSI < btn ; x ; y M/m) arrive |
| 14 | + * through the same stdin stream as keyboard input when mouse tracking is |
| 15 | + * enabled. The MouseProvider (which imports useInput directly from ink) |
| 16 | + * parses these into typed mouse events. However, ink v7's useInput is |
| 17 | + * multicast — ALL handlers receive ALL input. Without filtering, SGR |
| 18 | + * sequences leak into TextInput and appear as garbled text. |
| 19 | + * |
| 20 | + * We filter SGR sequences here so they never reach CA component handlers. |
| 21 | + * MouseProvider is unaffected because it imports useInput from ink directly. |
11 | 22 | */ |
12 | 23 | import { useInput as inkUseInput } from 'ink'; |
13 | 24 | import type { Key as InkKey } from 'ink'; |
14 | 25 | import { InputEvent, type Key } from './types.js'; |
15 | 26 |
|
16 | 27 | type InputHandler = (input: string, key: Key, event: InputEvent) => void; |
17 | 28 |
|
| 29 | +/** SGR extended mouse escape sequence: CSI < btn ; x ; y M/m */ |
| 30 | +const SGR_MOUSE_RE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/; |
| 31 | + |
18 | 32 | /** |
19 | 33 | * Wraps ink v7's 2-arg useInput to provide CA's 3-arg handler signature. |
20 | 34 | * |
21 | 35 | * For each input event, constructs an InputEvent with: |
22 | 36 | * - keypress.raw = input (the raw stdin string) |
23 | 37 | * - keypress.isPasted = false (bracketed paste detection deferred) |
24 | 38 | * |
25 | | - * The Key type is cast to our extended type with wheelUp/wheelDown stubs. |
| 39 | + * SGR mouse escape sequences are silently dropped — they are consumed by |
| 40 | + * the MouseProvider (which uses ink's useInput directly) and should not |
| 41 | + * reach CA component handlers such as TextInput. |
26 | 42 | */ |
27 | 43 | export function useInput(inputHandler: InputHandler, options?: { isActive?: boolean }): void { |
28 | 44 | inkUseInput((input: string, key: InkKey) => { |
| 45 | + // Drop SGR mouse escape sequences — they are handled by MouseProvider |
| 46 | + // which imports useInput directly from 'ink'. Letting them through causes |
| 47 | + // garbled text in TextInput (e.g. "[<64;60;19M" on touch scroll). |
| 48 | + if (SGR_MOUSE_RE.test(input)) return; |
| 49 | + |
29 | 50 | const event = new InputEvent(input, key as Key); |
30 | 51 | inputHandler(input, key as Key, event); |
31 | 52 | }, options as any); |
|
0 commit comments