summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--backend-handoff.md8
-rw-r--r--src/features/chat/ui.test.ts14
-rw-r--r--src/features/chat/ui/Composer.svelte6
3 files changed, 23 insertions, 5 deletions
diff --git a/backend-handoff.md b/backend-handoff.md
index d1d0756..8d30083 100644
--- a/backend-handoff.md
+++ b/backend-handoff.md
@@ -18,6 +18,14 @@
> ?archived=`) was made in the same branch and the FE consumes it via the `file:` deps (dists
> rebuilt). Recorded below.
+## Milestone: Consistent composer action width (`dev`, 2026-10-24)
+
+- Send, Queue, Stop, and Flush now share the same fixed `w-20` width; Flush no longer
+ widens the composer or changes text wrapping. Button actions and labels are unchanged.
+- Added regression coverage for the shared width across all four labels.
+- Verified: typecheck (0 errors/warnings), tests (2,591 passed; 2 skipped), Biome, and build.
+- Backend contracts, pins, and mirrors unchanged; no backend work required.
+
## Milestone: Close-tab cascade subagents (`feature/close-tab-cascade-subagents`, 2026-10-24)
**Slice status: COMPLETE across the isolated frontend + backend feature branches.**
diff --git a/src/features/chat/ui.test.ts b/src/features/chat/ui.test.ts
index 74c0619..f98fdbb 100644
--- a/src/features/chat/ui.test.ts
+++ b/src/features/chat/ui.test.ts
@@ -850,6 +850,20 @@ describe("ChatView", () => {
describe("Composer", () => {
it.each([
+ { status: "idle", queuedMessageCount: 0, text: "", label: "Send" },
+ { status: "running", queuedMessageCount: 0, text: "next", label: "Queue" },
+ { status: "running", queuedMessageCount: 0, text: "", label: "Stop generation" },
+ { status: "running", queuedMessageCount: 1, text: "", label: "Flush" },
+ ] as const)("keeps $label at the same fixed width", async ({ label, text, ...props }) => {
+ const user = userEvent.setup();
+ render(Composer, { props: { ...props, onSend: vi.fn(), onQueue: vi.fn(), onStop: vi.fn() } });
+ if (text) await user.type(screen.getByRole("textbox", { name: "Message input" }), text);
+ const button = screen.getByRole("button", { name: label });
+ expect(button).toHaveClass("w-20", "shrink-0");
+ expect(button).not.toHaveClass("w-36");
+ });
+
+ it.each([
"running",
"queued",
] as const)("relabels Stop only with queued input while %s and preserves its action", async (status) => {
diff --git a/src/features/chat/ui/Composer.svelte b/src/features/chat/ui/Composer.svelte
index a43ed53..cd451bc 100644
--- a/src/features/chat/ui/Composer.svelte
+++ b/src/features/chat/ui/Composer.svelte
@@ -376,11 +376,7 @@
{#if buttonMode === "stop"}
<button
- class="btn btn-error shrink-0"
- class:w-20={stopButton.text === "Stop"}
- class:w-36={stopButton.text !== "Stop"}
- class:whitespace-normal={stopButton.text !== "Stop"}
- class:leading-tight={stopButton.text !== "Stop"}
+ class="btn btn-error w-20 shrink-0"
type="button"
aria-label={stopButton.ariaLabel}
title={stopButton.title}