feat: pause auto-approve while typing in chat input

- Auto-approve operations are now paused when there is text in the chat input box
- Added visual indicator showing "Auto-approve paused while typing" when paused
- Added tests to verify the pause/resume functionality
- Addresses user feedback about auto-approve continuing while typing hints

Fixes #8074
This commit is contained in:
Roo Code 2025-09-17 16:54:18 +00:00
parent 7b1e3a0ee5
commit 77768f836b
3 changed files with 175 additions and 0 deletions

View file

@ -1594,6 +1594,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
return
}
// Check if there's text in the input box - pause auto-approve if there is
const hasInputText = inputValueRef.current.trim().length > 0
if (hasInputText) {
// User is typing, don't auto-approve
return
}
const autoApproveOrReject = async () => {
// Check for auto-reject first (commands that should be denied)
if (lastMessage?.ask === "command" && isDeniedCommand(lastMessage)) {
@ -1703,6 +1710,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
isDeniedCommand,
getDeniedPrefix,
tSettings,
inputValue, // Add inputValue dependency to re-run when input changes
])
// Function to handle mode switching
@ -1898,6 +1906,16 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
? "opacity-100"
: "opacity-50"
}`}>
{/* Show auto-approve paused indicator when there's input text */}
{enableButtons &&
autoApprovalEnabled &&
hasEnabledOptions &&
inputValue.trim().length > 0 &&
!showScrollToBottom && (
<div className="flex-1 text-xs text-vscode-descriptionForeground opacity-75 text-center">
{t("chat:autoApprovePaused")}
</div>
)}
{showScrollToBottom ? (
<StandardTooltip content={t("chat:scrollToBottom")}>
<VSCodeButton

View file

@ -671,4 +671,160 @@ describe("ChatView - Auto Approval Tests", () => {
askResponse: "yesButtonClicked",
})
})
it("does not auto-approve when there is text in the input box", async () => {
const { container } = renderChatView()
// First hydrate state with initial task
mockPostMessage({
alwaysAllowReadOnly: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Simulate typing in the input box
const textarea = container.querySelector("textarea")
if (textarea) {
// Simulate user typing
Object.defineProperty(textarea, "value", {
writable: true,
value: "User is typing something...",
})
// Trigger input event
const inputEvent = new Event("input", { bubbles: true })
textarea.dispatchEvent(inputEvent)
}
// Then send the read tool ask message
mockPostMessage({
alwaysAllowReadOnly: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({ tool: "readFile", path: "test.txt" }),
partial: false,
},
],
})
// Wait a short time and verify no auto-approval message was sent
await new Promise((resolve) => setTimeout(resolve, 200))
expect(vscode.postMessage).not.toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
})
it("resumes auto-approve when input text is cleared", async () => {
const { container } = renderChatView()
// First hydrate state with initial task
mockPostMessage({
alwaysAllowReadOnly: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
],
})
// Simulate typing in the input box
const textarea = container.querySelector("textarea")
if (textarea) {
// First add text
Object.defineProperty(textarea, "value", {
writable: true,
value: "User is typing...",
})
const inputEvent = new Event("input", { bubbles: true })
textarea.dispatchEvent(inputEvent)
}
// Send a tool ask message while text is present
mockPostMessage({
alwaysAllowReadOnly: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now() - 100,
text: JSON.stringify({ tool: "readFile", path: "test1.txt" }),
partial: false,
},
],
})
// Wait and verify no auto-approval
await new Promise((resolve) => setTimeout(resolve, 100))
expect(vscode.postMessage).not.toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
// Now clear the input
if (textarea) {
Object.defineProperty(textarea, "value", {
writable: true,
value: "",
})
const clearEvent = new Event("input", { bubbles: true })
textarea.dispatchEvent(clearEvent)
}
// Send another tool ask message after clearing text
mockPostMessage({
alwaysAllowReadOnly: true,
autoApprovalEnabled: true,
clineMessages: [
{
type: "say",
say: "task",
ts: Date.now() - 2000,
text: "Initial task",
},
{
type: "ask",
ask: "tool",
ts: Date.now(),
text: JSON.stringify({ tool: "readFile", path: "test2.txt" }),
partial: false,
},
],
})
// Now it should auto-approve since input is cleared
await waitFor(() => {
expect(vscode.postMessage).toHaveBeenCalledWith({
type: "askResponse",
askResponse: "yesButtonClicked",
})
})
})
})

View file

@ -272,6 +272,7 @@
},
"autoApprove": {
"tooltipManage": "Manage auto-approval settings",
"autoApprovePaused": "Auto-approve paused while typing",
"tooltipStatus": "Auto-approval enabled for: {{toggles}}",
"title": "Auto-approve",
"all": "All",