From 7a3632e8635d0e58e75096d6c9ac9af3b4d87b2a Mon Sep 17 00:00:00 2001 From: Roo Code Date: Mon, 4 May 2026 19:51:50 +0000 Subject: [PATCH] fix: show stop button in chat input during follow-up questions When the agent asks a follow-up question, the normal stop button in the chat input area was not visible because isStreaming is false during asks. This left users with no way to abort the task during follow-up questions. Added a dedicated stop button that appears in the chat text area when there is an active follow-up question (canStopTask prop). This button is separate from the morphing send/stop button, so users can both send a response AND stop the task. Fixes #12268 --- .../src/components/chat/ChatTextArea.tsx | 26 +++++++++ webview-ui/src/components/chat/ChatView.tsx | 1 + .../chat/__tests__/ChatTextArea.spec.tsx | 55 +++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index e72c1726f35..e6d6b5a0eff 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -54,6 +54,7 @@ interface ChatTextAreaProps { onCancel?: () => void // Stop/Queue functionality isStreaming?: boolean + canStopTask?: boolean onStop?: () => void onEnqueueMessage?: () => void } @@ -77,6 +78,7 @@ export const ChatTextArea = forwardRef( isEditMode = false, onCancel, isStreaming = false, + canStopTask = false, onStop, onEnqueueMessage, }, @@ -1219,6 +1221,30 @@ export const ChatTextArea = forwardRef( )} + {/* Stop button - shown when task is active (e.g. during follow-up questions) but not streaming */} + {!isEditMode && !isStreaming && canStopTask && ( + + + + )} {/* Send/Stop button - morphs based on streaming state, always visible in edit mode */} diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx index 0b63a68f4ec..19ffe0f7e2e 100644 --- a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx @@ -1205,4 +1205,59 @@ describe("ChatTextArea", () => { expect(sendButton).toHaveClass("pointer-events-auto") }) }) + + describe("stop button during follow-up questions", () => { + it("should show a stop button when canStopTask is true and not streaming", () => { + const onStop = vi.fn() + const { container } = render( + , + ) + + // Find the stop button by looking for the button with the Square icon (fill style) + const buttons = container.querySelectorAll("button") + const stopButton = Array.from(buttons).find((button) => button.querySelector(".lucide-square") !== null) + + expect(stopButton).toBeInTheDocument() + }) + + it("should not show the separate stop button when canStopTask is false", () => { + const onStop = vi.fn() + const { container } = render( + , + ) + + // Should not find any button with Square icon when not streaming and canStopTask is false + const buttons = container.querySelectorAll("button") + const stopButton = Array.from(buttons).find((button) => button.querySelector(".lucide-square") !== null) + + expect(stopButton).not.toBeDefined() + }) + + it("should not show the separate stop button when isStreaming is true (morphed button handles it)", () => { + const onStop = vi.fn() + const { container } = render( + , + ) + + // When streaming, the morphed send/stop button shows the Square icon + // but the separate stop button should NOT be rendered + const buttons = container.querySelectorAll("button") + const squareButtons = Array.from(buttons).filter( + (button) => button.querySelector(".lucide-square") !== null, + ) + + // Only 1 square button (the morphed send/stop), not 2 + expect(squareButtons.length).toBe(1) + }) + + it("should call onStop when the stop button is clicked during a follow-up question", () => { + const onStop = vi.fn() + render() + + const stopButton = screen.getByRole("button", { name: /stop/i }) + fireEvent.click(stopButton) + + expect(onStop).toHaveBeenCalledTimes(1) + }) + }) })