From 8aeaf9b51ff4409dc292025f93c66dff468b0a98 Mon Sep 17 00:00:00 2001 From: Zeus-Deus Date: Tue, 20 Oct 2026 11:03:25 +0200 Subject: [PATCH 1/2] fix(web): stacked composer notices line up their details buttons Each stacked notice sized its action column to its own buttons, so a Stop notice put its details button a few pixels right of a Clear notice's. Give the action column a shared minimum width so short actions line up. Co-Authored-By: Claude Opus 5.5 --- .../chat/ComposerBannerStack.browser.test.tsx | 46 +++++++++++++++++++ .../components/chat/ComposerBannerStack.tsx | 4 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/chat/ComposerBannerStack.browser.test.tsx b/apps/web/src/components/chat/ComposerBannerStack.browser.test.tsx index ecb56eab5debd..de7994f64de9a 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.browser.test.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.browser.test.tsx @@ -5,6 +5,8 @@ import { createRoot, type Root } from "react-dom/client"; import { afterEach, beforeEach, expect, it, vi } from "vite-plus/test"; import { page, userEvent } from "vite-plus/test/browser"; +import { Button } from "../ui/button"; + import { ComposerBannerStack } from "./ComposerBannerStack"; let root: Root; @@ -141,3 +143,47 @@ it("reveals the full running command only while its title is clipped", async () await expect.element(details).not.toBeInTheDocument(); await expect.element(popup).not.toBeInTheDocument(); }); + +// Narrow viewports use larger touch-sized buttons, so check both breakpoints. +it.each([480, 1000])( + "lines up the details buttons of stacked notices whose actions differ in width at %ipx", + async (viewportWidth) => { + await page.viewport(viewportWidth, 600); + container.style.width = `${viewportWidth - 32}px`; + const long = "Stress test the build pipeline end to end and report every failure ".repeat(3); + await act(async () => + root.render( + Clear, + }, + { + id: "background-work", + variant: "default", + priority: "activity", + icon: null, + title: `Waiting on subagent ${long}`, + actions: , + }, + ]} + />, + ), + ); + // The pointer left over from the previous case may already have expanded the stack. + await act(async () => + container.querySelector('[data-slot="composer-banner-peek"]')?.click(), + ); + await nextFrame(); + + const left = (label: string) => + page.getByRole("button", { name: label }).element().getBoundingClientRect().left; + expect(left("Show title details")).toBe(left("Show notice details")); + }, +); diff --git a/apps/web/src/components/chat/ComposerBannerStack.tsx b/apps/web/src/components/chat/ComposerBannerStack.tsx index fc309c5352a67..8d817665f065e 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.tsx @@ -374,8 +374,10 @@ function ComposerBannerStackAlert({ ) : null} + {/* A shared minimum keeps short actions (Stop, Clear, dismiss) the same width, + so the details buttons of stacked notices line up. */} {item.actions || item.onDismiss ? ( - + {item.actions} {item.onDismiss ? ( Date: Mon, 26 Oct 2026 20:13:37 +0000 Subject: [PATCH 2/2] refactor(web): keep notice alignment to shared minimum widths --- apps/web/src/components/chat/ComposerBannerStack.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/src/components/chat/ComposerBannerStack.tsx b/apps/web/src/components/chat/ComposerBannerStack.tsx index ae82527e5ce95..5b95a28c1b0eb 100644 --- a/apps/web/src/components/chat/ComposerBannerStack.tsx +++ b/apps/web/src/components/chat/ComposerBannerStack.tsx @@ -355,8 +355,6 @@ function ComposerBannerStackAlert({ ) : null} - {/* A shared minimum keeps short actions (Stop, Clear, dismiss) the same width, - so the details buttons of stacked notices line up. */} {item.actions || item.onDismiss ? ( {item.actions}