[PR #779] [CLOSED] fix(core): prevent mouse escape sequences from leaking into keyboard input #1556

Closed
opened 2026-03-14 09:43:01 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opentui/pull/779
Author: @Jerwand
Created: 3/5/2026
Status: Closed

Base: mainHead: fix/mouse-tracking-leak


📝 Commits (1)

  • 4bd8ccc fix(core): prevent mouse escape sequences from leaking into keyboard input

📊 Changes

4 files changed (+223 additions, -15 deletions)

View changed files

📝 packages/core/src/lib/parse.mouse.test.ts (+109 -1)
📝 packages/core/src/lib/parse.mouse.ts (+70 -6)
📝 packages/core/src/renderer.ts (+39 -6)
📝 packages/core/src/tests/renderer.mouse.test.ts (+5 -2)

📄 Description

Summary

Fixes the root cause of mouse escape sequences (e.g. [555;129;29M) appearing as typed text in input fields during normal use. This happens when stdin delivers mouse data split across chunk boundaries, which occurs regularly under high mouse activity.

  • Add parseAllMouseEventsWithOffset() to MouseParser — returns consumed byte count and partial-sequence detection so callers know exactly what was parsed and what remains
  • Add isPartialMouseSequence() to detect truncated SGR/Basic mouse sequences at chunk boundaries (e.g. \x1b[<35;20 missing the terminating M)
  • Add _mousePartialBuffer to CliRenderer that preserves incomplete mouse sequences between stdin chunks
  • Rewrite stdinListener to properly route unconsumed non-mouse data to StdinBuffer instead of silently dropping it
  • Update split-height test: mouse events above the render area are now correctly ignored instead of leaking to input handlers

Root cause

The old stdinListener had an all-or-nothing design:

// OLD (buggy)
if (this._useMouse && this.handleMouseData(data)) {
  return  // entire chunk treated as mouse — any non-mouse remainder is lost
}
this._stdinBuffer.process(data)  // entire chunk treated as keyboard

When a mouse sequence was split across two stdin chunks (e.g. \x1b[<35;20;5M\x1b[<35 followed by ;21;5M), the partial tail was dropped and its completion in the next chunk leaked into the keyboard handler as literal text.

Three scenarios this fixes

Scenario Before (bug) After (fix)
Split: \x1b[<35;20;5M\x1b[<35 + ;21;5M ;21;5M appears as typed text Buffered and correctly parsed
Mixed: \x1b[<35;20;5Mhello hello silently lost Mouse → handler, hello → keyboard
Rapid: 10 events + partial Last event lost All 11 events parsed correctly

Test plan

  • All 81 existing mouse parser tests pass
  • 10 new tests for parseAllMouseEventsWithOffset covering split sequences, partial detection, mixed data, and chunk reassembly
  • All 35 renderer mouse tests pass (1 test updated to match corrected behavior)
  • All 180 keyboard/keypress tests pass (no regressions)

Fixes https://github.com/anomalyco/opencode/issues/8614
Related: https://github.com/anomalyco/opencode/issues/7316, https://github.com/anomalyco/opencode/issues/11636


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/anomalyco/opentui/pull/779 **Author:** [@Jerwand](https://github.com/Jerwand) **Created:** 3/5/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/mouse-tracking-leak` --- ### 📝 Commits (1) - [`4bd8ccc`](https://github.com/anomalyco/opentui/commit/4bd8cccf1733d91a0dcb021495653eb2eed2429a) fix(core): prevent mouse escape sequences from leaking into keyboard input ### 📊 Changes **4 files changed** (+223 additions, -15 deletions) <details> <summary>View changed files</summary> 📝 `packages/core/src/lib/parse.mouse.test.ts` (+109 -1) 📝 `packages/core/src/lib/parse.mouse.ts` (+70 -6) 📝 `packages/core/src/renderer.ts` (+39 -6) 📝 `packages/core/src/tests/renderer.mouse.test.ts` (+5 -2) </details> ### 📄 Description ## Summary Fixes the root cause of mouse escape sequences (e.g. `[555;129;29M`) appearing as typed text in input fields during normal use. This happens when stdin delivers mouse data split across chunk boundaries, which occurs regularly under high mouse activity. - Add `parseAllMouseEventsWithOffset()` to `MouseParser` — returns consumed byte count and partial-sequence detection so callers know exactly what was parsed and what remains - Add `isPartialMouseSequence()` to detect truncated SGR/Basic mouse sequences at chunk boundaries (e.g. `\x1b[<35;20` missing the terminating `M`) - Add `_mousePartialBuffer` to `CliRenderer` that preserves incomplete mouse sequences between stdin chunks - Rewrite `stdinListener` to properly route unconsumed non-mouse data to `StdinBuffer` instead of silently dropping it - Update split-height test: mouse events above the render area are now correctly ignored instead of leaking to input handlers ## Root cause The old `stdinListener` had an all-or-nothing design: ```typescript // OLD (buggy) if (this._useMouse && this.handleMouseData(data)) { return // entire chunk treated as mouse — any non-mouse remainder is lost } this._stdinBuffer.process(data) // entire chunk treated as keyboard ``` When a mouse sequence was split across two stdin chunks (e.g. `\x1b[<35;20;5M\x1b[<35` followed by `;21;5M`), the partial tail was dropped and its completion in the next chunk leaked into the keyboard handler as literal text. ## Three scenarios this fixes | Scenario | Before (bug) | After (fix) | |----------|-------------|-------------| | Split: `\x1b[<35;20;5M\x1b[<35` + `;21;5M` | `;21;5M` appears as typed text | Buffered and correctly parsed | | Mixed: `\x1b[<35;20;5Mhello` | `hello` silently lost | Mouse → handler, `hello` → keyboard | | Rapid: 10 events + partial | Last event lost | All 11 events parsed correctly | ## Test plan - [x] All 81 existing mouse parser tests pass - [x] 10 new tests for `parseAllMouseEventsWithOffset` covering split sequences, partial detection, mixed data, and chunk reassembly - [x] All 35 renderer mouse tests pass (1 test updated to match corrected behavior) - [x] All 180 keyboard/keypress tests pass (no regressions) Fixes https://github.com/anomalyco/opencode/issues/8614 Related: https://github.com/anomalyco/opencode/issues/7316, https://github.com/anomalyco/opencode/issues/11636 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-03-14 09:43:01 +03:00
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#1556
No description provided.