Skip to content

fix(inventory): make wheel scrolling stop when the user does - #660

Open
naxty wants to merge 2 commits into
mainfrom
naxty/scrolling-delay
Open

fix(inventory): make wheel scrolling stop when the user does#660
naxty wants to merge 2 commits into
mainfrom
naxty/scrolling-delay

Conversation

@naxty

@naxty naxty commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Wheel-scrolling formae inventory resources kept moving long after the wheel stopped. Three causes, all on the per-event path:

  • Redundant pipeline work per key. Every navigation key re-ran filter → sort → truncate → per-cell styling over the capped row set, and the render path re-ran the filter and sort twice more per frame — for a result a cursor move cannot change. Navigation no longer re-syncs; the view path counts filtered rows without sorting them.
  • One notch was several events. Without mouse tracking, a terminal in the alternate screen translates a wheel notch into several arrow keys, so each notch cost several full frames. The inventory TUI now enables tracking and sizes the step itself. Other TUIs are unchanged; FORMAE_TUI_NO_MOUSE=1 opts out for anyone who would rather keep click-drag text selection (with tracking on, selection needs shift held).
  • A frame per queued event. Bubbletea renders after every message, so a fast flick still queued more frames than the renderer could retire and the surplus drained after the user stopped. Wheel travel now accumulates and a settle tick applies the total in one move and one repaint; mid-burst frames reuse the last paint.

Per queued notch: 8.45 ms → 0.011 ms, plus one 2.2 ms settle per burst (1000 resources, benchmarks included).

Two display bugs surfaced while fixing the render path — both pre-existing, both made trivial to hit by wheel scrolling:

  • Resizing the table rebuilt the wrapped table's rows, which reset its scroll offset while keeping the cursor. Since the render path resizes on every frame, the selected row left the screen after roughly one screenful of travel — no row highlighted, and Enter opened detail for a row the user could not see. Same-size resizes are now a no-op, and only width changes reproject (height cannot change which columns fit).
  • Per-cell styling was applied by absolute row index onto a window of rendered lines, so once scrolled it targeted the wrong rows and silently vanished. It now aligns to the rendered window.

Covered by new tests for wheel coalescing, frame reuse, scroll-follow, and the filtered-count equality that the split view path relies on. Golden renders are unchanged.

naxty and others added 2 commits August 20, 2026 20:22
Scrolling the inventory list with the mouse wheel kept moving well after
the wheel stopped, and the further the list travelled the worse it got.

Three separate causes, all on the per-event path:

- Every navigation key re-ran the whole filter -> sort -> truncate -> style
  pipeline over the capped row set, and the render path re-ran the filter
  and sort twice more per frame, for a result that a cursor move cannot
  change. Navigation no longer re-syncs, and the view path counts filtered
  rows without sorting them.
- Without mouse tracking a terminal in the alternate screen turns one wheel
  notch into several arrow keys, so a notch cost several full frames. The
  inventory TUI now enables tracking and sizes the step itself; other TUIs
  are unchanged, and FORMAE_TUI_NO_MOUSE opts out for anyone who would
  rather keep click-drag text selection.
- Bubbletea renders after every message, so a fast flick still queued more
  frames than the renderer could retire. Wheel travel now accumulates and a
  settle tick applies the total in one move and one repaint; mid-burst
  frames reuse the last paint.

Per queued notch: 8.45ms -> 0.011ms, with one 2.2ms settle per burst.

While fixing the render path, two display bugs surfaced that scrolling made
easy to hit: resizing the table rebuilt the wrapped table's rows and reset
its scroll offset while keeping the cursor, so the selected row left the
screen after roughly one screenful of travel (the render path resizes on
every frame); and per-cell styling was applied by absolute row index to a
window of rendered lines, so it landed on the wrong rows once scrolled.
Same-size resizes are now a no-op, only width changes reproject, and cell
styling aligns to the rendered window.
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