🤖 fix: reclaim the mobile bottom band and move the PR badge to the header - #3757
Conversation
…ader The workspace footer bar reserved the full iOS bottom safe-area inset, but every ancestor between it and the app root clips overflow at the root's content edge, so its negative margin never reached the screen edge. Drop the root's bottom inset at narrow widths and let the footer own a small clearance instead. Also move the PR badge to the workspace header below 768px (it was overlapping the drift toggle on phones because the touch-target min-width lets a link shrink below its content), and add a phone story asserting the placement.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c51762b18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d00ceaeaf9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 078a582c71
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review The only remaining thread was the footer clearance cap, which is an intentional owner-requested tradeoff (rationale in the resolved thread). Nothing else changed since the last round. Please confirm the rest of the diff. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 078a582c71
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 411632987c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7cbdb293b0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review All functional checks pass on The footer clearance cap is an intentional owner-requested tradeoff with the rationale recorded in the resolved thread; please review the rest of the diff on that basis rather than re-raising it. |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…transcript column (#3758) ## Summary Removes the "Let's get building." headline from the creation page, and lines every surface in the composer dock up with the transcript column so the composer stops floating in the middle of a full-width transcript. ## Background Both reported from the chrome redesign in #3753. Rebased onto `main` now that #3757 has merged, so this diff is only the two commits below. The headline gave a static string the largest type on the page. It was already `sr-only` below 768px, so this removes it outright. The alignment bug is structural. The composer dock lives *inside* the transcript scrollport and cancels its `px-[15px]` gutter with `mx-[-15px]` so it can paint full-bleed. Every surface inside the dock therefore has to re-apply that gutter and follow the transcript's width mode. They all hardcoded `px-4` (16px) plus `mx-auto max-w-4xl` instead. Centered mode hid this completely: the transcript and the docked surfaces shared both a center line and the same `4xl` cap, so their edges coincided by accident. Turn on `chatTranscriptFullWidth` and the transcript uncaps while everything docked stays 896px wide. ## Implementation `CHAT_DOCK_GUTTER_CLASS` (in `constants/layout`) plus `ChatDockColumnProvider` / `useChatDockColumnWidthClass` / `ChatDockSurface` (in `components/ChatPane/chatDockColumn`) now carry the contract. The dock provides the width mode; surfaces inside consume it. Applied to the composer section, `ChatInputDecoration` (todos, background bashes, reviews, queued messages, chat instructions), `ConcurrentLocalWarningDecoration`, and `TranscriptOnlyNoticePane`. The second commit sweeps three decoration-lane entries that never followed the column in *either* mode: the compaction warning and context-switch banner carried their own `mx-4`, and the pre-stream task card had no gutter at all. All three go through `ChatDockSurface`, and the two `mx-4` values are removed so the wrapper is the single source of the inset. The width mode resolves through context rather than calling `useChatTranscriptFullWidth` per surface, because that hook also issues a `config.getConfig` fetch and an `onConfigChanged` subscription per instance, and the dock can hold several decorations at once. ## Validation Measured at 1500px with the left sidebar collapsed: in full-width mode the transcript rows, composer box, background-bash decoration, and auto-compact label all span 35..1465; centered, all cap at 896. Two play tests (`FullWidthTranscriptAlignment`, `CenteredTranscriptAlignment`) assert every docked surface shares the transcript column's left and right edges, including each `ChatDockSurface`-wrapped entry. Four red-green toggles: | toggle | failure | | --- | --- | | width class reverted to the hardcoded cap | `Docked composer is inset ... by 157px left and 157px right` | | gutter reverted to `px-4` | `... by 1px left and 1px right` | | compaction warning unwrapped | `No ChatDockSurface-wrapped entry rendered` | | transcript pinned to centered | `Transcript is 896px wide, so this story is not in full-width mode` | The centered story separately asserts the scrollport has room to exceed the cap, so it proves the cap is what limits the column rather than the available width. One trap worth recording: `updatePersistedState(CHAT_TRANSCRIPT_FULL_WIDTH_KEY, true)` does **not** enable full width in a story, because `useChatTranscriptFullWidth` syncs from `config.getConfig` and overwrites it. The story would silently run centered and pass with the bug present. `setupSimpleChatStory` now takes `chatTranscriptFullWidth` and threads it to the mock config. ## Risks Expect Pixel diffs on any story showing the compaction warning, context-switch banner, or pre-stream task card: those move from a 16px full-bleed inset onto the composer column, which is the intended correction. Not fixed here, pre-existing and unrelated to width mode: in centered mode the composer's toast overlay is positioned against the outer section rather than the inner column, so it spans nearly the whole dock. Moving its containing block would also shift it vertically, so it needs its own change. --- _Generated with `mux` • Model: `anthropic:claude-opus-5` • Thinking: `xhigh`_ <!-- mux-attribution: model=anthropic:claude-opus-5 thinking=xhigh -->
Summary
On phones the workspace footer info bar stranded a band of empty space below it, and its PR badge overlapped the drift toggle. This hands the bottom safe-area inset to the chat column so the footer bar reaches the screen edge with a small home-indicator clearance, and moves the PR badge into the workspace header below 768px.
Background
Reported from an iPhone against the chrome redesign in #3753.
The footer bar was written to own the mobile bottom inset: a negative margin was supposed to pull its fill down to the screen edge while its own padding kept the controls clear of the home indicator. That never worked.
.mobile-main-content,.mobile-layout, and the workspace shell all setoverflow: hiddenand end at the app root's content edge, so anything the footer painted inside the root'spb-[env(safe-area-inset-bottom)]was clipped. The result was a ~34pt band under the bar in the root's own fill, which reads as dead space.The overlapping PR badge is a separate mobile-only bug: the touch-target rule in
globals.cssputs an explicitmin-width: 44pxon every link, which replaces the flex min-content floor, so the badge could shrink to 44px inside the footer's scrolling row while its label spilled over the next item.Implementation
ChatPanemarks itselfdata-bottom-inset-ownerwhile visible (dropped when immersive review hides it), andglobals.cssdrops the root's bottom padding at narrow widths via.mobile-bottom-inset-host:has([data-bottom-inset-owner]). Immersive review, the loading placeholder, settings, analytics, creation, and the root shells keep the inset with no route knowledge inApp, and any future footerless workspace surface inherits the safe default.WorkspaceFooterBar: no negative margin; at narrow widths it reserves an 8px-capped clearance. Its fill genuinely reaches the screen edge, and ~26pt comes back to the transcript.WorkspaceMenuBar: renders the PR badge below 768px; the footer copy hides at the same width. Duplicate-and-hide via CSS rather than a width hook, so there is no resize state and the hidden copy leaves the accessibility tree.WorkspaceLinksforwards aclassNameto the badge itself so hiding it does not leave an empty flex item behind in agap-2row.PRLinkBadge:shrink-0, fixing the overlap for any row that holds it.Validation
env(safe-area-inset-bottom)is 0 in a desktop browser, so the band this PR removes is invisible to normal Storybook checks. Measured over CDP (Emulation.setSafeAreaInsetsOverridewith bottom 34px, plus touch emulation and mobile device metrics):padding-bottomWide touch (1024px, same insets) is unchanged: the root keeps its inset, the footer reserves nothing, and the row still ends at the column's content edge. Light theme has no colour seam at the bottom edge, because the footer's own fill covers the clearance.
Two story assertions, both red-green verified:
IPhone16ePRLinkPlacement: exactly one PR link is visible, in the header when the viewport matches the narrow query and in the footer otherwise. Un-hiding the header copy fails withAt 1280px the PR link belongs in the footer, but 1 were visible in the header and 1 in the footer. Pixel captures the narrow placement; the test-runner runs at desktop width and covers the wide placement.IPhone17ProMaxTouchReviewImmersive: while immersive review is open, nothing claimsdata-bottom-inset-owner. Making the attribute unconditional fails withImmersive review left the bottom safe-area inset unowned.This one holds without an inset override because it tests the handoff, not the resolved padding.Risks
Footer controls now sit 8px above the physical edge rather than 34pt, so their bottom sliver overlaps the home-indicator strip. The indicator itself is a ~5pt pill within the bottom ~13pt, the strip is reserved for edge-swipe drags rather than taps, and touch targets there are 44px tall, so taps on the labels still land. Raising or removing the
pb-[min(env(safe-area-inset-bottom,0px),8px)]cap is a one-line change if it reads badly on device.Two surfaces now subscribe to
PRStatusStorefor the same workspace. The store keys its cache and refresh batching by workspace id, so this adds a subscriber rather than a secondgh pr view.Generated with
mux• Model:anthropic:claude-opus-5• Thinking:xhigh