diff --git a/webview-ui/src/components/chat/AutoApproveKeyboardShortcuts.tsx b/webview-ui/src/components/chat/AutoApproveKeyboardShortcuts.tsx new file mode 100644 index 0000000000..ada19a8fe9 --- /dev/null +++ b/webview-ui/src/components/chat/AutoApproveKeyboardShortcuts.tsx @@ -0,0 +1,121 @@ +import { useEffect, useCallback, useMemo } from "react" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import { vscode } from "@src/utils/vscode" +import { AutoApproveSetting } from "../settings/AutoApproveToggle" +import { useAutoApprovalToggles } from "@src/hooks/useAutoApprovalToggles" + +// Keyboard shortcuts mapping for auto-approve options +const KEYBOARD_SHORTCUTS: Record = { + "1": "alwaysAllowReadOnly", + "2": "alwaysAllowWrite", + "3": "alwaysAllowBrowser", + "4": "alwaysAllowExecute", + "5": "alwaysAllowMcp", + "6": "alwaysAllowModeSwitch", + "7": "alwaysAllowSubtasks", + "8": "alwaysAllowFollowupQuestions", + "9": "alwaysAllowUpdateTodoList", + "0": "alwaysApproveResubmit", +} + +export const AutoApproveKeyboardShortcuts = () => { + const { + setAlwaysAllowReadOnly, + setAlwaysAllowWrite, + setAlwaysAllowExecute, + setAlwaysAllowBrowser, + setAlwaysAllowMcp, + setAlwaysAllowModeSwitch, + setAlwaysAllowSubtasks, + setAlwaysApproveResubmit, + setAlwaysAllowFollowupQuestions, + setAlwaysAllowUpdateTodoList, + alwaysApproveResubmit, + } = useExtensionState() + + const baseToggles = useAutoApprovalToggles() + const toggles = useMemo( + () => ({ + ...baseToggles, + alwaysApproveResubmit, + }), + [baseToggles, alwaysApproveResubmit], + ) + + const handleToggle = useCallback( + (key: AutoApproveSetting) => { + const currentValue = toggles[key] + const newValue = !currentValue + + // Send message to extension + vscode.postMessage({ type: key, bool: newValue }) + + // Update local state + switch (key) { + case "alwaysAllowReadOnly": + setAlwaysAllowReadOnly(newValue) + break + case "alwaysAllowWrite": + setAlwaysAllowWrite(newValue) + break + case "alwaysAllowExecute": + setAlwaysAllowExecute(newValue) + break + case "alwaysAllowBrowser": + setAlwaysAllowBrowser(newValue) + break + case "alwaysAllowMcp": + setAlwaysAllowMcp(newValue) + break + case "alwaysAllowModeSwitch": + setAlwaysAllowModeSwitch(newValue) + break + case "alwaysAllowSubtasks": + setAlwaysAllowSubtasks(newValue) + break + case "alwaysApproveResubmit": + setAlwaysApproveResubmit(newValue) + break + case "alwaysAllowFollowupQuestions": + setAlwaysAllowFollowupQuestions(newValue) + break + case "alwaysAllowUpdateTodoList": + setAlwaysAllowUpdateTodoList(newValue) + break + } + }, + [ + toggles, + setAlwaysAllowReadOnly, + setAlwaysAllowWrite, + setAlwaysAllowExecute, + setAlwaysAllowBrowser, + setAlwaysAllowMcp, + setAlwaysAllowModeSwitch, + setAlwaysAllowSubtasks, + setAlwaysApproveResubmit, + setAlwaysAllowFollowupQuestions, + setAlwaysAllowUpdateTodoList, + ], + ) + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + // Check if Alt/Option key is pressed along with a number key + if (event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { + const shortcut = KEYBOARD_SHORTCUTS[event.key] + if (shortcut) { + event.preventDefault() + handleToggle(shortcut) + } + } + } + + window.addEventListener("keydown", handleKeyDown) + return () => { + window.removeEventListener("keydown", handleKeyDown) + } + }, [handleToggle]) + + return null // This component doesn't render anything +} diff --git a/webview-ui/src/components/chat/AutoApproveToggleDropdown.tsx b/webview-ui/src/components/chat/AutoApproveToggleDropdown.tsx index 279194593a..4d13846350 100644 --- a/webview-ui/src/components/chat/AutoApproveToggleDropdown.tsx +++ b/webview-ui/src/components/chat/AutoApproveToggleDropdown.tsx @@ -22,6 +22,20 @@ type AutoApproveToggleDropdownProps = AutoApproveToggles & { onToggle: (key: AutoApproveSetting, value: boolean) => void } +// Keyboard shortcuts mapping +const KEYBOARD_SHORTCUTS: Record = { + alwaysAllowReadOnly: "Alt+1", + alwaysAllowWrite: "Alt+2", + alwaysAllowBrowser: "Alt+3", + alwaysAllowExecute: "Alt+4", + alwaysAllowMcp: "Alt+5", + alwaysAllowModeSwitch: "Alt+6", + alwaysAllowSubtasks: "Alt+7", + alwaysAllowFollowupQuestions: "Alt+8", + alwaysAllowUpdateTodoList: "Alt+9", + alwaysApproveResubmit: "Alt+0", +} + export const AutoApproveToggleDropdown = ({ onToggle, ...props }: AutoApproveToggleDropdownProps) => { const { t } = useAppTranslation() @@ -37,32 +51,35 @@ export const AutoApproveToggleDropdown = ({ onToggle, ...props }: AutoApproveTog labelKey, icon, testId, - }: (typeof autoApproveSettingsConfig)[AutoApproveSetting]) => ( - - - - ) + + {t(labelKey)} + + {props[key] ? "✓" : ""} + + + + ) + } return ( <> diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 3cd10138e8..53eb79bcd7 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -51,6 +51,7 @@ import ChatRow from "./ChatRow" import ChatTextArea from "./ChatTextArea" import TaskHeader from "./TaskHeader" import AutoApproveMenu from "./AutoApproveMenu" +import { AutoApproveKeyboardShortcuts } from "./AutoApproveKeyboardShortcuts" import SystemPromptWarning from "./SystemPromptWarning" import ProfileViolationWarning from "./ProfileViolationWarning" import { CheckpointWarning } from "./CheckpointWarning" @@ -1793,6 +1794,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction + {(showAnnouncement || showAnnouncementModal) && ( {