[PR #461] fix: Handle combined mouse event and paste sequences in stdin buffer #553

Open
opened 2026-03-02 23:47:03 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opentui/pull/461
Author: @d3vr
Created: 12/31/2025
Status: 🔄 Open

Base: mainHead: main


📝 Commits (1)

  • c859dfe fix: Handle combined mouse event and paste sequences in stdin buffer

📊 Changes

4 files changed (+197 additions, -27 deletions)

View changed files

packages/core/src/lib/parse.mouse.test.ts (+142 -0)
📝 packages/core/src/lib/parse.mouse.ts (+40 -15)
📝 packages/core/src/renderer.ts (+11 -2)
📝 packages/core/src/testing/integration.test.ts (+4 -10)

📄 Description

Drag-and-drop file paths from a file manager into terminals using OpenTUI-based applications (like OpenCode) was not working in Alacritty, while it worked correctly in Ghostty/Kitty/others.

Root Cause Analysis

When a file is dragged and dropped into a terminal, the terminal sends the file path as a bracketed paste sequence (\x1b[200~...\x1b[201~). However, different terminals emit these sequences differently:

Terminal Behavior
Ghostty Sends paste in a separate chunk - no mouse event combined
Alacritty Combines mouse event + paste in a single chunk

Alacritty's combined sequence:

\x1b[<35;37;18M\x1b[200~/home/user/file.jpg \x1b[201~
└─── Mouse ───┘└────────── Paste data ──────────┘

The bug occurred because:

  1. stdinListener() called handleMouseData(data) with the full buffer
  2. MouseParser.parseMouseEvent() found the mouse event using regex match() (not anchored)
  3. handleMouseData() returned true, signaling the entire buffer was handled
  4. The paste data following the mouse event was discarded

Solution

Added a new method parseMouseEventWithConsumed() to MouseParser that returns both the parsed event and the number of bytes consumed. This allows the caller to process any remaining data in the buffer.

Key changes:

  1. parse.mouse.ts:

    • Added MouseParseResult type: { event: RawMouseEvent, consumed: number }
    • Added parseMouseEventWithConsumed() method with ^-anchored regex
    • Refactored parseMouseEvent() to call the new method (maintains backward compatibility, since the method was exported, I erred on the safe side).
  2. renderer.ts:

    • Updated handleMouseData() to use parseMouseEventWithConsumed()
    • After parsing mouse event, remaining bytes are passed to _stdinBuffer.process()

Flow after fix:

1. stdinListener receives: [mouse event][paste data]
2. handleMouseData() calls parseMouseEventWithConsumed()
3. Returns: { event: {...}, consumed: 14 }
4. Slices remaining data: buffer.slice(14) → paste sequence
5. Passes remaining to _stdinBuffer.process() → paste event fires ✓
6. Mouse event is then handled normally

Testing

  • Added new unit test suite parse.mouse.test.ts (11 tests)
  • Updated parseAllEvents helper in integration tests to use the new method
  • Manually verified drag-and-drop works in Alacritty with OpenCode
  • Affects any OpenTUI-based application running in Alacritty terminal
  • May fix similar issues with other terminals that combine escape sequences

🔄 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/461 **Author:** [@d3vr](https://github.com/d3vr) **Created:** 12/31/2025 **Status:** 🔄 Open **Base:** `main` ← **Head:** `main` --- ### 📝 Commits (1) - [`c859dfe`](https://github.com/anomalyco/opentui/commit/c859dfe84aa12ef1773e13303f224974a1233a8d) fix: Handle combined mouse event and paste sequences in stdin buffer ### 📊 Changes **4 files changed** (+197 additions, -27 deletions) <details> <summary>View changed files</summary> ➕ `packages/core/src/lib/parse.mouse.test.ts` (+142 -0) 📝 `packages/core/src/lib/parse.mouse.ts` (+40 -15) 📝 `packages/core/src/renderer.ts` (+11 -2) 📝 `packages/core/src/testing/integration.test.ts` (+4 -10) </details> ### 📄 Description Drag-and-drop file paths from a file manager into terminals using OpenTUI-based applications (like OpenCode) was not working in **Alacritty**, while it worked correctly in Ghostty/Kitty/others. ### Root Cause Analysis When a file is dragged and dropped into a terminal, the terminal sends the file path as a **bracketed paste** sequence (`\x1b[200~...\x1b[201~`). However, different terminals emit these sequences differently: | Terminal | Behavior | |----------|----------| | **Ghostty** | Sends paste in a **separate chunk** - no mouse event combined | | **Alacritty** | Combines mouse event + paste in a **single chunk** | **Alacritty's combined sequence:** ``` \x1b[<35;37;18M\x1b[200~/home/user/file.jpg \x1b[201~ └─── Mouse ───┘└────────── Paste data ──────────┘ ``` The bug occurred because: 1. `stdinListener()` called `handleMouseData(data)` with the full buffer 2. `MouseParser.parseMouseEvent()` found the mouse event using regex `match()` (not anchored) 3. `handleMouseData()` returned `true`, signaling the entire buffer was handled 4. **The paste data following the mouse event was discarded** ### Solution Added a new method `parseMouseEventWithConsumed()` to `MouseParser` that returns both the parsed event **and** the number of bytes consumed. This allows the caller to process any remaining data in the buffer. **Key changes:** 1. **`parse.mouse.ts`**: - Added `MouseParseResult` type: `{ event: RawMouseEvent, consumed: number }` - Added `parseMouseEventWithConsumed()` method with `^`-anchored regex - Refactored `parseMouseEvent()` to call the new method (maintains backward compatibility, since the method was exported, I erred on the safe side). 2. **`renderer.ts`**: - Updated `handleMouseData()` to use `parseMouseEventWithConsumed()` - After parsing mouse event, remaining bytes are passed to `_stdinBuffer.process()` **Flow after fix:** ``` 1. stdinListener receives: [mouse event][paste data] 2. handleMouseData() calls parseMouseEventWithConsumed() 3. Returns: { event: {...}, consumed: 14 } 4. Slices remaining data: buffer.slice(14) → paste sequence 5. Passes remaining to _stdinBuffer.process() → paste event fires ✓ 6. Mouse event is then handled normally ``` ### Testing - Added new unit test suite `parse.mouse.test.ts` (11 tests) - Updated `parseAllEvents` helper in integration tests to use the new method - Manually verified drag-and-drop works in Alacritty with OpenCode ### Related - Affects any OpenTUI-based application running in Alacritty terminal - May fix similar issues with other terminals that combine escape sequences --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#553
No description provided.