authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-10-09 22:32:19-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-09 22:32:19-03:00
log3e94afa7c26734f525a7d8a9cdb47896e8618cf8
tree3f601a34a21423508a131a4171727b67c38eff32
parentcc62349c37fa486000b89c7ecc975c1bf66ec8a4
signature Signed by PGP key B5690EEEBB952194

fix(web): composer banner actions stay inline whenever they fit (#17640)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

3 files changed, 9 insertions(+), 22 deletions(-)

apps/web/src/components/ChatView.tsx-2
......@@ -2542,7 +2542,6 @@ export default function ChatView(props: ChatViewProps) {
25422542 return {
25432543 id: `project-clone:${projectId}`,
25442544 variant: "info",
2545 compact: true,
25462545 priority: "activity",
25472546 icon: <DownloadIcon />,
25482547 title: `Cloning ${name}`,
......@@ -2566,7 +2565,6 @@ export default function ChatView(props: ChatViewProps) {
25662565 return {
25672566 id: `project-clone:${projectId}`,
25682567 variant: cancelled ? "warning" : "error",
2569 compact: true,
25702568 icon: <DownloadIcon />,
25712569 title: cancelled ? `Cancelled cloning ${name}` : `Failed to clone ${name}`,
25722570 description: cancelled ? "Retry to bring in the repository." : activeProjectClone.error,
apps/web/src/components/chat/ComposerBanner.tsx+4-7
......@@ -183,17 +183,15 @@ function Row({
183183 layout = "inline",
184184 ...props
185185}: useRender.ComponentProps<"div"> & {
186 layout?: "inline" | "wrap-actions" | "wrap-actions-narrow" | "approval";
186 layout?: "inline" | "wrap-actions" | "approval";
187187}) {
188188 const rowProps = {
189189 className: cn(
190190 "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",
191191 "not-has-[>[data-slot=composer-banner-actions]]:grid-cols-[var(--composer-banner-icon-column)_minmax(0,1fr)]",
192192 "[&: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",
193 layout === "wrap-actions" &&
194 "@max-[400px]:*:data-[slot=composer-banner-content]:min-h-(--composer-banner-icon-column)",
195 layout === "wrap-actions-narrow" &&
196 "@max-[320px]:*:data-[slot=composer-banner-content]:min-h-(--composer-banner-icon-column)",
193 // Actions share the title row while the title fits and move below it otherwise.
194 layout === "wrap-actions" && "flex flex-wrap *:data-[slot=composer-banner-content]:grow",
197195 layout === "approval" && "items-start gap-x-2 gap-y-3",
198196 className,
199197 ),
......@@ -257,8 +255,7 @@ function Actions({ className, ...props }: ComponentProps<"span">) {
257255 className={cn(
258256 "col-start-3 row-start-1 flex flex-wrap items-center justify-end gap-1",
259257 "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",
260 "@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",
261 "@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",
258 "group-data-[composer-banner-layout=wrap-actions]/banner-row:ms-auto",
262259 className,
263260 )}
264261 {...props}
apps/web/src/components/chat/ComposerBannerStack.tsx+5-13
......@@ -13,7 +13,6 @@ export interface ComposerBannerStackItem {
1313 readonly id: string;
1414 readonly variant: ComposerBannerVariant;
1515 readonly priority?: "urgent" | "activity" | "notice";
16 readonly compact?: boolean;
1716 readonly icon: ReactNode;
1817 readonly title: ReactNode;
1918 readonly description?: ReactNode;
......@@ -250,7 +249,7 @@ export function ComposerBannerStack({ className, items, attachedAbove }: Compose
250249}
251250
252251/** Keep full descriptions reachable only when their inline copy is clipped. */
253function NoticeDescription({ children, compact }: { children: ReactNode; compact?: boolean }) {
252function NoticeDescription({ children }: { children: ReactNode }) {
254253 const descriptionRef = useRef<HTMLSpanElement>(null);
255254 const detailsRef = useRef<HTMLButtonElement>(null);
256255 const [showDetails, setShowDetails] = useState(false);
......@@ -283,13 +282,10 @@ function NoticeDescription({ children, compact }: { children: ReactNode; compact
283282 }, []);
284283
285284 return (
286 <span className={compact ? "contents" : "flex min-w-8 flex-1 items-center gap-1"}>
285 <span className="contents">
287286 <span
288287 ref={descriptionRef}
289 className={cn(
290 "min-w-0 truncate text-muted-foreground",
291 compact && "shrink-[9999] @max-[400px]:sr-only",
292 )}
288 className="w-0 min-w-0 grow truncate text-muted-foreground @max-[400px]:sr-only"
293289 >
294290 {children}
295291 </span>
......@@ -354,17 +350,13 @@ function ComposerBannerStackAlert({
354350 variant={item.variant}
355351 density="comfortable"
356352 >
357 <ComposerBanner.Row layout={item.compact ? "wrap-actions-narrow" : "wrap-actions"}>
353 <ComposerBanner.Row layout="wrap-actions">
358354 <ComposerBanner.Icon className="h-(--composer-banner-icon-column) self-start">
359355 {item.icon}
360356 </ComposerBanner.Icon>
361357 <ComposerBanner.Content className="whitespace-nowrap">
362358 <span className="min-w-0 truncate font-medium leading-7 sm:leading-6">{item.title}</span>
363 {item.description ? (
364 <NoticeDescription compact={item.compact ?? false}>
365 {item.description}
366 </NoticeDescription>
367 ) : null}
359 {item.description ? <NoticeDescription>{item.description}</NoticeDescription> : null}
368360 </ComposerBanner.Content>
369361 {item.actions || item.onDismiss ? (
370362 <ComposerBanner.Actions>