This commit is contained in:
roomote-v0[bot] 2026-05-13 01:41:49 -04:00 committed by GitHub
commit fe8a6ffa67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 82 additions and 0 deletions

View file

@ -53,6 +53,7 @@ interface ChatTextAreaProps {
onCancel?: () => void
// Stop/Queue functionality
isStreaming?: boolean
canStopTask?: boolean
onStop?: () => void
onEnqueueMessage?: () => void
}
@ -76,6 +77,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
isEditMode = false,
onCancel,
isStreaming = false,
canStopTask = false,
onStop,
onEnqueueMessage,
},
@ -1217,6 +1219,30 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</button>
</StandardTooltip>
)}
{/* Stop button - shown when task is active (e.g. during follow-up questions) but not streaming */}
{!isEditMode && !isStreaming && canStopTask && (
<StandardTooltip content={t("chat:stop.title")}>
<button
aria-label={t("chat:stop.title")}
disabled={false}
onClick={onStop}
className={cn(
"relative inline-flex items-center justify-center",
"bg-transparent border-none p-1.5",
"rounded-full min-w-[28px] min-h-[28px]",
"text-vscode-descriptionForeground hover:text-vscode-foreground",
"transition-all duration-200",
"opacity-100 hover:opacity-100 pointer-events-auto",
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
"active:bg-[rgba(255,255,255,0.1)]",
"cursor-pointer",
"bg-vscode-button-background hover:bg-vscode-button-background",
)}>
<Square className="size-4 stroke-none fill-vscode-button-foreground" />
</button>
</StandardTooltip>
)}
{/* Send/Stop button - morphs based on streaming state, always visible in edit mode */}
<StandardTooltip
content={

View file

@ -1782,6 +1782,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
setMode={setMode}
modeShortcutText={modeShortcutText}
isStreaming={isStreaming}
canStopTask={!!task && !isStreaming && clineAsk === "followup"}
onStop={handleStopTask}
onEnqueueMessage={handleEnqueueCurrentMessage}
/>

View file

@ -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(
<ChatTextArea {...defaultProps} canStopTask={true} isStreaming={false} onStop={onStop} />,
)
// 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(
<ChatTextArea {...defaultProps} canStopTask={false} isStreaming={false} onStop={onStop} />,
)
// 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(
<ChatTextArea {...defaultProps} canStopTask={true} isStreaming={true} onStop={onStop} />,
)
// 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(<ChatTextArea {...defaultProps} canStopTask={true} isStreaming={false} onStop={onStop} />)
const stopButton = screen.getByRole("button", { name: /stop/i })
fireEvent.click(stopButton)
expect(onStop).toHaveBeenCalledTimes(1)
})
})
})