Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 91 additions & 36 deletions webview-ui/src/components/chat/AutoApproveMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { vscode } from "@src/utils/vscode"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { AutoApproveToggle, AutoApproveSetting, autoApproveSettingsConfig } from "../settings/AutoApproveToggle"
import { StandardTooltip } from "@src/components/ui"
import { useAutoApprovalState } from "@src/hooks/useAutoApprovalState"

interface AutoApproveMenuProps {
style?: React.CSSProperties
Expand Down Expand Up @@ -43,10 +45,40 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {

const { t } = useAppTranslation()

const toggles = useMemo(
() => ({
alwaysAllowReadOnly: alwaysAllowReadOnly,
alwaysAllowWrite: alwaysAllowWrite,
alwaysAllowExecute: alwaysAllowExecute,
alwaysAllowBrowser: alwaysAllowBrowser,
alwaysAllowMcp: alwaysAllowMcp,
alwaysAllowModeSwitch: alwaysAllowModeSwitch,
alwaysAllowSubtasks: alwaysAllowSubtasks,
alwaysApproveResubmit: alwaysApproveResubmit,
alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions,
alwaysAllowUpdateTodoList: alwaysAllowUpdateTodoList,
}),
[
alwaysAllowReadOnly,
alwaysAllowWrite,
alwaysAllowExecute,
alwaysAllowBrowser,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
alwaysApproveResubmit,
alwaysAllowFollowupQuestions,
alwaysAllowUpdateTodoList,
],
)

const { hasEnabledOptions, effectiveAutoApprovalEnabled } = useAutoApprovalState(toggles, autoApprovalEnabled)

const onAutoApproveToggle = useCallback(
(key: AutoApproveSetting, value: boolean) => {
vscode.postMessage({ type: key, bool: value })

// Update the specific toggle state
switch (key) {
case "alwaysAllowReadOnly":
setAlwaysAllowReadOnly(value)
Expand Down Expand Up @@ -79,8 +111,30 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysAllowUpdateTodoList(value)
break
}

// Check if we need to update the master auto-approval state
// Create a new toggles state with the updated value
const updatedToggles = {
...toggles,
[key]: value,
}

const willHaveEnabledOptions = Object.values(updatedToggles).some((v) => !!v)

// If enabling the first option, enable master auto-approval
if (value && !hasEnabledOptions && willHaveEnabledOptions) {
setAutoApprovalEnabled(true)
vscode.postMessage({ type: "autoApprovalEnabled", bool: true })
}
// If disabling the last option, disable master auto-approval
else if (!value && hasEnabledOptions && !willHaveEnabledOptions) {
setAutoApprovalEnabled(false)
vscode.postMessage({ type: "autoApprovalEnabled", bool: false })
}
},
[
toggles,
hasEnabledOptions,
setAlwaysAllowReadOnly,
setAlwaysAllowWrite,
setAlwaysAllowExecute,
Expand All @@ -91,43 +145,32 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysApproveResubmit,
setAlwaysAllowFollowupQuestions,
setAlwaysAllowUpdateTodoList,
setAutoApprovalEnabled,
],
)

const toggleExpanded = useCallback(() => setIsExpanded((prev) => !prev), [])
const toggleExpanded = useCallback(() => {
setIsExpanded((prev) => !prev)
}, [])

const toggles = useMemo(
() => ({
alwaysAllowReadOnly: alwaysAllowReadOnly,
alwaysAllowWrite: alwaysAllowWrite,
alwaysAllowExecute: alwaysAllowExecute,
alwaysAllowBrowser: alwaysAllowBrowser,
alwaysAllowMcp: alwaysAllowMcp,
alwaysAllowModeSwitch: alwaysAllowModeSwitch,
alwaysAllowSubtasks: alwaysAllowSubtasks,
alwaysApproveResubmit: alwaysApproveResubmit,
alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions,
alwaysAllowUpdateTodoList: alwaysAllowUpdateTodoList,
}),
[
alwaysAllowReadOnly,
alwaysAllowWrite,
alwaysAllowExecute,
alwaysAllowBrowser,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
alwaysApproveResubmit,
alwaysAllowFollowupQuestions,
alwaysAllowUpdateTodoList,
],
)
// Disable main checkbox while menu is open or no options selected
const isCheckboxDisabled = useMemo(() => {
return !hasEnabledOptions || isExpanded
}, [hasEnabledOptions, isExpanded])

const enabledActionsList = Object.entries(toggles)
.filter(([_key, value]) => !!value)
.map(([key]) => t(autoApproveSettingsConfig[key as AutoApproveSetting].labelKey))
.join(", ")

// Update displayed text logic
const displayText = useMemo(() => {
if (!effectiveAutoApprovalEnabled || !hasEnabledOptions) {
return t("chat:autoApprove.none")
}
return enabledActionsList || t("chat:autoApprove.none")
}, [effectiveAutoApprovalEnabled, hasEnabledOptions, enabledActionsList, t])

const handleOpenSettings = useCallback(
() =>
window.postMessage({ type: "action", action: "settingsButtonClicked", values: { section: "autoApprove" } }),
Expand Down Expand Up @@ -155,14 +198,26 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
}}
onClick={toggleExpanded}>
<div onClick={(e) => e.stopPropagation()}>
<VSCodeCheckbox
checked={autoApprovalEnabled ?? false}
onChange={() => {
const newValue = !(autoApprovalEnabled ?? false)
setAutoApprovalEnabled(newValue)
vscode.postMessage({ type: "autoApprovalEnabled", bool: newValue })
}}
/>
<StandardTooltip content={!hasEnabledOptions ? t("chat:autoApprove.selectOptionsFirst") : ""}>
<VSCodeCheckbox
checked={effectiveAutoApprovalEnabled}
disabled={isCheckboxDisabled}
aria-label={
hasEnabledOptions
? t("chat:autoApprove.toggleAriaLabel")
: t("chat:autoApprove.disabledAriaLabel")
}
onChange={() => {
if (!hasEnabledOptions) {
// Show a message or do nothing
return
}
const newValue = !(autoApprovalEnabled ?? false)
setAutoApprovalEnabled(newValue)
vscode.postMessage({ type: "autoApprovalEnabled", bool: newValue })
}}
/>
</StandardTooltip>
</div>
<div
style={{
Expand All @@ -188,7 +243,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
flex: 1,
minWidth: 0,
}}>
{enabledActionsList || t("chat:autoApprove.none")}
{displayText}
</span>
<span
className={`codicon codicon-chevron-${isExpanded ? "down" : "right"}`}
Expand Down
17 changes: 17 additions & 0 deletions webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -961,10 +961,27 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro

const isAutoApproved = useCallback(
(message: ClineMessage | undefined) => {
// First check if auto-approval is enabled AND we have at least one permission
if (!autoApprovalEnabled || !message || message.type !== "ask") {
return false
}

// Check if ANY auto-approve option is enabled
const hasAnyAutoApproveEnabled =
alwaysAllowReadOnly ||
alwaysAllowWrite ||
alwaysAllowBrowser ||
alwaysAllowExecute ||
alwaysAllowMcp ||
alwaysAllowModeSwitch ||
alwaysAllowSubtasks ||
alwaysAllowFollowupQuestions ||
alwaysAllowUpdateTodoList

if (!hasAnyAutoApproveEnabled) {
return false
}

if (message.ask === "followup") {
return alwaysAllowFollowupQuestions
}
Expand Down
Loading