mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
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
This commit is contained in:
parent
5e218febdb
commit
f3a7a7d2af
6 changed files with 120 additions and 29 deletions
1
.tmp/Roo-Code
Submodule
1
.tmp/Roo-Code
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 86debeef43acbea9bdc1aa4b38d514541e164c91
|
||||
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 ?? []
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ export interface WebviewMessage {
|
|||
| "openFile"
|
||||
| "openMention"
|
||||
| "cancelTask"
|
||||
| "pauseTask"
|
||||
| "resumeTask"
|
||||
| "updateVSCodeSetting"
|
||||
| "getVSCodeSetting"
|
||||
| "vsCodeSetting"
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
ref,
|
||||
) => {
|
||||
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<ChatViewRef, ChatViewPro
|
|||
setEnableButtons(false)
|
||||
setPrimaryButtonText(undefined)
|
||||
setSecondaryButtonText(undefined)
|
||||
setIsPaused(false)
|
||||
}
|
||||
}, [messages.length])
|
||||
|
||||
|
|
@ -578,6 +580,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
setSelectedImages([])
|
||||
setClineAsk(undefined)
|
||||
setEnableButtons(false)
|
||||
setIsPaused(false)
|
||||
// Do not reset mode here as it should persist.
|
||||
// setPrimaryButtonText(undefined)
|
||||
// setSecondaryButtonText(undefined)
|
||||
|
|
@ -728,8 +731,14 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
const trimmedInput = text?.trim()
|
||||
|
||||
if (isStreaming) {
|
||||
vscode.postMessage({ type: "cancelTask" })
|
||||
setDidClickCancel(true)
|
||||
// If paused, resume; otherwise pause
|
||||
if (isPaused) {
|
||||
vscode.postMessage({ type: "resumeTask" })
|
||||
setIsPaused(false)
|
||||
} else {
|
||||
vscode.postMessage({ type: "pauseTask" })
|
||||
setIsPaused(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -767,7 +776,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
setClineAsk(undefined)
|
||||
setEnableButtons(false)
|
||||
},
|
||||
[clineAsk, startNewTask, isStreaming],
|
||||
[clineAsk, startNewTask, isStreaming, isPaused],
|
||||
)
|
||||
|
||||
const { info: model } = useSelectedModel(apiConfiguration)
|
||||
|
|
@ -1945,26 +1954,69 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
</StandardTooltip>
|
||||
)}
|
||||
{(secondaryButtonText || isStreaming) && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
isStreaming
|
||||
? t("chat:cancel.tooltip")
|
||||
: secondaryButtonText === t("chat:startNewTask.title")
|
||||
? t("chat:startNewTask.tooltip")
|
||||
: secondaryButtonText === t("chat:reject.title")
|
||||
? t("chat:reject.tooltip")
|
||||
: secondaryButtonText === t("chat:terminate.title")
|
||||
? t("chat:terminate.tooltip")
|
||||
: undefined
|
||||
}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!enableButtons && !(isStreaming && !didClickCancel)}
|
||||
className={isStreaming ? "flex-[2] ml-0" : "flex-1 ml-[6px]"}
|
||||
onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}>
|
||||
{isStreaming ? t("chat:cancel.title") : secondaryButtonText}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
<>
|
||||
{isStreaming && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
isPaused
|
||||
? t("chat:resume.tooltip", {
|
||||
defaultValue: "Resume the AI's response",
|
||||
})
|
||||
: t("chat:pause.tooltip", {
|
||||
defaultValue: "Pause the AI's response",
|
||||
})
|
||||
}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={didClickCancel}
|
||||
className="flex-1 mr-[6px]"
|
||||
onClick={() =>
|
||||
handleSecondaryButtonClick(inputValue, selectedImages)
|
||||
}>
|
||||
{isPaused
|
||||
? t("chat:resume.title", { defaultValue: "Resume" })
|
||||
: t("chat:pause.title", { defaultValue: "Pause" })}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
{isStreaming && (
|
||||
<StandardTooltip content={t("chat:cancel.tooltip")}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={didClickCancel}
|
||||
className="flex-1 ml-[6px]"
|
||||
onClick={() => {
|
||||
vscode.postMessage({ type: "cancelTask" })
|
||||
setDidClickCancel(true)
|
||||
setIsPaused(false)
|
||||
}}>
|
||||
{t("chat:cancel.title")}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
{!isStreaming && secondaryButtonText && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
secondaryButtonText === t("chat:startNewTask.title")
|
||||
? t("chat:startNewTask.tooltip")
|
||||
: secondaryButtonText === t("chat:reject.title")
|
||||
? t("chat:reject.tooltip")
|
||||
: secondaryButtonText === t("chat:terminate.title")
|
||||
? t("chat:terminate.tooltip")
|
||||
: undefined
|
||||
}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!enableButtons}
|
||||
className="flex-1 ml-[6px]"
|
||||
onClick={() =>
|
||||
handleSecondaryButtonClick(inputValue, selectedImages)
|
||||
}>
|
||||
{secondaryButtonText}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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..."
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue