m29 drags: a terminal's program gets its size during a divider drag, coalesced at 25 ms, only sizes shown on screen

LA
18h ago
+197-41

cmux-ad m18/m29: during a person's divider or column-edge drag, the program in each terminal now gets its new size while the drag moves, coalesced as Ghostty.app does (the newest grid at most once per 25 ms), and only a size that a commit already showed on screen. Before, a full-screen program kept its start size for the whole drag (the terminal background grew beside it) and jumped at the mouse-up.

Stacked on #36553 (TerminalGridReportCoalescer). Until #36553 merges, this diff also shows its two commits.

Why

  • A drag held each terminal's grid report until the landing (updateSurfaceSize: holding: ... || isFollowingResizeGesture), so the PTY got one grid per drag.
  • The reflow preview re-wraps the normal screen per frame, but not the alternate screen. A full-screen program (vim, htop, an agent TUI) froze at its start size, then jumped at the mouse-up (the RESIZE OWNER's film of px-col-drag-2-sw-terminals at 96ad37a03a3, STATUS 14461).

Fix

  • TerminalGridReporter (new): every grid report of a terminal view goes through one TerminalGridReportCoalescer, so its last sent grid is the PTY's.
    • A drag's grid is coalesced (25 ms window, the newest goes).
    • Every other report (at rest, forced, a layout spring's final grid) goes now; it also drops a drag grid still waiting.
  • A drag's grid is the grid of a size shown on screen: at once when the bounds take the size, else from the presenter's landing (TerminalSurfaceView.frameDidLand, one call site; key resizes reuse it, frame-owner-s4's T-KEYS #36478).
  • updateSurfaceSize no longer holds the grid for a followed drag or its release spring.
  • The drag's end, and a surface that stops following (it left its host or window), send the waiting grid now.
  • Split dividers and column or row edges share the path: every handle kind reaches the same followers through ScreenContentView.resizeGestureMoved.

Changelog

  • Fixed: A full-screen terminal program (vim, htop, an agent) resizes while you drag a divider or column edge, instead of jumping when you let go.

Tests

RunRefStepResult
red: PTY grids during a drag2d7728c6556 (test only, on #36553)10980491e9da4567245aab87 (cmux9)FAIL as expected: DividerDragTerminalResizeCountTests.aDividerDragResizesEachPTYWhileItMovesCoalescedAndAtTheLanding, during.count → 0 >= 2 for both terminals (no PTY grid while the drag moves)
red: every drag grid is a shown frame's (display), first trycffa23bf829 (scratch, feat merged)78cf9923598483a4ac2ab151failed, but the head failed the same way: the test was wrong (below)
green: display suites, first trycc8adfffe323c8a6bb8bc97b90b3702ac3efailed: TerminalDragShownGridTests 46 grids "never shown", the same failure as the red run
green: 14 suitescc8adfffe32 (feat merged, ghostty-next 383b27d73717)8fc860667acd00dd0ffad227ci-step m29z-green exited 0
red: every drag grid is a shown frame's (display), corrected test7d4c62ebaef (scratch, feat merged, no shown-grid fix)9e5afccfbbc7ee78a1ef0caaFAIL as expected: 16 grids sent before any commit showed them (e.g. p1 57x33 while the shown frames were 58x33..61x33: the asked size, one ahead)
green: display, corrected test5b4fdce482dd6d71b43e7ddca29c1fbc68fci-step m29y-green-display exited 0: 23 PTY grids per terminal during the drag, 0 never shown, newest grid at rest = the view's
app buildcc8adfffe32301042ed0b1a173f19e54759done
launch smoke (fresh-home)build 76aa7e0b8960bd3a0d3b602c1f8e481cexit 0 (cmux12)

Observed / Not run

  • Why the first display test proved nothing: it counted a size as shown only when no next size was in flight, but a drag asks its next size in the same pass as a landing, so it recorded almost no drag frames and called nearly every grid "never shown", with and without the fix. 5b4fdce482d counts every committed frame whose layer and presented frame have one pixel size. It must still fail on 7d4c62ebaef (no shown-grid fix) for a grid the program got that no commit showed.

  • Observed: the app build and the launch smoke above; prepush gate, check-no-godfiles (TerminalSurfaceView got smaller; the new file is 70 lines).

  • Not run yet: the display red and the two green steps above (queued; the display pool has 3 workers paused for disk). Films after merge.

  • Review: none yet. Codex was not run on this branch.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes terminal resizing during divider and column-edge drags so the running program gets its new size while the drag moves, instead of jumping at the mouse-up. A full-screen program (vim, htop, an agent TUI) previously kept its start size for the whole drag with the terminal background beside it; now its grid is coalesced like Ghostty.app's (the newest grid at most once per 25 ms), and only grids whose size a commit actually showed on screen are sent.

  • Adds TerminalGridReporter and TerminalGridReportCoalescer: every grid report goes through one 25 ms coalescing window, with drags coalesced and all other reports (rest, forced, layout-spring finals) sent immediately.
  • A drag's grid is sent once its size is shown on screen, either immediately when the bounds take it or from the presenter's landing (frameDidLand); a report sent by now() also drops any drag grid still waiting for its size to show.
  • updateSurfaceSize no longer holds grid reports during a followed drag or its release spring; the drag's end and surfaces that stop following flush the waiting grid.
  • Covers split dividers and column/row edges through the same path.
  • The shown-grid display test now records a committed frame at its own size even when the next size is already in flight, so it no longer misses drag frames.