diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index d358c68f1c..83509779a4 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -1594,6 +1594,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction 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 + {/* Show auto-approve paused indicator when there's input text */} + {enableButtons && + autoApprovalEnabled && + hasEnabledOptions && + inputValue.trim().length > 0 && + !showScrollToBottom && ( +
+ {t("chat:autoApprovePaused")} +
+ )} {showScrollToBottom ? ( { 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", + }) + }) + }) }) diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index d6fb807888..bb200993f4 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -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",