Skip to content

fix(web): stop a tall terminal drawer pushing the composer out of the chat area - #5207

Open
carlosricojr wants to merge 3 commits into
pingdotgg:mainfrom
carlosricojr:fix/composer-over-thread-header
Open

fix(web): stop a tall terminal drawer pushing the composer out of the chat area#5207
carlosricojr wants to merge 3 commits into
pingdotgg:mainfrom
carlosricojr:fix/composer-over-thread-header

Conversation

@carlosricojr

@carlosricojr carlosricojr commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

The composer is an overlay, and the chat area it overlays could shrink to nothing (min-h-0) while the terminal drawer held an explicit height of up to 75% of the window. Once a tall drawer left the chat area shorter than the composer, nothing clipped the overlay — it spilled out of the area instead.

Docked, it spilled upward over the thread header and off the top of the viewport. On a new thread it is worse: the hero composer is centred in the chat area, and a centred flex item that outgrows its container overflows both ends — so it covered the header and ran back down across the top of the terminal, with the headline pushed off-screen above. Its glass surface let the header and the terminal read through it either way.

The fix

The chat area reserves the height the composer actually needs, so flex layout shrinks the drawer rather than the composer's room. Docked that is the overlay's own height; in the hero state the overlay spans the whole area, so the reserve is the composer stack plus twice any banner (centring mirrors a banner's space below the composer, and banners carry actions).

The hero headline is deliberately not reserved. It is decorative, and reserving it costs the drawer 138px it should keep — it hides once the area cannot hold it and returns when the drawer shrinks. Hidden means visibility: hidden, because the decision reads its measured height and unmounting it would oscillate.

The drawer became shrinkable to allow the reserve — its wrapper is display: contents so the drawer itself is the flex child that yields — and it keeps a floor at MIN_DRAWER_HEIGHT so the resize handle stays reachable. Drags now start from the height layout actually granted, so the divider keeps tracking the pointer while the drawer is being squeezed.

What it costs the terminal

A visible composer and an uncapped drawer are mutually exclusive — the bug exists exactly when they don't both fit — so the goal was to make the drawer give up as little as possible:

window drawer before drawer after cost
≥1024px 75% cap 75% cap none
900px 675 644 31px
900px, typical draft 675 675 none
700px 525 444 81px

Measured live, not derived. At 900px with an ordinary draft the drawer holds its full cap with 6px to spare.

Before / after

New thread, 1280×700 (the tightest case above), terminal dragged to its cap, long draft in the composer.

Before After
composer over the header and the terminal, headline clipped off-screen composer sitting inside the chat area, headline dropped

Same run at 1280×900, where the drawer barely moves (675 → 644): before · after.

Geometry at 1280×700, same 204px composer:

before after
composer over the header 40px 0
composer over the terminal 40px 0
headline top −57px (off-screen) hidden
drawer 525px 444px

Reverse state checked: dragging the divider back down brings the headline straight back (drawer 280 → headline shown, 675 → hidden, 203 → shown). The docked case was verified separately — composer top at 52px, bottom exactly on the drawer edge, 0px overlap either side.

Notes

  • Right-panel terminals (mode="panel") are unchanged — they size with h-full flex-1 and never carried the pixel height.
  • GhosttyTerminalSurface already observes its own container and refits synchronously, so a drawer shrunk by layout re-fits itself with no extra plumbing.
  • The drawer's stored height survives a squeeze, so it springs back to what you dragged once the composer shrinks or the thread leaves the hero state. A drag taken while squeezed only lowers it if the drag itself went downward — nudging the divider up cannot quietly rewrite a taller preference.

Verified with tsgo --noEmit, vp lint, and vp test run over the touched files (57 tests), plus the live passes above.

Review round: Bugbot raised three findings. Two were real and are fixed in 466ce94 — the reserve went stale for a frame when switching threads (the overlay node is reused, so the measuring effect never re-ran), and a drag taken while the reserve squeezed the drawer could persist a smaller height than the user had stored while they were dragging to make it bigger. The third was already resolved on the current head; replies are inline.

Claude Opus 5 via Claude Code, driven from T3 Code.


Note

Medium Risk
Touches core thread layout flex behavior and persisted terminal drawer height during squeeze; logic is covered by unit tests but regressions could affect composer/terminal coexistence on small viewports.

Overview
Fixes the composer overlay spilling over the thread header and terminal when a tall drawer left the chat area shorter than the composer.

The chat row now applies minHeight from chatAreaReservedComposerHeight (docked overlay height vs draft-hero stack plus twice banner height). ChatView measures stack, headline, and banners via ResizeObserver, tracks chat area height, and hides the draft hero headline with visibility: hidden when shouldShowDraftHeroHeadline says there isn’t room—without unmounting, so measurements stay stable.

The terminal drawer wrapper uses display: contents when visible so the drawer is the flex child that shrinks; the drawer drops shrink-0, keeps a minHeight floor, and resize drags anchor on rendered height (resolveDrawerResizeStartHeight) and persist height without clobbering a taller stored preference when squeezed (resolveDrawerResizeStoredHeight).

Reviewed by Cursor Bugbot for commit 154ad23. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix tall terminal drawer pushing the composer out of the chat area

  • The chat area now reserves vertical space for the composer via a new chatAreaReservedComposerHeight calculation, causing the terminal drawer to shrink under flex layout instead of overlapping the composer.
  • The terminal drawer loses shrink-0 and gains a minHeight style so it can yield space while remaining resizable; its wrapper div uses contents when hidden to keep the drawer as the direct flex child.
  • Resize drags now start from the drawer's rendered height (via resolveDrawerResizeStartHeight), and height persistence via resolveDrawerResizeStoredHeight avoids overwriting a taller stored preference when the drawer is squeezed.
  • The draft hero headline is conditionally hidden (CSS visibility) via shouldShowDraftHeroHeadline when the chat area lacks room to display it above and below the composer.
  • Behavioral Change: drawer height can now shrink below its stored value when the composer reserves space; stored height is only updated on explicit drag.

Macroscope summarized 154ad23.

@coderabbitai

coderabbitai Bot commented Aug 1, 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: c74733b8-bdb3-41e5-b0d8-6d485dfdb571

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 size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Aug 1, 2026
Comment thread apps/web/src/components/ChatView.tsx
@macroscopeapp

macroscopeapp Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved 154ad23

This PR is a well-tested UI layout bug fix that prevents the terminal drawer from pushing the composer out of view. Changes consist of pure calculation functions with comprehensive unit tests (103+ lines), CSS layout adjustments, and measurement logic - all self-contained with no runtime behavior changes beyond the specific fix.

You can customize Macroscope's approvability policy. Learn more.

@carlosricojr
carlosricojr force-pushed the fix/composer-over-thread-header branch from 0929a90 to 3a8c2e0 Compare August 1, 2026 22:45
@carlosricojr carlosricojr changed the title fix(web): stop a tall terminal drawer pushing the composer over the thread header fix(web): stop a tall terminal drawer pushing the composer out of the chat area Aug 1, 2026
@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 1, 2026
Comment thread apps/web/src/components/ChatView.tsx Outdated
… chat area

The composer is an overlay, and the chat area it overlays could shrink to
nothing (`min-h-0`) while the terminal drawer held an explicit height of up to
75% of the window. Once a tall drawer left the chat area shorter than the
composer, nothing clipped the overlay — it spilled out of the area instead.

Docked, it spilled upward over the thread header and off the top of the
viewport. On a new thread it is worse: the hero composer is centred in the chat
area, and a centred flex item that outgrows its container overflows *both*
ends, so it covered the header and ran back down across the top of the
terminal, with the headline pushed off-screen above. Its glass surface let the
header and the terminal read through it either way.

The chat area now reserves the height the composer actually needs, so flex
layout shrinks the drawer rather than the composer's room. Docked, that is the
overlay's own height. In the hero state the overlay spans the whole area, so
the reserve is the composer stack, plus twice any banner — centring mirrors a
banner's space below the composer, and banners carry actions.

The hero headline is deliberately not reserved. It is decorative, and paying
for it costs the drawer 138px it should keep; it hides instead once the area
cannot hold it, and comes back when the drawer shrinks. Hidden means
`visibility: hidden`, since the decision reads its measured height and
unmounting it would oscillate. The drawer now keeps 644 of its 675px cap at a
900px window instead of 506, and is untouched above ~1024px.

The drawer became shrinkable to allow the reserve (its wrapper is
`display: contents` so the drawer itself is the flex child that yields) and
keeps a floor at MIN_DRAWER_HEIGHT so the resize handle stays reachable. Drags
now start from the height layout actually granted, so the divider keeps
tracking the pointer while the drawer is being squeezed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@carlosricojr
carlosricojr force-pushed the fix/composer-over-thread-header branch from 3a8c2e0 to e4d5290 Compare August 1, 2026 23:10

@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 e4d5290. Configure here.

Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx
…ed drags

Two follow-ups from review of the reserve.

The measuring effect only re-ran when the overlay node or the hero state
changed, but the overlay node is reused across threads. Switching to a thread
with a taller composer left the previous thread's reserve in place until the
resize observer caught up a frame later, which is long enough for the composer
to spill again with the drawer open. The active thread key now re-measures it
in the same layout pass.

A drag while the reserve squeezes the drawer anchored on the rendered height
and then persisted the result, so nudging the divider up by 10px rewrote a
stored 675px down to 454px — the user asked for more room and silently lost
their terminal size, with no visible feedback because the squeeze caps what
renders either way. A drag that ends above where it started now keeps the
larger stored height; only a drag that ends below it is treated as a request to
shrink.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread apps/web/src/components/ThreadTerminalDrawer.tsx Outdated
resolveDrawerResizeStoredHeight took the shrink branch on equality. Because the
drag re-anchors on the squeezed rendered height, the first pointer move sets
that height even with no net movement, so pressing the divider and returning to
the starting point persisted the squeezed height over a taller stored one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 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.

1 participant