mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: add manual approve/reject buttons for tool requests when auto-approval is disabled
- Created new ToolApprovalButtons component with Approve/Reject buttons - Integrated buttons into ChatRow for various tool types (file edits, diffs, commands, etc.) - Buttons only appear when auto-approval is disabled - Buttons send appropriate askResponse messages to backend - Hide buttons after user interaction to prevent duplicate responses Fixes #9739
This commit is contained in:
parent
906c6f0de4
commit
e3e4bc2251
2 changed files with 120 additions and 1 deletions
|
|
@ -29,6 +29,7 @@ import ErrorRow from "./ErrorRow"
|
|||
import McpResourceRow from "../mcp/McpResourceRow"
|
||||
|
||||
import { Mention } from "./Mention"
|
||||
import { ToolApprovalButtons } from "./ToolApprovalButtons"
|
||||
import { CheckpointSaved } from "./checkpoints/CheckpointSaved"
|
||||
import { FollowUpSuggest } from "./FollowUpSuggest"
|
||||
import { BatchFilePermission } from "./BatchFilePermission"
|
||||
|
|
@ -164,7 +165,15 @@ export const ChatRowContent = ({
|
|||
}: ChatRowContentProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { mcpServers, alwaysAllowMcp, currentCheckpoint, mode, apiConfiguration, clineMessages } = useExtensionState()
|
||||
const {
|
||||
mcpServers,
|
||||
alwaysAllowMcp,
|
||||
currentCheckpoint,
|
||||
mode,
|
||||
apiConfiguration,
|
||||
clineMessages,
|
||||
autoApprovalEnabled,
|
||||
} = useExtensionState()
|
||||
const { info: model } = useSelectedModel(apiConfiguration)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editedContent, setEditedContent] = useState("")
|
||||
|
|
@ -402,6 +411,13 @@ export const ChatRowContent = ({
|
|||
</span>
|
||||
</div>
|
||||
<BatchDiffApproval files={tool.batchDiffs} ts={message.ts} />
|
||||
{!autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons
|
||||
messageTs={message.ts}
|
||||
isProtected={false}
|
||||
disabled={isStreaming}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -438,6 +454,13 @@ export const ChatRowContent = ({
|
|||
diffStats={tool.diffStats}
|
||||
/>
|
||||
</div>
|
||||
{message.type === "ask" && !autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons
|
||||
messageTs={message.ts}
|
||||
isProtected={tool.isProtected}
|
||||
disabled={isStreaming}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case "insertContent":
|
||||
|
|
@ -476,6 +499,13 @@ export const ChatRowContent = ({
|
|||
diffStats={tool.diffStats}
|
||||
/>
|
||||
</div>
|
||||
{message.type === "ask" && !autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons
|
||||
messageTs={message.ts}
|
||||
isProtected={tool.isProtected}
|
||||
disabled={isStreaming}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case "searchAndReplace":
|
||||
|
|
@ -510,6 +540,13 @@ export const ChatRowContent = ({
|
|||
diffStats={tool.diffStats}
|
||||
/>
|
||||
</div>
|
||||
{message.type === "ask" && !autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons
|
||||
messageTs={message.ts}
|
||||
isProtected={tool.isProtected}
|
||||
disabled={isStreaming}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case "codebaseSearch": {
|
||||
|
|
@ -571,6 +608,13 @@ export const ChatRowContent = ({
|
|||
diffStats={tool.diffStats}
|
||||
/>
|
||||
</div>
|
||||
{message.type === "ask" && !autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons
|
||||
messageTs={message.ts}
|
||||
isProtected={tool.isProtected}
|
||||
disabled={isStreaming}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case "readFile":
|
||||
|
|
@ -593,6 +637,13 @@ export const ChatRowContent = ({
|
|||
}}
|
||||
ts={message?.ts}
|
||||
/>
|
||||
{!autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons
|
||||
messageTs={message.ts}
|
||||
isProtected={false}
|
||||
disabled={isStreaming}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -633,6 +684,9 @@ export const ChatRowContent = ({
|
|||
</ToolUseBlockHeader>
|
||||
</ToolUseBlock>
|
||||
</div>
|
||||
{message.type === "ask" && !autoApprovalEnabled && !message.partial && (
|
||||
<ToolApprovalButtons messageTs={message.ts} isProtected={false} disabled={isStreaming} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case "fetchInstructions":
|
||||
|
|
|
|||
65
webview-ui/src/components/chat/ToolApprovalButtons.tsx
Normal file
65
webview-ui/src/components/chat/ToolApprovalButtons.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import React, { useState } from "react"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import { Check, X } from "lucide-react"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
|
||||
interface ToolApprovalButtonsProps {
|
||||
messageTs: number
|
||||
isProtected?: boolean
|
||||
disabled?: boolean
|
||||
onResponse?: () => void
|
||||
}
|
||||
|
||||
export const ToolApprovalButtons: React.FC<ToolApprovalButtonsProps> = ({
|
||||
messageTs: _messageTs, // Prefixed with _ to indicate intentionally unused
|
||||
isProtected = false,
|
||||
disabled = false,
|
||||
onResponse,
|
||||
}) => {
|
||||
const [hasResponded, setHasResponded] = useState(false)
|
||||
|
||||
const handleApprove = () => {
|
||||
if (hasResponded) return
|
||||
setHasResponded(true)
|
||||
vscode.postMessage({
|
||||
type: "askResponse",
|
||||
askResponse: "yesButtonClicked",
|
||||
})
|
||||
onResponse?.()
|
||||
}
|
||||
|
||||
const handleReject = () => {
|
||||
if (hasResponded) return
|
||||
setHasResponded(true)
|
||||
vscode.postMessage({
|
||||
type: "askResponse",
|
||||
askResponse: "noButtonClicked",
|
||||
})
|
||||
onResponse?.()
|
||||
}
|
||||
|
||||
if (hasResponded) {
|
||||
return null // Hide buttons after response
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 mt-3 ml-6">
|
||||
<VSCodeButton
|
||||
appearance="primary"
|
||||
onClick={handleApprove}
|
||||
disabled={disabled}
|
||||
style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|
||||
<Check className="w-4 h-4" />
|
||||
{isProtected ? "Approve (Protected File)" : "Approve"}
|
||||
</VSCodeButton>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleReject}
|
||||
disabled={disabled}
|
||||
style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|
||||
<X className="w-4 h-4" />
|
||||
Reject
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue