From 4b0e9eb93dc3eca8fe72eedee4769c149aed38d6 Mon Sep 17 00:00:00 2001 From: BinBandit Date: Wed, 11 Mar 2026 20:59:19 +1100 Subject: [PATCH] Add pointer cursor to running stop-generation button - Add `cursor-pointer` to the running stop button in `ChatView` - Extend browser test snapshot helpers to set session status - Add regression test verifying stop button cursor is `pointer` while running --- apps/web/src/components/ChatView.browser.tsx | 32 +++++++++++++++++++- apps/web/src/components/ChatView.tsx | 2 +- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index d8b74c8cc..29021d463 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -57,6 +57,13 @@ interface ViewportSpec { attachmentTolerancePx: number; } +type SnapshotSessionStatus = + OrchestrationReadModel["threads"][number]["session"] extends infer Session + ? Session extends { status: infer Status } + ? Status + : never + : never; + const DEFAULT_VIEWPORT: ViewportSpec = { name: "desktop", width: 960, @@ -152,6 +159,7 @@ function createSnapshotForTargetUser(options: { targetMessageId: MessageId; targetText: string; targetAttachmentCount?: number; + sessionStatus?: SnapshotSessionStatus; }): OrchestrationReadModel { const messages: Array = []; @@ -221,7 +229,7 @@ function createSnapshotForTargetUser(options: { checkpoints: [], session: { threadId: THREAD_ID, - status: "ready", + status: options.sessionStatus ?? "ready", providerName: "codex", runtimeMode: "full-access", activeTurnId: null, @@ -994,6 +1002,28 @@ describe("ChatView timeline estimator parity (full app)", () => { } }); + it("shows a pointer cursor for the running stop button", async () => { + const mounted = await mountChatView({ + viewport: DEFAULT_VIEWPORT, + snapshot: createSnapshotForTargetUser({ + targetMessageId: "msg-user-stop-button-cursor" as MessageId, + targetText: "stop button cursor target", + sessionStatus: "running", + }), + }); + + try { + const stopButton = await waitForElement( + () => document.querySelector('button[aria-label="Stop generation"]'), + "Unable to find stop generation button.", + ); + + expect(getComputedStyle(stopButton).cursor).toBe("pointer"); + } finally { + await mounted.cleanup(); + } + }); + it("keeps the new thread selected after clicking the new-thread button", async () => { const mounted = await mountChatView({ viewport: DEFAULT_VIEWPORT, diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 3c8a0a152..f5770b0dc 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -3981,7 +3981,7 @@ export default function ChatView({ threadId }: ChatViewProps) { ) : phase === "running" ? (