feat: add keyboard shortcuts for auto-approve selection

- Add Alt+1 through Alt+0 keyboard shortcuts to toggle auto-approve options
- Display keyboard shortcuts in tooltips for each option
- Create AutoApproveKeyboardShortcuts component to handle global keyboard events
This commit is contained in:
Roo Code 2025-08-26 17:40:18 +00:00
parent e19492fb57
commit 67d9d14549
3 changed files with 164 additions and 24 deletions

View file

@ -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<string, AutoApproveSetting> = {
"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
}

View file

@ -22,6 +22,20 @@ type AutoApproveToggleDropdownProps = AutoApproveToggles & {
onToggle: (key: AutoApproveSetting, value: boolean) => void
}
// Keyboard shortcuts mapping
const KEYBOARD_SHORTCUTS: Record<AutoApproveSetting, string> = {
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]) => (
<StandardTooltip key={key} content={t(descriptionKey || "")}>
<button
onClick={() => onToggle(key, !props[key])}
aria-label={t(labelKey)}
aria-pressed={!!props[key]}
data-testid={testId}
className={cn(
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left",
"transition-colors hover:bg-vscode-list-hoverBackground",
props[key]
? "bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground"
: "opacity-70",
)}>
<span className={cn("codicon", `codicon-${icon}`, "text-sm flex-shrink-0")} />
<span className="flex-1 truncate">{t(labelKey)}</span>
<span
}: (typeof autoApproveSettingsConfig)[AutoApproveSetting]) => {
const tooltipContent = `${t(descriptionKey || "")} (${KEYBOARD_SHORTCUTS[key]})`
return (
<StandardTooltip key={key} content={tooltipContent}>
<button
onClick={() => onToggle(key, !props[key])}
aria-label={t(labelKey)}
aria-pressed={!!props[key]}
data-testid={testId}
className={cn(
"text-[10px] px-1 rounded",
props[key] ? "bg-vscode-badge-background text-vscode-badge-foreground" : "",
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left",
"transition-colors hover:bg-vscode-list-hoverBackground",
props[key]
? "bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground"
: "opacity-70",
)}>
{props[key] ? "✓" : ""}
</span>
</button>
</StandardTooltip>
)
<span className={cn("codicon", `codicon-${icon}`, "text-sm flex-shrink-0")} />
<span className="flex-1 truncate">{t(labelKey)}</span>
<span
className={cn(
"text-[10px] px-1 rounded",
props[key] ? "bg-vscode-badge-background text-vscode-badge-foreground" : "",
)}>
{props[key] ? "✓" : ""}
</span>
</button>
</StandardTooltip>
)
}
return (
<>

View file

@ -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<ChatViewRef, ChatViewPro
<div
data-testid="chat-view"
className={isHidden ? "hidden" : "fixed top-0 left-0 right-0 bottom-0 flex flex-col overflow-hidden"}>
<AutoApproveKeyboardShortcuts />
{(showAnnouncement || showAnnouncementModal) && (
<Announcement
hideAnnouncement={() => {