diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index 7bf3ecf26c..b346187614 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -1758,6 +1758,57 @@ describe("ChatView timeline estimator parity (full app)", () => { } }); + it("does not start a new turn when Enter is pressed while the thread is running", async () => { + useComposerDraftStore.getState().setPrompt(THREAD_ID, "follow-up while running"); + + const mounted = await mountChatView({ + viewport: DEFAULT_VIEWPORT, + snapshot: createSnapshotForTargetUser({ + targetMessageId: "msg-user-enter-while-running" as MessageId, + targetText: "enter while running target", + sessionStatus: "running", + }), + }); + + try { + const composerEditor = await waitForComposerEditor(); + const initialTurnStartRequestCount = wsRequests.filter( + (request) => + request._tag === ORCHESTRATION_WS_METHODS.dispatchCommand && + request.command && + typeof request.command === "object" && + "type" in request.command && + request.command.type === "thread.turn.start", + ).length; + + composerEditor.focus(); + composerEditor.dispatchEvent( + new KeyboardEvent("keydown", { + key: "Enter", + bubbles: true, + cancelable: true, + }), + ); + await waitForLayout(); + + const nextTurnStartRequestCount = wsRequests.filter( + (request) => + request._tag === ORCHESTRATION_WS_METHODS.dispatchCommand && + request.command && + typeof request.command === "object" && + "type" in request.command && + request.command.type === "thread.turn.start", + ).length; + + expect(nextTurnStartRequestCount).toBe(initialTurnStartRequestCount); + expect(document.querySelector('button[aria-label="Stop generation"]')).toBeInstanceOf( + HTMLButtonElement, + ); + } 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 1d926bf308..7622eb94b1 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -2431,11 +2431,14 @@ export default function ChatView({ threadId }: ChatViewProps) { const onSend = async (e?: { preventDefault: () => void }) => { e?.preventDefault(); const api = readNativeApi(); - if (!api || !activeThread || isSendBusy || isConnecting || sendInFlightRef.current) return; + if (!api || !activeThread) return; if (activePendingProgress) { onAdvanceActivePendingUserInput(); return; } + if (phase === "running" || isSendBusy || isConnecting || sendInFlightRef.current) { + return; + } const promptForSend = promptRef.current; const { trimmedPrompt: trimmed,