fix(desktop): welcome banner overlap and missing dismiss control - #5330
Conversation
The WelcomeComposerGuidanceLayer was positioned absolute/bottom-full outside the composerWrapperRef measurement boundary, so its height was invisible to useComposerHeightPadding. The banner sat on top of the newest message and blocked thread affordances on that message. There was also no way to dismiss the banner without sending a message that mentions an agent. Fix overlap: Change WelcomeComposerGuidanceLayer from `absolute inset-x-0 bottom-full z-[-1]` to `relative` (in-flow). Being a normal-flow child of composer-dock, its full height is now measured by the ResizeObserver and fed into the timeline's paddingBottom, so the newest message is always fully visible and its thread affordance is always clickable while the banner shows. Fix dismiss: Add an X close button on the prompt state that fires `onDismiss`. The dismiss callback drives `dismissing → hidden` immediately (same slide-down animation as the auto-dismiss path) and marks the channel as completed so the banner does not reappear on channel re-entry within the session. Extract the banner state machine (refs, timers, effects, callbacks) into useWelcomeComposerBanner.ts to keep ChannelPane.tsx under the 1000-line file-size ratchet. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…sitioning expectWelcomeComposerBannerLayout tested geometry from the old absolute/bottom-full design where the banner's bottom extended past the composer's top edge. With the guidance layer now in normal flow, the banner sits entirely above the composer (no overlap) and the guidance backdrop bottom is strictly less than composerBox.y rather than approximately equal to it. Remove the z-index comparison — no longer meaningful once the guidance layer dropped its z-[-1] stacking context. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wesbillman
left a comment
There was a problem hiding this comment.
Reviewed on Wes's behalf. One user-visible state mismatch needs fixing before merge: manual dismissal can hide the banner while leaving the kickoff characters floating above the composer. Please couple those states (or suppress dismissal during setup) and cover the manual-dismiss path in E2E.
…fix CI layout assertion Three fixes folded into one push per review feedback: 1. Wes P1: suppress the dismiss X while settingUp is true. The kickoff stage characters are absolutely positioned above the composer; allowing dismiss while they are present would hide the banner but leave the stage floating with nothing under it until the 90s timeout. The setup window is transient so suppressing the X there is the minimal correct fix; no teardown coupling needed. 2. E2E coverage (Wes): two new onboarding.spec.ts tests exercise the dismiss path that was previously untested: - clicking X removes the guidance surface (banner + guidance layer gone) - dismiss persists on channel re-entry (completedChannelIdsRef holds) 3. CI fix: expectWelcomeComposerBannerLayout asserted dockBackdropBox.y ≈ composerBox.y, which was correct when the guidance layer was absolute bottom-full (dock only contained the composer). Now that the guidance layer is in-flow inside composer-dock, the backdrop's absolute inset-y-0 spans guidance-layer-top → bottom, so the correct assertion is dockBackdropBox.y ≈ guidanceLayerBox.y. Added guidanceLayerBox measurement to the helper and updated the assertion accordingly. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
ScreenshotsBefore — newest message (bob, 12:21 PM) occluded by the banner; no dismissThe banner renders After — all messages visible; × dismiss button shownThe guidance layer now renders in normal document flow inside the composer overlay, so its height is included in the |
wesbillman
left a comment
There was a problem hiding this comment.
Re-reviewed at b9fe344 on Wes's behalf. The prior blocker is resolved: the dismiss control is now suppressed while settingUp, so the kickoff stage cannot be orphaned, and the added E2E cases cover manual dismissal plus channel re-entry persistence. I found no new actionable issues in the follow-up delta.
I am leaving this as a comment rather than an approval because Wes did not explicitly delegate approval authority for this PR.
* origin/main: fix(desktop): welcome banner overlap and missing dismiss control (#5330) fix(desktop): prevent horizontal clipping in Prompt Context modal (#5324) chore(release): release Buzz Relay version 0.2.1 (#2856) chore(release): release Buzz Desktop version 0.5.8 (#5326) fix(buzz-agent): recover from 400-shaped image rejections; unbound benchmark agent rounds (#5318) Revert "fix(acp): reject unattended permission requests" (#5323) feat(desktop): unify add agent flows (#5015) fix(buzz-agent): budget summarizer reasoning separately so it cannot starve the handoff summary (#5248) infra: bind development services to loopback (#4871) chore(release): release Buzz Desktop version 0.5.7 (#5252) fix(desktop): isolate relay admission tests (#5221) fix(desktop): externalize boot <style> to prevent Tauri CSP nonce override (#5242) fix(desktop): let imported and recovered identities finish onboarding (#5228) Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>


Problem
The
WelcomeComposerGuidanceLayerin the#Welcomechannel was positioned withabsolute inset-x-0 bottom-full z-[-1]— outside thecomposerWrapperRefmeasurement boundary.useComposerHeightPaddingobservescomposerWrapperRef's block size to setpaddingBottomon the timeline scroll container, but the absolutely-positioned layer didn't contribute to that size. The banner sat directly on top of the newest message, blocking the thread affordance on that message, and had no manual dismiss control.Fix
Overlap: Changed
WelcomeComposerGuidanceLayerfromabsolute inset-x-0 bottom-full z-[-1]torelative(in normal flow). As a normal-flow child ofcomposer-dock, the layer's full height is now measured by the ResizeObserver and fed into the timeline'spaddingBottom, so the newest message is always fully visible and its thread affordance is always clickable while the banner shows.Dismiss: Added an
Xclose button (data-testid="welcome-composer-dismiss-button") on the prompt state. Clicking firesonDismiss, which drivesdismissing → hiddenimmediately (same slide-down animation as the auto-dismiss path) and marks the channel ID as completed in the session ref so the banner does not reappear on channel re-entry within the session.Refactor: Extracted the banner state machine (refs, timers,
useEffects, and callbacks) fromChannelPane.tsxintouseWelcomeComposerBanner.ts. This keepsChannelPane.tsxwell under the 1000-line file-size ratchet and makes the state machine independently testable.Changed files
desktop/src/features/channels/ui/WelcomeComposerBanner.tsx—WelcomeComposerGuidanceLayerpositioning fix;onDismissprop; dismiss button;overflow-hidden/mb-0/flex-1cleanupdesktop/src/features/channels/ui/ChannelPane.tsx— remove inline banner state machine, useuseWelcomeComposerBannerhook, passonDismissdesktop/src/features/channels/ui/useWelcomeComposerBanner.ts— new hook owning all banner state