fix(tui): drop corrupted SGR mouse garbage that leaked into composer - #34317
fix(tui): drop corrupted SGR mouse garbage that leaked into composer#34317konsisumer wants to merge 1 commit into
Conversation
Under heavy render backpressure in tmux/WezTerm, dozens of SGR mouse events can accumulate in the tty buffer. The 50ms flush timer splits ESC from its continuation, so subsequent reads arrive as raw text. parseTextWithSgrMouseFragments recovers adjacent valid fragments as a burst, but the corrupted/partial bytes before or after those matches (e.g. '5;34M;34M' — missing a semicolon pair) were passed verbatim to parseKeypress, which emitted them as garbage text into the composer. Add MOUSE_GARBAGE_RE (/^[\d;Mm\[<]+$/) and apply it to any gap or tail text adjacent to a consumed burst: if the slice consists solely of characters found in SGR mouse event streams, silently discard it rather than leaking it as visible input. Real trailing text (e.g. 'typed') is unaffected because it contains non-mouse characters. Fixes NousResearch#18658
|
Thanks @alt-glitch for the chain context. I see two open PRs already targeting the same root cause:
This PR uses a Should I close this PR in favor of one of the existing ones, or is there value in keeping all three open while a canonical fix is chosen? Happy to close and redirect effort to whichever approach you prefer. |
|
Closing as redundant against current This is part of the TUI/CLI ANSI/SGR/escape-leak cluster, most of which is already solved on
Your work here overlapped this already-merged coverage. Thanks for the contribution and for helping surface the leak class — it's appreciated. |
What changed and why
Under heavy render backpressure in tmux/WezTerm, dozens of SGR mouse events accumulate in the tty buffer. The 50 ms flush timer splits
ESCfrom its continuation bytes, so subsequent reads arrive as plain text.parseTextWithSgrMouseFragmentsalready recovers adjacent valid fragments as a burst, but the corrupted/partial bytes surrounding those matches (e.g.5;34M;34M— fragments with only one semicolon, or two consecutiveMterminators) were passed verbatim toparseKeypress, which emitted them as garbage text into the composer.This PR adds
MOUSE_GARBAGE_RE(/^[\d;Mm\[<]+$/) and applies it to any gap or tail text adjacent to a consumed burst: if the slice consists solely of characters that appear in SGR mouse event streams, it is silently discarded. Real trailing text (e.g. user-typed'hello') is unaffected because it contains non-mouse characters.The bug was reported by two independent users:
Both environments confirm the leakage is not tmux-specific but inherent to the fragment-recovery fallback when mouse events arrive faster than the event loop can drain stdin.
How to test
hermes --tuiinside tmux or WezTerm with mouse tracking enabled (defaultdisplay.tui_mouse: trueorall)›) stays clean — nodigit;digit;digitMgarbagecd ui-tui && npm test -- packages/hermes-ink/src/ink/parse-keypress.test.tsWhat platforms tested on
Fixes #18658