feat: restore wheel-scroll tab/pane navigation without rate limiting#109
Conversation
…108) #104 deleted wheel-scroll navigation wholesale when it rolled back the buggy timing-based cooldown (#83/#96/#100). Restore the navigation on its own, dropping the rate limiter that caused the trouble. `scroll` is a ScrollMode enum {tab (default), pane, off}: `tab` switches tabs (wrapping), `pane` walks the focused pane in reading order across tab boundaries (wrapping globally), `off` opts out. One wheel event maps to exactly one step — no Gate, no cooldown window, no Timer subscription. A stepless device (Magic Mouse, trackpad) bursts per flick and steps several at once; users who dislike that set `scroll = off` rather than living with a limiter that never reconciled both device classes. The pure traversal math (next_tab / next_pane / step) lives in a new dependency-free `scroll` module, mirroring the renderer's off-wasm testable discipline; lib.rs holds only the thin host-call dispatch. Rides the existing ChangeApplicationState grant — no new permission, so existing installs gain it on update without a re-grant (freeze-safe, zellij#4982).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughRestores wheel-scroll tab/pane navigation to the zellij tab-bar plugin without any rate limiting. A new ChangesWheel-scroll navigation feature
Sequence Diagram(s)sequenceDiagram
participant Zellij
participant update as update()
participant scroll as State::scroll()
participant scroll_tabs as scroll_tabs()
participant scroll_panes as scroll_panes()
Zellij->>update: Mouse::ScrollUp / ScrollDown
update->>scroll: self.scroll(dir)
alt config.scroll == Tab
scroll->>scroll_tabs: next_tab(active, count, dir)
scroll_tabs->>Zellij: switch_tab_to(next_index)
else config.scroll == Pane
scroll->>scroll_panes: focused_pane_id() → pane_focus_order()
scroll_panes->>Zellij: focus_terminal_pane(next_pane_id)
else config.scroll == Off
scroll-->>update: no-op
end
update-->>Zellij: false (no repaint requested)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This PR restores mouse wheel navigation over the tab bar (removed in #104), reintroducing tab/pane traversal without any timing-based rate limiting, and gates the behavior behind a new scroll config mode.
Changes:
- Adds a new dependency-free
scrollmodule implementing pure traversal logic (ScrollMode,ScrollDir,next_tab,next_pane) with unit tests. - Wires
Mouse::ScrollUp/ScrollDowninState::update()to dispatch host calls (switch_tab_to,focus_terminal_pane) according to the configured scroll mode. - Reintroduces pane reading-order traversal via
projection::pane_ids_in_reading_order, and documents the newscrollconfig key in README.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/scroll.rs | New pure traversal module (tab/pane wrapping step logic) with unit tests. |
| src/projection.rs | Adds pane_ids_in_reading_order helper (tiled panes only) plus tests. |
| src/lib.rs | Restores wheel event handling and dispatches to tab/pane navigation based on config. |
| src/config.rs | Adds scroll: ScrollMode config key with default + parsing/tests. |
| README.md | Documents the scroll setting and behavior/trade-offs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Closes #108.
Background
#104 deleted wheel-scroll tab/pane navigation wholesale when it rolled back the buggy timing-based cooldown (the
scroll_cooldown_mslimiter that #83 / #96 / #100 each tried and never got right). Only the limiter was the problem — the navigation itself was wanted. This restores the navigation on its own, without the rate limiter.What changed
scrollis back as aScrollModeenum —tab(default) /pane/off:tab— scroll up = next tab, down = previous, wrapping at the ends (zellij's stock tab-bar direction, but wrap instead of clamp).pane— walks the focused pane forward / backward in reading order (top→bottom, then left→right), crossing tab boundaries, wrapping globally.off— the wheel is inert.One wheel event maps to exactly one step. No
Gate, no cooldown window, noTimersubscription — none of the machinery that made the old limiter flaky.Known trade-off
zellij hands the plugin
ScrollUp/ScrollDownwith no device identity, so a stepless device (Magic Mouse, trackpad) reports a single flick as a burst of events and steps several tabs / panes at once. That is the accepted cost of dropping the limiter: users who dislike it setscroll "off"and navigate by keyboard, rather than living with a timing heuristic that never reconciled notched wheels and stepless devices. See #108 for the rationale.Design
next_tab/next_pane/step, plus theScrollMode/ScrollDirenums) lives in a new dependency-freescrollmodule — no zellij types, unit-tested off-wasm, mirroring the renderer's discipline.lib.rsholds only the thin host-call dispatch (switch_tab_to/focus_terminal_pane) behind the twoMouse::ScrollUp/Downarms.projection::pane_ids_in_reading_order, so the wheel and the minimap can never disagree on which panes exist.Permission safety
Rides the existing
ChangeApplicationStategrant — no new permission. Existing installs gain this on update without a re-grant, so it can't trigger the silent-freeze trap (zellij#4982).Tests
scroll.rs100% /config.rs99.58% /projection.rs100% /lib.rs99.25% line coverage; 263 native unit tests pass, clean wasm build, clean clippy.Summary by CodeRabbit
Release Notes
scrollconfiguration option for the tab-bar plugin to control mouse-wheel behavior. Choose from three modes: switch between tabs (tab), navigate panes across tab boundaries (pane), or disable wheel handling entirely (off). Each wheel event triggers exactly one action with automatic wrapping at boundaries. No additional permissions required.