Context
There's currently no mechanism to receive mouse events. The TUI reads raw stdin chunks as strings in for await (const chunk of process.stdin) (src/tui.ts) and matches them against known keyboard escape sequences. Mouse clicks/wheel arrive on this same channel as SGR sequences (\x1b[<b;x;yM / ...m) once mouse reporting is enabled on the terminal.
Part of #166.
Solution
- Enable mouse tracking (
\x1b[?1000h\x1b[?1006h) on stdout when runInteractive() starts, next to the existing process.stdin.setRawMode(true).
- Disable it (
\x1b[?1000l\x1b[?1006l) in every exit path — the exit() cleanup function, and any other place that calls setRawMode(false) — so a killed/crashed process never leaves the user's terminal stuck in mouse-reporting mode.
- Add a pure parser function (e.g.
parseMouseEvent(sequence: string)) that recognizes SGR mouse sequences and returns a structured event ({ button, x, y, isRelease } or null for anything else). Place it in a new small module (e.g. src/render/mouse.ts or src/mouse.ts, to be decided based on whether it needs types.ts additions) so it can be unit-tested independently of the stdin loop.
- Wire the parser into the main input loop in
tui.ts: unrecognized/malformed sequences are ignored (no throw), preserving graceful degradation for terminals without mouse support.
Acceptance Criteria
Definition of Done
Context
There's currently no mechanism to receive mouse events. The TUI reads raw stdin chunks as strings in
for await (const chunk of process.stdin)(src/tui.ts) and matches them against known keyboard escape sequences. Mouse clicks/wheel arrive on this same channel as SGR sequences (\x1b[<b;x;yM/...m) once mouse reporting is enabled on the terminal.Part of #166.
Solution
\x1b[?1000h\x1b[?1006h) on stdout whenrunInteractive()starts, next to the existingprocess.stdin.setRawMode(true).\x1b[?1000l\x1b[?1006l) in every exit path — theexit()cleanup function, and any other place that callssetRawMode(false)— so a killed/crashed process never leaves the user's terminal stuck in mouse-reporting mode.parseMouseEvent(sequence: string)) that recognizes SGR mouse sequences and returns a structured event ({ button, x, y, isRelease }ornullfor anything else). Place it in a new small module (e.g.src/render/mouse.tsorsrc/mouse.ts, to be decided based on whether it needstypes.tsadditions) so it can be unit-tested independently of the stdin loop.tui.ts: unrecognized/malformed sequences are ignored (no throw), preserving graceful degradation for terminals without mouse support.Acceptance Criteria
nulland are silently ignored (no crash, no visual glitch).Definition of Done
bun testpasses, including a new companion*.test.tsfor the parser covering: valid click press/release, valid wheel up/down, malformed sequence, empty string, partial/truncated sequence.bun run lintpasses.bun run format:checkpasses.bun run knippasses (no unused exports/imports).bun run build.tscompiles successfully.