Skip to content

fix: filter mouse escape fragments leaking into dialogs on Linux#161

Open
jacola wants to merge 1 commit intomarcus:mainfrom
jacola:mouse-invstigation
Open

fix: filter mouse escape fragments leaking into dialogs on Linux#161
jacola wants to merge 1 commit intomarcus:mainfrom
jacola:mouse-invstigation

Conversation

@jacola
Copy link

@jacola jacola commented Feb 13, 2026

Summary

Screenshot From 2026-02-13 18-55-38

Fix mouse escape sequence fragments leaking into dialogs as garbage text on Linux (Gnome with Ptyxis emulator). On Linux, terminal emulators split SGR mouse sequences across read() boundaries, causing Bubble Tea to deliver fragments like [<67;78;9M as KeyMsg instead of MouseMsg. These fragments appear as typed garbage in modal text inputs and can trigger unintended keybindings (e.g., [prev-tab).

Root cause: The existing isMouseEscapeSequence() filter was too strict (required trailing M/m, missing truncated fragments) and was only applied in 5 of 11 modal handlers.

Fix: Add a single, early universal filter in handleKeyMsg() that catches all fragment types before any modal/plugin routing. This uses the existing, well-tested tty.LooksLikeMouseFragment() plus a recentMouseEvent flag for bare [ suppression. Then remove all redundant per-modal filters.

Changes Made

  • internal/app/model.go: Add recentMouseEvent bool field for tracking proximity to mouse events
  • internal/app/update.go:
    • Set recentMouseEvent = true on every tea.MouseMsg
    • Add universal mouse fragment filter early in handleKeyMsg() (after Esc handling, before any modal/plugin routing)
    • Uses zero-allocation fast path: checks runes directly, only calls LooksLikeMouseFragment when first rune suggests mouse data
    • Remove isMouseEscapeSequence() function definition and all 5 call sites
    • Add tty package import
  • internal/app/project_add_modal.go: Remove 2 isMouseEscapeSequence() call sites

Net result: -27 lines (24 added, 51 removed)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

How to Test

  1. Build and run sidecar on a Linux system (GNOME Terminal, kitty, or similar)
  2. Open the project switcher (@) and move the mouse / scroll — no garbage should appear in the filter input
  3. Open Create New Worktree (n) and move the mouse — no fragments in the name field
  4. Open theme switcher (#), help (?), quit confirm (ctrl+c), diagnostics — mouse interaction should not cause unexpected behavior
  5. Verify [ key still works for prev-tab / prev-file navigation when no mouse is active
  6. On macOS: verify no false positives (all modals work normally)

Verification

  • go build ./... passes
  • go test ./internal/app/... passes
  • go test ./internal/tty/... passes
  • go vet ./... passes
  • Manual testing on Linux (GNOME Terminal) — confirmed fragments no longer leak into Create New Worktree dialog

Additional Notes

  • This is a Linux-only issue. macOS terminals deliver escape sequences atomically in a single read(), so Bubble Tea always successfully parses them.
  • The existing tty.LooksLikeMouseFragment() function (in internal/tty/output_buffer.go) already had comprehensive tests — no changes needed to that function.
  • Pre-existing mouse motion event flooding (causing click delay during rapid mouse movement) is a separate issue affecting Linux (I do not observe it on macOS), unrelated to this fix. Moving the mouse effectively freezes the UI dialog elements.

Add universal mouse fragment filter early in handleKeyMsg() using
tty.LooksLikeMouseFragment() and a recentMouseEvent flag for bare
bracket time-gating. This catches SGR mouse sequences that Linux
terminals split across read boundaries, which Bubble Tea delivers
as KeyMsg instead of MouseMsg.

Remove the now-redundant per-modal isMouseEscapeSequence() calls
and the function itself, centralizing the defense in one place.
@jacola jacola force-pushed the mouse-invstigation branch from bab75c1 to 89f2d13 Compare February 13, 2026 12:07
@jacola
Copy link
Author

jacola commented Feb 13, 2026

I accidentally added some of my other local fixes into this pr and cleaned them up with the force push above. Sorry about that. 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant