Skip to content

fix(web): refit the terminal when its container is resized - #4845

Open
Sy-D wants to merge 2 commits into
pingdotgg:mainfrom
Sy-D:fix/terminal-drawer-refit
Open

fix(web): refit the terminal when its container is resized#4845
Sy-D wants to merge 2 commits into
pingdotgg:mainfrom
Sy-D:fix/terminal-drawer-refit

Conversation

@Sy-D

@Sy-D Sy-D commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What Changed

TerminalViewport now 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 a setTimeout after 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:

  • Coalescing. A drag emits a ResizeObserver callback per frame. They collapse into one requestAnimationFrame, so a gesture costs one fit rather than one per pixel.
  • Reporting only on real changes. Pixel changes usually leave the character grid where it was. The PTY is told only when cols/rows actually move, so a drag does not spray identical resize RPCs across the socket.
  • One helper instead of three copies. The read-viewport / fit / pin-to-bottom / report sequence existed three times. It is now 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 ResizeObserver also fires once on observe. 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.

container .xterm-screen columns
before 1016px 1001px 139
narrowed 520px 504px 70
restored 1016px 1001px 139

Columns derived from .xterm-char-measure-element (32-character sample). Nothing else in the component observes width, so on main this transition produces no refit at all.

  • vp run --filter @t3tools/web test — 1663 passed
  • vp run --filter @t3tools/web typecheck — no errors
  • vp lint / vp fmt --check on the changed file — clean

Closes #4842.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • No visual change to screenshot; the before/after is the grid geometry above

Note

Refit terminal in TerminalViewport when its container is resized

  • Adds a ResizeObserver effect in ThreadTerminalDrawer.tsx that watches the terminal container for size changes not covered by existing props (e.g. window or layout changes).
  • Coalesces rapid resize callbacks via requestAnimationFrame, skips fitting when the container is hidden (0×0), and only notifies the PTY when cols/rows actually change.
  • Extracts a refitTerminal helper 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
TerminalViewport now keeps the xterm grid aligned when the DOM container changes size without updating drawer props (window resize, divider drag, layout reflow).

A refitTerminal helper centralizes fit + bottom-scroll preservation and returns {cols, rows}; mount and prop-driven refit paths use it instead of duplicated logic.

A ResizeObserver on the container coalesces callbacks with requestAnimationFrame, skips 0×0 hidden layouts, and calls resizeTerminal only 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.

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>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ecf660e-c770-43ab-87ac-aab86acff8d4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 29, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx
@macroscopeapp

macroscopeapp Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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>
@Sy-D

Sy-D commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

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:

rows container width
visible 16 1016px
hidden, before the guard 6 0px
hidden, after the guard 16 0px

The row count changing is what makes this bite: the cols/rows dedup would have let the 0x0 fit straight through to the PTY, so a running program would reflow for a viewport nobody can see and stay that way until the drawer came back.

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 ResizeObserver callbacks at all, because the automated tab was not visible. requestAnimationFrame is throttled the same way. The numbers above were taken with the tab visible and the observer confirmed firing.

vp run --filter @t3tools/web test — 1663 passed.

@Sy-D
Sy-D force-pushed the fix/terminal-drawer-refit branch from c3143b7 to 461f863 Compare July 29, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Terminal drawer does not re-fit when its container is resized

1 participant