From f3a7a7d2af4499c7372a3898580dd58a8a582dca Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 26 Sep 2025 16:52:34 +0000 Subject: [PATCH] feat: add pause/resume button for AI streaming responses - Add pauseTask and resumeTask message types to WebviewMessage interface - Implement pause/resume handlers in webviewMessageHandler - Add pauseTask() and resumeTask() methods to ClineProvider - Update ChatView UI to show Pause/Resume button alongside Cancel during streaming - Add localization strings for pause and resume buttons - Utilize existing Task.isPaused property for pause state management Fixes #8331 --- .tmp/Roo-Code | 1 + src/core/webview/ClineProvider.ts | 34 +++++-- src/core/webview/webviewMessageHandler.ts | 6 ++ src/shared/WebviewMessage.ts | 2 + webview-ui/src/components/chat/ChatView.tsx | 98 ++++++++++++++++----- webview-ui/src/i18n/locales/en/chat.json | 8 ++ 6 files changed, 120 insertions(+), 29 deletions(-) create mode 160000 .tmp/Roo-Code diff --git a/.tmp/Roo-Code b/.tmp/Roo-Code new file mode 160000 index 0000000000..86debeef43 --- /dev/null +++ b/.tmp/Roo-Code @@ -0,0 +1 @@ +Subproject commit 86debeef43acbea9bdc1aa4b38d514541e164c91 diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 119668d668..02f77c526d 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2650,12 +2650,34 @@ export class ClineProvider } } - public resumeTask(taskId: string): void { - // Use the existing showTaskWithId method which handles both current and - // historical tasks. - this.showTaskWithId(taskId).catch((error) => { - this.log(`Failed to resume task ${taskId}: ${error.message}`) - }) + public async pauseTask(): Promise { + const task = this.getCurrentTask() + if (!task) { + return + } + + this.log(`[pauseTask] pausing task ${task.taskId}`) + + // Set the task as paused + task.isPaused = true + + // Update the UI to reflect the paused state + await this.postStateToWebview() + } + + public async resumeTask(): Promise { + const task = this.getCurrentTask() + if (!task) { + return + } + + this.log(`[resumeTask] resuming task ${task.taskId}`) + + // Set the task as not paused + task.isPaused = false + + // Update the UI to reflect the resumed state + await this.postStateToWebview() } // Modes diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index c3e57c67a2..b6277995a9 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1003,6 +1003,12 @@ export const webviewMessageHandler = async ( case "cancelTask": await provider.cancelTask() break + case "pauseTask": + await provider.pauseTask() + break + case "resumeTask": + await provider.resumeTask() + break case "allowedCommands": { // Validate and sanitize the commands array const commands = message.commands ?? [] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d43a2fce04..59dc9770ce 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -76,6 +76,8 @@ export interface WebviewMessage { | "openFile" | "openMention" | "cancelTask" + | "pauseTask" + | "resumeTask" | "updateVSCodeSetting" | "getVSCodeSetting" | "vsCodeSetting" diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index d358c68f1c..5df28cf816 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -78,6 +78,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction { const isMountedRef = useRef(true) + const [isPaused, setIsPaused] = useState(false) const [audioBaseUri] = useState(() => { const w = window as any @@ -459,6 +460,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction )} {(secondaryButtonText || isStreaming) && ( - - handleSecondaryButtonClick(inputValue, selectedImages)}> - {isStreaming ? t("chat:cancel.title") : secondaryButtonText} - - + <> + {isStreaming && ( + + + handleSecondaryButtonClick(inputValue, selectedImages) + }> + {isPaused + ? t("chat:resume.title", { defaultValue: "Resume" }) + : t("chat:pause.title", { defaultValue: "Pause" })} + + + )} + {isStreaming && ( + + { + vscode.postMessage({ type: "cancelTask" }) + setDidClickCancel(true) + setIsPaused(false) + }}> + {t("chat:cancel.title")} + + + )} + {!isStreaming && secondaryButtonText && ( + + + handleSecondaryButtonClick(inputValue, selectedImages) + }> + {secondaryButtonText} + + + )} + )} )} diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 7bd0cde9d3..a03959f1ae 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -92,6 +92,14 @@ "title": "Cancel", "tooltip": "Cancel the current operation" }, + "pause": { + "title": "Pause", + "tooltip": "Pause the AI's response" + }, + "resume": { + "title": "Resume", + "tooltip": "Resume the AI's response" + }, "editMessage": { "placeholder": "Edit your message..." },