[GH-ISSUE #758] Unify stdin parsing: single parser for all escape sequence types #977

Closed
opened 2026-03-14 09:11:26 +03:00 by kerem · 1 comment
Owner

Originally created by @simonklee on GitHub (Mar 1, 2026).
Original GitHub issue: https://github.com/anomalyco/opentui/issues/758

Originally assigned to: @simonklee on GitHub.

stdinListener in renderer.ts splits stdin data into two mutually exclusive paths before parsing:

stdin "data" event
  ├─ if handleMouseData(data) → return     [Path A: mouse]
  └─ else → _stdinBuffer.process(data)     [Path B: everything else]

This is an either/or dispatch at the chunk level. If a chunk starts with mouse sequences, the entire chunk is routed to Path A and any trailing non-mouse data (keyboard input, focus events, etc.) is silently dropped. If a chunk starts with non-mouse data, any mouse sequences later in the chunk go to Path B where StdinBuffer will split them out but they won't reach MouseParser.

This is the cause of #748, and other related issues when the OS joins different sequence types into the same read() buffer.

There are at least four independent parsers that don't coordinate:

  1. MouseParser.parseAllMouseEvents(): parses SGR/X10 mouse from raw
    data, only reached via Path A
  2. StdinBuffer.extractCompleteSequences(): generic escape sequence
    splitter that already understands CSI, OSC, DCS, APC, SS3, and even SGR mouse
    boundaries. Only reached via Path B.
  3. inputHandlers chain: capability responses, focus, theme mode handlers
    that pattern-match on sequences after StdinBuffer emits them
  4. TerminalPalette: registers its own stdin.on("data") listener
    directly on stdin, completely outside the normal flow

Related:

Originally created by @simonklee on GitHub (Mar 1, 2026). Original GitHub issue: https://github.com/anomalyco/opentui/issues/758 Originally assigned to: @simonklee on GitHub. `stdinListener` in `renderer.ts` splits stdin data into two mutually exclusive paths before parsing: ``` stdin "data" event ├─ if handleMouseData(data) → return [Path A: mouse] └─ else → _stdinBuffer.process(data) [Path B: everything else] ``` This is an either/or dispatch at the chunk level. If a chunk starts with mouse sequences, the entire chunk is routed to Path A and any trailing non-mouse data (keyboard input, focus events, etc.) is silently dropped. If a chunk starts with non-mouse data, any mouse sequences later in the chunk go to Path B where `StdinBuffer` will split them out but they won't reach `MouseParser`. This is the cause of #748, and other related issues when the OS joins different sequence types into the same `read()` buffer. There are at least four independent parsers that don't coordinate: 1. `MouseParser.parseAllMouseEvents()`: parses SGR/X10 mouse from raw data, only reached via Path A 2. `StdinBuffer.extractCompleteSequences()`: generic escape sequence splitter that already understands CSI, OSC, DCS, APC, SS3, and even SGR mouse boundaries. Only reached via Path B. 3. `inputHandlers` chain: capability responses, focus, theme mode handlers that pattern-match on sequences after `StdinBuffer` emits them 4. `TerminalPalette`: registers its own `stdin.on("data")` listener directly on stdin, completely outside the normal flow Related: - https://github.com/anomalyco/opentui/pull/757 - https://github.com/anomalyco/opentui/pull/749
kerem 2026-03-14 09:11:26 +03:00
  • closed this issue
  • added the
    core
    bug
    labels
Author
Owner

@simonklee commented on GitHub (Mar 9, 2026):

Closed by #770

<!-- gh-comment-id:4026075190 --> @simonklee commented on GitHub (Mar 9, 2026): Closed by #770
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#977
No description provided.