Fix oversized transcript–composer gap when a task panel is showing#138
Open
yaacovcorcos wants to merge 3 commits into
Open
Fix oversized transcript–composer gap when a task panel is showing#138yaacovcorcos wants to merge 3 commits into
yaacovcorcos wants to merge 3 commits into
Conversation
The composer is a flex sibling below the transcript, so the stacked composer chrome (live-changes header, active task list, queued follow-ups) already reserves its own vertical space through the flex layout. The transcript then fed that same chrome height into its in-list bottom spacer (bottomContentInsetPx = composerStackedChromeHeight + 8), reserving the space a second time. The result was a large, growing gap between the last message and the composer whenever a task/stacked panel was present, while the no-task case (spacer at its 64px baseline) already looked correct. Remove the double reservation: the transcript's bottom spacer is now a fixed 64px in every state, matching the perfect no-task gap. This also retires the now-vestigial bottomContentInsetPx prop chain (ChatView → ChatTranscriptPane → MessagesTimeline / AgentActivityDetailView), collapsing both consumers to their constant baseline and correcting the misleading "composer overlaps / reserves matching bottom space" comments. composerStackedChromeHeight is kept for its real remaining job: the scroll-compensation layout effect that keeps the transcript pinned to its end as the flex viewport shrinks while the chrome grows. - Task-present gap now equals the no-task gap (single flex reservation). - Agent activity detail view uses the same fixed baseline (it was also double-counting). - No behavior change to the no-task case. Verified: oxfmt, oxlint (0 errors), tsc --noEmit (web), and the affected component/scroll unit tests (106 passing). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prove the stacked composer chrome is reserved only once: a [geometry:linux] browser test mounts ChatView with an active task card and again once the turn settles (no card), then asserts the gap between the transcript tail and the composer stack is unchanged. It anchors on the tail spacer's top (the true end of transcript content, past any live-turn activity indicator) so a running turn does not confound the measurement. Verified the test fails on the pre-fix code — the gap balloons to ~180px when the task card appears — and passes on the fix (~44px in both states). Adds two stable test hooks: data-chat-composer-stack on the composer block wrapper and data-testid="transcript-bottom-spacer" on the list tail spacer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a task/stacked panel is showing above the composer (active task list, live-changes header, queued follow-ups), an oversized gap opens up between the last message in the transcript and the composer. Without a task panel the gap is correct; with one, the transcript "hangs" far above the panel.
Root cause — a double space reservation
The composer is a flex sibling below the transcript, not an absolute overlay. So the stacked composer chrome already reserves its own vertical space through the flex layout (the transcript viewport shrinks from the bottom as the chrome grows).
On top of that, the transcript also fed the same chrome height into its in-list bottom spacer:
So the chrome height was reserved twice — once by flex, once by the spacer — producing a gap that grows with the task panel. The no-task path (spacer at its 64px baseline) was already correct.
Fix
Make the layout do a single space reservation:
composerStackedChromeHeight-driven feed, so the transcript's bottom spacer stays at its fixed 64px baseline in every state. The task-present gap now equals the perfect no-task gap.bottomContentInsetPxprop chain (ChatView → ChatTranscriptPane → MessagesTimeline/AgentActivityDetailView) rather than leaving a dead, always-undefinedprop threaded through four files. Both consumers collapse to their constant baseline; theMIN_-prefixed constants are renamed since nothing overrides them anymore.composerStackedChromeHeightis kept — it still drives the scroll-compensation layout effect that keeps the transcript pinned to its end as the flex viewport shrinks while the chrome grows. With the double-count gone, that compensation now lands exactly at the bottom.Behavior
data-*hooks are additive on top.Verification
oxfmtcleanoxlint— 0 errors (changed files: 0 warnings)tsc --noEmit(web) — cleanMessagesTimeline,ChatTranscriptPane,AgentActivityDetailView,MessagesTimeline.logic,chat-scroll)ChatView.browserstable 71 passing, geometry 12 passingRegression coverage
Added a
[geometry:linux]browser test inChatView.browser.tsxthat mounts ChatView with an active task card and again once the turn settles (no card), then asserts the gap between the transcript tail and the composer stack is unchanged. It anchors on the tail spacer's top — the true end of transcript content, past any live-turn activity indicator — so a running turn does not confound the measurement.Confirmed it is a genuine regression guard (not a tautology) by running it against the pre-fix code:
Two small, purely-additive test hooks were added for stable anchoring:
data-chat-composer-stackon the composer block wrapper anddata-testid="transcript-bottom-spacer"on the list tail spacer.🤖 Generated with Claude Code