Skip to content

Conversation

@croakingtoad
Copy link
Contributor

Problem

When scrolling with the mouse wheel in the browser, the terminal was scrolling through command history instead of the tmux scrollback buffer. This only affected browser usage - native tmux scrolling worked correctly.

Root Cause

The previous implementation sent SGR mouse sequences (\x1b[<64;40;12M) via tmux send-keys -l, which had two issues:

  1. The -l flag sends keys literally, bypassing tmux's WheelUpPane binding
  2. send-keys sends input TO the pane, not to tmux itself, so tmux never intercepts the wheel events

As a result, the shell received these sequences and interpreted them as scroll events, translating them to Up/Down arrow keys for command history navigation.

Solution

This PR changes the approach to directly control tmux copy-mode:

  1. Added new tmux-scroll message type - bypasses send-keys entirely
  2. Backend directly invokes tmux commands - tmux copy-mode + tmux send-keys -X scroll-up/down
  3. Auto-exit when scrolling to bottom - checks #{scroll_position} and exits copy-mode when reaching position 0
  4. Works with both mouse wheel and touch - updated both desktop and mobile scroll handlers

Changes

  • Added tmux-scroll message type to ClientMessage union
  • Implemented handleTmuxScroll function that:
    • Enters copy-mode if not already in it
    • Scrolls using tmux's native scroll commands
    • Detects when scroll position reaches 0 and exits copy-mode automatically
    • Reports copy-mode status to client
  • Updated desktop wheel handler to send tmux-scroll instead of SGR sequences
  • Updated touch scroll handler to send tmux-scroll instead of SGR sequences

Testing

Before: Scrolling with mouse wheel scrolled through shell command history

After:

  1. Scroll up → enters tmux copy-mode, shows "Jump to bottom" button
  2. Scroll through history → works smoothly
  3. Scroll back down to bottom → automatically exits copy-mode
  4. Terminal immediately ready for input

Verified on: Desktop Chrome, Firefox, Safari

Commits

  • fix: use tmux copy-mode commands instead of SGR sequences for mouse wheel scrolling
  • feat: auto-exit copy-mode when scrolling reaches bottom
  • fix: explicitly exit copy-mode when scroll position reaches 0

This fixes the longstanding issue where browser-based scrolling didn't match native tmux behavior.

Marty and others added 3 commits February 3, 2026 21:18
…heel scrolling

When scrolling with the mouse wheel in the browser, the previous implementation
sent SGR mouse sequences (\x1b[<64;..M) via `tmux send-keys -l`, which has two issues:

1. The `-l` flag sends keys literally, which doesn't trigger tmux's WheelUpPane binding
2. Even without `-l`, `send-keys` sends input TO the pane, not to tmux itself

This caused the shell to receive the sequences instead of tmux entering copy-mode,
resulting in command history scrolling instead of scrollback buffer scrolling.

The fix:
- Added new `tmux-scroll` message type to bypass send-keys entirely
- Directly invoke `tmux copy-mode` and `tmux send-keys -X scroll-up/down`
- Works consistently with both desktop mouse wheel and touch scrolling
- Matches native tmux behavior when connected directly

This ensures mouse wheel scrolling in the browser enters tmux copy-mode and
scrolls the scrollback buffer, just like scrolling in native tmux.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
After scrolling, check tmux's pane_in_mode status and report it to the client.
When scrolling down reaches the bottom, tmux automatically exits copy-mode,
and we now detect this and update the UI accordingly.

This eliminates the need to press 'q' to exit copy-mode - just scroll down
to the bottom and it exits automatically, matching native tmux behavior.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Tmux doesn't auto-exit copy-mode when reaching the bottom - it stays in
copy-mode until explicitly exited. Now we check #{scroll_position} after
scrolling down, and if it's 0 (at the bottom), we explicitly send the
cancel command to exit copy-mode.

This provides the expected behavior: scroll down to bottom → automatically
exits copy-mode and returns to live terminal.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@croakingtoad
Copy link
Contributor Author

I didn't note that this the error was on Windows 11 using Chrome Version 143.0.7499.193 (Official Build) (64-bit)

@gbasin
Copy link
Owner

gbasin commented Feb 4, 2026

Check this, it hopefully does it for you. I want to keep the way PTY scrolling works currently as it's way lower latency: #43

@gbasin gbasin closed this Feb 4, 2026
gbasin added a commit that referenced this pull request Feb 4, 2026
## Summary
- Fixes mouse wheel scrolling in pipe-pane terminal mode by intercepting
SGR scroll sequences server-side
- Only affects pipe-pane mode - PTY mode continues to use native tmux
mouse handling

## Changes
- Intercept `ESC[<64;col;rowM` (scroll-up) and `ESC[<65;col;rowM`
(scroll-down) in `PipePaneTerminalProxy.write()`
- Enter tmux copy-mode and send scroll commands instead of passing
sequences as literal text to shell
- Added unit tests for scroll handling

## Why this approach
The original PR (#42) intercepted scroll events client-side and sent a
separate `tmux-scroll` WebSocket message. This refactor:
- Keeps scroll logic server-side in the proxy that needs it
- Avoids extra WebSocket round-trips
- Leaves PTY mode completely unchanged (no performance impact)

Based on PR #42 by @BrennerSpear - thank you for identifying the issue
and initial implementation!

Co-Authored-By: Brenner Spear <BrennerSpear@users.noreply.github.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Marty <marty@example.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Brenner Spear <BrennerSpear@users.noreply.github.com>
gbasin added a commit that referenced this pull request Feb 4, 2026
## Summary
- Fixes mouse wheel scrolling in pipe-pane terminal mode by intercepting
SGR scroll sequences server-side
- Only affects pipe-pane mode - PTY mode continues to use native tmux
mouse handling

## Changes
- Intercept `ESC[<64;col;rowM` (scroll-up) and `ESC[<65;col;rowM`
(scroll-down) in `PipePaneTerminalProxy.write()`
- Enter tmux copy-mode and send scroll commands instead of passing
sequences as literal text to shell
- Added unit tests for scroll handling

## Why this approach
The original PR (#42) intercepted scroll events client-side and sent a
separate `tmux-scroll` WebSocket message. This refactor:
- Keeps scroll logic server-side in the proxy that needs it
- Avoids extra WebSocket round-trips
- Leaves PTY mode completely unchanged (no performance impact)

Based on PR #42 by @croakingtoad - thank you for identifying the issue
and initial implementation!

Co-Authored-By: Marty Martin <croakingtoad@users.noreply.github.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Marty <marty@example.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
gbasin added a commit that referenced this pull request Feb 4, 2026
## Summary
- Fixes mouse wheel scrolling in pipe-pane terminal mode by intercepting
SGR scroll sequences server-side
- Only affects pipe-pane mode - PTY mode continues to use native tmux
mouse handling

## Changes
- Intercept `ESC[<64;col;rowM` (scroll-up) and `ESC[<65;col;rowM`
(scroll-down) in `PipePaneTerminalProxy.write()`
- Enter tmux copy-mode and send scroll commands instead of passing
sequences as literal text to shell
- Added unit tests for scroll handling

## Why this approach
The original PR (#42) intercepted scroll events client-side and sent a
separate `tmux-scroll` WebSocket message. This refactor:
- Keeps scroll logic server-side in the proxy that needs it
- Avoids extra WebSocket round-trips
- Leaves PTY mode completely unchanged (no performance impact)

Based on PR #42 by @croakingtoad - thank you for identifying the issue
and initial implementation!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Marty Martin <croakingtoad@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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.

2 participants