Keep the update -i prompt frame on screen and skip no-op redraws
Problem
- In
bun update -ri, every mouse press and release adds one more copy of? Select packages to update - Space to toggle, Enter to confirm, ...to the terminal scrollback (#39679). - The input loop in
src/runtime/cli/update_interactive_command.rsredraws the whole frame on every input byte, a no-op mouse click included. The viewport height (get_terminal_size, old line 1117) reserves 4 rows but ignores the 2 lines each dependency-group header takes, so with-rthe frame is taller than the terminal. The redraw's cursor-up clamps at the screen top and each repaint scrolls the top of the previous frame into scrollback.
Fix
compute_viewport_heightsizes the package viewport from the raw terminal height minus the frame's fixed lines: the help line, 2 lines per dependency group, the bottom scroll indicator, and the row the cursor parks on below the frame. The frame now always fits, so an in-place redraw never scrolls.- The loop redraws only when input changed visible state (
needs_redraw). A click press or release changes nothing and no longer repaints, which also removes flicker. - The resize check now reacts to height changes too, and a resize redraws in place instead of printing a new frame below the old one.
- Verified: two new tests in
test/cli/update_interactive_formatting.test.ts(both fail on the current release: 7 prompt copies after 3 clicks, and a 25-line cursor-up on a 24-row terminal). Also ranupdate_interactive_install,update_interactive_snapshots,regression/issue/update-interactive-formatting, andbun-update-transitive(174 pass).
Background
- The prompt enables SGR mouse reporting (
\x1b[?1000h,\x1b[?1006h), so a click sends\x1b[<0;col;rowMon press and...mon release. Only wheel buttons (64/65) scroll. Button 0 is parsed and discarded. - A redraw moves the cursor up over the previous frame with
\x1b[{n}Aand repaints. A terminal clamps that cursor move at row 1. When the frame has more lines than the screen, the repaint pushes the overflow into scrollback, once per redraw. -ivs-riis not the real switch. The real switch is "frame taller than the terminal".-rjust makes the list taller. Plain-ishowed the same leak in a 9-row terminal.
Notes
Reproduction was driven through a pseudo-terminal: run bun update -ri in a 16x140 PTY against a workspace with 13 outdated dependencies, send 3 SGR clicks (press + release), then Ctrl+C, and feed the raw bytes through @xterm/headless to count help lines in screen plus scrollback. Unfixed: 7 (1 real + 6 duplicates, one per mouse event). Fixed: 0 extra (the one on screen). The 9-row -i case also went from 7 to 0.
The tests run without a PTY, so get_terminal_size falls back to 24x80. The frame-height test builds 20 outdated rows across 2 dependency groups (10 workspaces with one dependency and one devDependency each, -r), then parses the \x1b[{n}A\x1b[1G cursor-up the prompt emits: n must stay under 24. Unfixed, the fallback viewport was 20 rows and n was 25. The click test counts occurrences of the help line in raw stdout after 3 clicks: 1 fixed, 7 unfixed. A wheel event in the frame-height test asserts that a real scroll still repaints (the more package above indicator appears).
The fallback terminal size changes from 20x80 (already minus the old 4-row reserve) to a raw 24x80, and the reserve moves into compute_viewport_height. On a real terminal the numbers only shrink when more than one dependency group is visible, by 2 lines per extra group plus 1 for the resting row.
no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/update_interactive_formatting.test.ts