fix: restore mouse wheel scrolling in the review screens - #128
Merged
Merged
Conversation
Clipping the review content to the terminal window took the wheel away. The content used to overflow into the terminal's scrollback, where the wheel scrolled natively; now that the viewport clips it, nothing reaches the scrollback and the wheel scrolls an empty buffer. A terminal only reports the mouse to an application that asks for it, so ask — while there is something to scroll, and no longer, since mouse reporting also takes click-to-select away from the terminal. A zero-row viewport now counts as nothing to scroll, which it always was: the keys were live there too, on content no one could see. Legacy `ESC [ M` reports are read as one sequence so their three raw bytes cannot land in a text input as stray characters. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
| Topic | Details | |||
|---|---|---|---|---|
| Input and documentation | Prevent legacy mouse reports from being tokenized as typed text and document the new wheel behavior and terminal interaction.Modified files (3)
Latest Contributors(0)
| |||
| Mouse wheel scrolling | Restore wheel scrolling in review screens by tracking terminal mouse input only while content overflows, handling viewport ownership and three-line movement, and releasing tracking on cleanup.Modified files (5)
Latest Contributors(0)
|
An SGR release carrying a wheel button is no longer read as a notch: the terminator was matched but thrown away, so `ESC [ < 64 ; … m` scrolled. The release test passed on a left-button release, which never reached the terminator either way. The wheel no longer goes dead while the composer's mention list is open. That list takes over the arrow keys, which is why the viewport gives them up; nothing competes for the wheel, so it keeps scrolling the content behind the list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nimrodkor
approved these changes
Sep 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The regression
Scrolling with the keyboard works; the mouse wheel does nothing. It used to work before #119.
#119 added no mouse handling either way — what changed is that
ScrollableViewportclips the content to the window (height+overflow: hidden). Before that, long reviews overflowed into the terminal's scrollback, and the wheel scrolled that natively. Now nothing reaches the scrollback, so the wheel scrolls an empty buffer and looks dead.The fix
A terminal only reports the mouse to an application that asks for it, so ask — and read the wheel back.
src/lib/input/mouse.ts— turn tracking on (?1000+ SGR?1006) and parse wheel reports, in both the SGR and legacy encodings, ignoring clicks, drags, the horizontal wheel, and any modifiers held while scrolling.src/hooks/useMouseWheel.ts— asks for the mouse while active (ref-counted across viewports, restored on exit) and delivers wheel events.ScrollableViewport— a notch scrolls three lines, matching what terminals do themselves.Mouse reporting is asked for only while there is something to scroll, because it costs something: with it on, the terminal hands clicks to the application instead of using them for text selection. Off the moment the content fits.
Two things found along the way, both fixed here:
maxOffset > 0whileviewportHeight === 0), so the scroll keys were live on content no one could see. Now gated on the viewport having rows.ESC [ Mreport packs three raw bytes that are not parameter characters, so the tokenizer would have split them out as text and typed them into the search box. Read as one sequence now.Testing
npm run cicd— lint, format, 99 tests, build, all clean.src/lib/input/mouse.spec.ts— 8 cases over both encodings, modifiers, clicks/drags/horizontal wheel, releases and ordinary keys.src/components/ScrollableViewport.spec.tsx— wheel scrolls three lines and back, stops at both ends, asks for the mouse and hands it back on unmount, and leaves it alone when there is nothing to scroll.src/lib/input/line-editor.spec.ts— a legacy report stays one token and inserts nothing.Verified in the built output driven through a real pty, not just in tests:
Branch is cut from
mainat 9caa378.🤖 Generated with Claude Code