fix(web): refit the terminal when its container is resized - #4845
Conversation
The drawer refit on drawerHeight and a few sibling props, and once from a timer after mount. Nothing watched the container's own size, so dragging a divider or resizing the window left the grid at its mount dimensions: output stayed wrapped to the old column count and the PTY kept the stale size. Observe the container and refit from there, coalescing a drag's callbacks into one fit per frame and reporting to the PTY only when the grid actually moved. The fit-and-pin dance the three call sites repeated is now one helper. Closes pingdotgg#4842. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 276e2ac. Configure here.
ApprovabilityVerdict: Approved c3143b7 This is a self-contained bug fix that adds ResizeObserver-based terminal refitting when the container is resized by external factors (dragging dividers, window resize). The implementation is clean with proper debouncing and cleanup, and the scope is limited to terminal viewport UI behavior. You can customize Macroscope's approvability policy. Learn more. |
Review feedback: a hidden ancestor collapses the box to 0x0, and the observer fitted to that, handing the PTY a minimal grid. Measured before the guard, a 16-row terminal dropped to 6 while hidden, and the row change defeated the dedup, so the size reached the PTY and the running program reflowed for a viewport nobody could see. Skip the fit when the container has no layout box. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Confirmed and fixed in c3143b7 — the finding was real, and worse than the dedup suggested. Measured in a running client by hiding an ancestor of the drawer:
The row count changing is what makes this bite: the The observer now skips the fit when the container has no layout box. The width refit the PR is for is unaffected — 139 → 70 → 139 columns across the same transition. A note on how I verified it, since it nearly fooled me: my first measurement appeared to show the guard working while it was actually the browser not delivering
|
c3143b7 to
461f863
Compare

What Changed
TerminalViewportnow observes its container's size and refits from there, instead of relying only on props and a post-mount timer.Why
Reported in #4842. The existing refits cover
drawerHeight,resizeEpoch, and asetTimeoutafter mount. None of them observe the container itself, so a resize that leaves those props untouched — dragging a divider, resizing the window, any surrounding reflow — left the grid at its mount dimensions. Output stayed wrapped to the old column count and the PTY kept a stale size.Notes For Review
Three details that are deliberate rather than incidental:
ResizeObservercallback per frame. They collapse into onerequestAnimationFrame, so a gesture costs one fit rather than one per pixel.cols/rowsactually move, so a drag does not spray identical resize RPCs across the socket.refitTerminal, which returns the resulting grid and lets each caller decide whether to report. Keeping the report outside the helper matters: the mount path must report even when the fit changes nothing, or the PTY would never learn its initial size.The
ResizeObserveralso fires once onobserve. That is harmless — a fit is idempotent, and the dedup means no RPC follows.Validation
Measured in a running client: opened the drawer, then changed only the container's width, leaving every prop the existing refits depend on untouched.
.xterm-screenColumns derived from
.xterm-char-measure-element(32-character sample). Nothing else in the component observes width, so onmainthis transition produces no refit at all.vp run --filter @t3tools/web test— 1663 passedvp run --filter @t3tools/web typecheck— no errorsvp lint/vp fmt --checkon the changed file — cleanCloses #4842.
Checklist
Note
Refit terminal in
TerminalViewportwhen its container is resizedResizeObservereffect in ThreadTerminalDrawer.tsx that watches the terminal container for size changes not covered by existing props (e.g. window or layout changes).requestAnimationFrame, skips fitting when the container is hidden (0×0), and only notifies the PTY when cols/rows actually change.refitTerminalhelper that fits the xterm instance and preserves bottom-pinned scroll position, returning the resulting{cols, rows}.Macroscope summarized 461f863.
Note
Low Risk
UI-only terminal sizing fix in ThreadTerminalDrawer with no auth, data, or API contract changes.
Overview
TerminalViewportnow keeps the xterm grid aligned when the DOM container changes size without updating drawer props (window resize, divider drag, layout reflow).A
refitTerminalhelper centralizes fit + bottom-scroll preservation and returns{cols, rows}; mount and prop-driven refit paths use it instead of duplicated logic.A
ResizeObserveron the container coalesces callbacks withrequestAnimationFrame, skips 0×0 hidden layouts, and callsresizeTerminalonly when cols/rows actually change (avoiding redundant PTY RPCs during pixel-only resizes).Reviewed by Cursor Bugbot for commit 461f863. Bugbot is set up for automated code reviews on this repo. Configure here.