From 3e94afa7c26734f525a7d8a9cdb47896e8618cf8 Mon Sep 17 00:00:00 2001 From: maria Date: Fri, 9 Oct 2026 22:32:19 -0300 Subject: [PATCH] fix(web): composer banner actions stay inline whenever they fit (#17640) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> --- apps/web/src/components/ChatView.tsx | 2 -- .../web/src/components/chat/ComposerBanner.tsx | 11 ++++------- .../components/chat/ComposerBannerStack.tsx | 18 +++++------------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index a62da612e68846542c4cd6a6a9a11c9c1faf107a..3c46bfa90299b47ab88faa82949891d932e186d1 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -2542,7 +2542,6 @@ export default function ChatView(props: ChatViewProps) { return { id: `project-clone:${projectId}`, variant: "info", - compact: true, priority: "activity", icon: , title: `Cloning ${name}`, @@ -2566,7 +2565,6 @@ export default function ChatView(props: ChatViewProps) { return { id: `project-clone:${projectId}`, variant: cancelled ? "warning" : "error", - compact: true, icon: , title: cancelled ? `Cancelled cloning ${name}` : `Failed to clone ${name}`, description: cancelled ? "Retry to bring in the repository." : activeProjectClone.error, diff --git a/apps/web/src/components/chat/ComposerBanner.tsx b/apps/web/src/components/chat/ComposerBanner.tsx index cbcb588971570bd45dded7c6156a57e643bece7c..317c2136da87f1d44b3a6e9a9a1a2a7c31cb3825 100644 --- a/apps/web/src/components/chat/ComposerBanner.tsx +++ b/apps/web/src/components/chat/ComposerBanner.tsx @@ -183,17 +183,15 @@ function Row({ layout = "inline", ...props }: useRender.ComponentProps<"div"> & { - layout?: "inline" | "wrap-actions" | "wrap-actions-narrow" | "approval"; + layout?: "inline" | "wrap-actions" | "approval"; }) { const rowProps = { className: cn( "group/banner-row grid min-h-(--composer-banner-icon-column) w-full min-w-0 grid-cols-[var(--composer-banner-icon-column)_minmax(0,1fr)_auto] items-center gap-x-1 text-start", "not-has-[>[data-slot=composer-banner-actions]]:grid-cols-[var(--composer-banner-icon-column)_minmax(0,1fr)]", "[&:is(button)]:cursor-pointer [&:is(button)]:rounded-md [&:is(button)]:focus-visible:outline-2 [&:is(button)]:focus-visible:-outline-offset-2 [&:is(button)]:focus-visible:outline-ring", - layout === "wrap-actions" && - "@max-[400px]:*:data-[slot=composer-banner-content]:min-h-(--composer-banner-icon-column)", - layout === "wrap-actions-narrow" && - "@max-[320px]:*:data-[slot=composer-banner-content]:min-h-(--composer-banner-icon-column)", + // Actions share the title row while the title fits and move below it otherwise. + layout === "wrap-actions" && "flex flex-wrap *:data-[slot=composer-banner-content]:grow", layout === "approval" && "items-start gap-x-2 gap-y-3", className, ), @@ -257,8 +255,7 @@ function Actions({ className, ...props }: ComponentProps<"span">) { className={cn( "col-start-3 row-start-1 flex flex-wrap items-center justify-end gap-1", "group-data-[composer-banner-layout=approval]/banner-row:self-center group-data-[composer-banner-layout=approval]/banner-row:gap-1.5 @max-[560px]:group-data-[composer-banner-layout=approval]/banner-row:col-start-2 @max-[560px]:group-data-[composer-banner-layout=approval]/banner-row:col-end-4 @max-[560px]:group-data-[composer-banner-layout=approval]/banner-row:row-start-2", - "@max-[400px]:group-data-[composer-banner-layout=wrap-actions]/banner-row:has-[>:nth-child(2)]:col-start-2 @max-[400px]:group-data-[composer-banner-layout=wrap-actions]/banner-row:has-[>:nth-child(2)]:col-end-4 @max-[400px]:group-data-[composer-banner-layout=wrap-actions]/banner-row:has-[>:nth-child(2)]:row-start-2 @max-[400px]:group-data-[composer-banner-layout=wrap-actions]/banner-row:has-[>:nth-child(2)]:justify-end", - "@max-[320px]:group-data-[composer-banner-layout=wrap-actions-narrow]/banner-row:has-[>:nth-child(2)]:col-start-2 @max-[320px]:group-data-[composer-banner-layout=wrap-actions-narrow]/banner-row:has-[>:nth-child(2)]:col-end-4 @max-[320px]:group-data-[composer-banner-layout=wrap-actions-narrow]/banner-row:has-[>:nth-child(2)]:row-start-2 @max-[320px]:group-data-[composer-banner-layout=wrap-actions-narrow]/banner-row:has-[>:nth-child(2)]:-ms-2 @max-[320px]:group-data-[composer-banner-layout=wrap-actions-narrow]/banner-row:has-[>:nth-child(2)]:justify-start", + "group-data-[composer-banner-layout=wrap-actions]/banner-row:ms-auto", className, )} {...props} diff --git a/apps/web/src/components/chat/ComposerBannerStack.tsx b/apps/web/src/components/chat/ComposerBannerStack.tsx index ff66869a9ec86dfb800b6f6a7355843d8eb093ee..df69e60c8fafc59f01c824b8c47928e267c16522 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.tsx @@ -13,7 +13,6 @@ export interface ComposerBannerStackItem { readonly id: string; readonly variant: ComposerBannerVariant; readonly priority?: "urgent" | "activity" | "notice"; - readonly compact?: boolean; readonly icon: ReactNode; readonly title: ReactNode; readonly description?: ReactNode; @@ -250,7 +249,7 @@ export function ComposerBannerStack({ className, items, attachedAbove }: Compose } /** Keep full descriptions reachable only when their inline copy is clipped. */ -function NoticeDescription({ children, compact }: { children: ReactNode; compact?: boolean }) { +function NoticeDescription({ children }: { children: ReactNode }) { const descriptionRef = useRef(null); const detailsRef = useRef(null); const [showDetails, setShowDetails] = useState(false); @@ -283,13 +282,10 @@ function NoticeDescription({ children, compact }: { children: ReactNode; compact }, []); return ( - + {children} @@ -354,17 +350,13 @@ function ComposerBannerStackAlert({ variant={item.variant} density="comfortable" > - + {item.icon} {item.title} - {item.description ? ( - - {item.description} - - ) : null} + {item.description ? {item.description} : null} {item.actions || item.onDismiss ? ( -- 2.54.0