fix(fuzzylist): clamp pickers to the pane with a scrolling viewport#41
fix(fuzzylist): clamp pickers to the pane with a scrolling viewport#41todie wants to merge 1 commit into
Conversation
Both pickers render every row unconditionally, so a list taller than the pane (e.g. 60+ project files) overflows the alt-screen: rows past the pane height are cut off, the detail bar and footer scroll away, and the cursor can walk out of view. Long rows also soft-wrap, breaking the list's line accounting for mouse clicks. Give fuzzyList a viewport: setViewport(rows, width) caps how many body lines view() emits, a scroll offset follows the cursor in both directions (ensureVisible), and every emitted line is ANSI-aware truncated to the pane width so nothing soft-wraps. rowIndexAt maps clicks through the scroll offset, so click-to-open keeps working while scrolled. The projects browser and both quick-actions stages feed their pane size through on WindowSizeMsg (and when the select-stage option list is created), each reserving its own fixed chrome lines. With no viewport set, behavior is unchanged (unbounded, as before).
cloudmanic
left a comment
There was a problem hiding this comment.
Thanks for this, @todie — genuinely well done. Reusing one line-accounting function (lineOf) across view(), rowIndexAt(), and the scroll math is exactly the right way to keep rendering, clicks, and the viewport in sync, and keeping it a no-op when no viewport is set means nothing else had to change. This is a fix I'll benefit from directly — my own project list is well past a screen. Verified it builds and the tests pass on my end.
One small, non-blocking thing before it goes in: go.mod still marks github.com/charmbracelet/x/ansi as indirect, but the PR now imports it directly, so 'go mod tidy' will promote it to a direct require. Mind running that and pushing the go.mod change? Once that's in it's good to merge.
Thanks again for the careful work here.
Problem
Both pickers render every list row unconditionally. With more entries than the pane has rows (I hit this with ~66 project files), the alt-screen view overflows: rows past the pane height are truncated, the detail bar and footer disappear, and arrowing down walks the cursor out of view. Independently, rows longer than the pane soft-wrap, which breaks the line accounting
rowIndexAtrelies on for mouse clicks.Fix
Give
fuzzyLista viewport:setViewport(rows, width)caps how many body linesview()emits; a scroll offset (lineOffset) follows the cursor in both directions viaensureVisible(), so the highlighted row is always on screen.x/ansi.Truncate, already an indirect dep) to the pane width so nothing soft-wraps.rowIndexAtmaps clicks through the scroll offset, so click-to-open keeps working while scrolled and clicks on the query block or below the window stay no-ops.WindowSizeMsg(and when the select-stage option list is created), each reserving its own fixed chrome lines.With no viewport set the component behaves exactly as before (unbounded), so existing tests pass unchanged; two new tests cover window-follows-cursor (both directions + click mapping through the offset) and width truncation.
Verified
Drove
projects-uiwith 66 project files in a 25x90 pty and checked the emulated screen: the list windows to the pane with the detail box and footer pinned and visible, scrolling 30 rows down keeps the cursor and its detail preview on screen, and filtering re-clamps the window. Same check onquick-actions-uiat 12x70.