Skip to content

fix: restore mouse wheel scrolling in the review screens - #128

Merged
nimrodkor merged 2 commits into
baz-scm:mainfrom
benglewis:fix/mouse-wheel-scrolling
Sep 29, 2026
Merged

nimrodkor merged 2 commits into
baz-scm:mainfrom
benglewis:fix/mouse-wheel-scrolling

Conversation

@benglewis

Copy link
Copy Markdown
Contributor

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 ScrollableViewport clips 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:

  • A zero-row viewport counted as scrollable (maxOffset > 0 while viewportHeight === 0), so the scroll keys were live on content no one could see. Now gated on the viewport having rows.
  • A legacy ESC [ M report 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:

asked terminal for mouse reporting: True
top line at rest:            line 0
top line after wheel down:   line 3
status after wheel down:     ↑↓ lines 4-19 of 40 · ↑/↓ PgUp/PgDn to scroll
top line after 2nd down:     line 6
top line after wheel up:     line 3
released mouse on exit:      True

Branch is cut from main at 9caa378.

🤖 Generated with Claude Code

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>
@baz-reviewer

baz-reviewer Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Review this PR on Baz

Baz Summary

Restore mouse-wheel scrolling in clipped review viewports by enabling terminal mouse reporting and parsing SGR and legacy wheel events. Update ScrollableViewport, useMouseWheel, and input tokenization to deliver three-line wheel scrolling without capturing clicks when content cannot scroll.

Topics

TopicDetails
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)
  • README.md
  • src/lib/input/line-editor.spec.ts
  • src/lib/input/line-editor.ts
Latest Contributors(0)
UserCommitDate
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)
  • src/components/ScrollableViewport.spec.tsx
  • src/components/ScrollableViewport.tsx
  • src/hooks/useMouseWheel.ts
  • src/lib/input/mouse.spec.ts
  • src/lib/input/mouse.ts
Latest Contributors(0)
UserCommitDate

Merger  Activate to get a short verdict whether this PR is good to go or not

Comment thread src/hooks/useMouseWheel.ts
Comment thread src/hooks/useMouseWheel.ts
Comment thread src/lib/input/mouse.ts Outdated
Comment thread src/components/ScrollableViewport.tsx Outdated
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
nimrodkor merged commit 75cf1e2 into baz-scm:main Sep 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants