Skip to content

Commit 67d9d14

Browse files
committed
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
1 parent e19492f commit 67d9d14

File tree

3 files changed

+164
-24
lines changed

3 files changed

+164
-24
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { useEffect, useCallback, useMemo } from "react"
2+
import { useExtensionState } from "@src/context/ExtensionStateContext"
3+
import { vscode } from "@src/utils/vscode"
4+
import { AutoApproveSetting } from "../settings/AutoApproveToggle"
5+
import { useAutoApprovalToggles } from "@src/hooks/useAutoApprovalToggles"
6+
7+
// Keyboard shortcuts mapping for auto-approve options
8+
const KEYBOARD_SHORTCUTS: Record<string, AutoApproveSetting> = {
9+
"1": "alwaysAllowReadOnly",
10+
"2": "alwaysAllowWrite",
11+
"3": "alwaysAllowBrowser",
12+
"4": "alwaysAllowExecute",
13+
"5": "alwaysAllowMcp",
14+
"6": "alwaysAllowModeSwitch",
15+
"7": "alwaysAllowSubtasks",
16+
"8": "alwaysAllowFollowupQuestions",
17+
"9": "alwaysAllowUpdateTodoList",
18+
"0": "alwaysApproveResubmit",
19+
}
20+
21+
export const AutoApproveKeyboardShortcuts = () => {
22+
const {
23+
setAlwaysAllowReadOnly,
24+
setAlwaysAllowWrite,
25+
setAlwaysAllowExecute,
26+
setAlwaysAllowBrowser,
27+
setAlwaysAllowMcp,
28+
setAlwaysAllowModeSwitch,
29+
setAlwaysAllowSubtasks,
30+
setAlwaysApproveResubmit,
31+
setAlwaysAllowFollowupQuestions,
32+
setAlwaysAllowUpdateTodoList,
33+
alwaysApproveResubmit,
34+
} = useExtensionState()
35+
36+
const baseToggles = useAutoApprovalToggles()
37+
const toggles = useMemo(
38+
() => ({
39+
...baseToggles,
40+
alwaysApproveResubmit,
41+
}),
42+
[baseToggles, alwaysApproveResubmit],
43+
)
44+
45+
const handleToggle = useCallback(
46+
(key: AutoApproveSetting) => {
47+
const currentValue = toggles[key]
48+
const newValue = !currentValue
49+
50+
// Send message to extension
51+
vscode.postMessage({ type: key, bool: newValue })
52+
53+
// Update local state
54+
switch (key) {
55+
case "alwaysAllowReadOnly":
56+
setAlwaysAllowReadOnly(newValue)
57+
break
58+
case "alwaysAllowWrite":
59+
setAlwaysAllowWrite(newValue)
60+
break
61+
case "alwaysAllowExecute":
62+
setAlwaysAllowExecute(newValue)
63+
break
64+
case "alwaysAllowBrowser":
65+
setAlwaysAllowBrowser(newValue)
66+
break
67+
case "alwaysAllowMcp":
68+
setAlwaysAllowMcp(newValue)
69+
break
70+
case "alwaysAllowModeSwitch":
71+
setAlwaysAllowModeSwitch(newValue)
72+
break
73+
case "alwaysAllowSubtasks":
74+
setAlwaysAllowSubtasks(newValue)
75+
break
76+
case "alwaysApproveResubmit":
77+
setAlwaysApproveResubmit(newValue)
78+
break
79+
case "alwaysAllowFollowupQuestions":
80+
setAlwaysAllowFollowupQuestions(newValue)
81+
break
82+
case "alwaysAllowUpdateTodoList":
83+
setAlwaysAllowUpdateTodoList(newValue)
84+
break
85+
}
86+
},
87+
[
88+
toggles,
89+
setAlwaysAllowReadOnly,
90+
setAlwaysAllowWrite,
91+
setAlwaysAllowExecute,
92+
setAlwaysAllowBrowser,
93+
setAlwaysAllowMcp,
94+
setAlwaysAllowModeSwitch,
95+
setAlwaysAllowSubtasks,
96+
setAlwaysApproveResubmit,
97+
setAlwaysAllowFollowupQuestions,
98+
setAlwaysAllowUpdateTodoList,
99+
],
100+
)
101+
102+
useEffect(() => {
103+
const handleKeyDown = (event: KeyboardEvent) => {
104+
// Check if Alt/Option key is pressed along with a number key
105+
if (event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
106+
const shortcut = KEYBOARD_SHORTCUTS[event.key]
107+
if (shortcut) {
108+
event.preventDefault()
109+
handleToggle(shortcut)
110+
}
111+
}
112+
}
113+
114+
window.addEventListener("keydown", handleKeyDown)
115+
return () => {
116+
window.removeEventListener("keydown", handleKeyDown)
117+
}
118+
}, [handleToggle])
119+
120+
return null // This component doesn't render anything
121+
}

webview-ui/src/components/chat/AutoApproveToggleDropdown.tsx

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ type AutoApproveToggleDropdownProps = AutoApproveToggles & {
2222
onToggle: (key: AutoApproveSetting, value: boolean) => void
2323
}
2424

25+
// Keyboard shortcuts mapping
26+
const KEYBOARD_SHORTCUTS: Record<AutoApproveSetting, string> = {
27+
alwaysAllowReadOnly: "Alt+1",
28+
alwaysAllowWrite: "Alt+2",
29+
alwaysAllowBrowser: "Alt+3",
30+
alwaysAllowExecute: "Alt+4",
31+
alwaysAllowMcp: "Alt+5",
32+
alwaysAllowModeSwitch: "Alt+6",
33+
alwaysAllowSubtasks: "Alt+7",
34+
alwaysAllowFollowupQuestions: "Alt+8",
35+
alwaysAllowUpdateTodoList: "Alt+9",
36+
alwaysApproveResubmit: "Alt+0",
37+
}
38+
2539
export const AutoApproveToggleDropdown = ({ onToggle, ...props }: AutoApproveToggleDropdownProps) => {
2640
const { t } = useAppTranslation()
2741

@@ -37,32 +51,35 @@ export const AutoApproveToggleDropdown = ({ onToggle, ...props }: AutoApproveTog
3751
labelKey,
3852
icon,
3953
testId,
40-
}: (typeof autoApproveSettingsConfig)[AutoApproveSetting]) => (
41-
<StandardTooltip key={key} content={t(descriptionKey || "")}>
42-
<button
43-
onClick={() => onToggle(key, !props[key])}
44-
aria-label={t(labelKey)}
45-
aria-pressed={!!props[key]}
46-
data-testid={testId}
47-
className={cn(
48-
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left",
49-
"transition-colors hover:bg-vscode-list-hoverBackground",
50-
props[key]
51-
? "bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground"
52-
: "opacity-70",
53-
)}>
54-
<span className={cn("codicon", `codicon-${icon}`, "text-sm flex-shrink-0")} />
55-
<span className="flex-1 truncate">{t(labelKey)}</span>
56-
<span
54+
}: (typeof autoApproveSettingsConfig)[AutoApproveSetting]) => {
55+
const tooltipContent = `${t(descriptionKey || "")} (${KEYBOARD_SHORTCUTS[key]})`
56+
return (
57+
<StandardTooltip key={key} content={tooltipContent}>
58+
<button
59+
onClick={() => onToggle(key, !props[key])}
60+
aria-label={t(labelKey)}
61+
aria-pressed={!!props[key]}
62+
data-testid={testId}
5763
className={cn(
58-
"text-[10px] px-1 rounded",
59-
props[key] ? "bg-vscode-badge-background text-vscode-badge-foreground" : "",
64+
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left",
65+
"transition-colors hover:bg-vscode-list-hoverBackground",
66+
props[key]
67+
? "bg-vscode-list-activeSelectionBackground text-vscode-list-activeSelectionForeground"
68+
: "opacity-70",
6069
)}>
61-
{props[key] ? "✓" : ""}
62-
</span>
63-
</button>
64-
</StandardTooltip>
65-
)
70+
<span className={cn("codicon", `codicon-${icon}`, "text-sm flex-shrink-0")} />
71+
<span className="flex-1 truncate">{t(labelKey)}</span>
72+
<span
73+
className={cn(
74+
"text-[10px] px-1 rounded",
75+
props[key] ? "bg-vscode-badge-background text-vscode-badge-foreground" : "",
76+
)}>
77+
{props[key] ? "✓" : ""}
78+
</span>
79+
</button>
80+
</StandardTooltip>
81+
)
82+
}
6683

6784
return (
6885
<>

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import ChatRow from "./ChatRow"
5151
import ChatTextArea from "./ChatTextArea"
5252
import TaskHeader from "./TaskHeader"
5353
import AutoApproveMenu from "./AutoApproveMenu"
54+
import { AutoApproveKeyboardShortcuts } from "./AutoApproveKeyboardShortcuts"
5455
import SystemPromptWarning from "./SystemPromptWarning"
5556
import ProfileViolationWarning from "./ProfileViolationWarning"
5657
import { CheckpointWarning } from "./CheckpointWarning"
@@ -1793,6 +1794,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
17931794
<div
17941795
data-testid="chat-view"
17951796
className={isHidden ? "hidden" : "fixed top-0 left-0 right-0 bottom-0 flex flex-col overflow-hidden"}>
1797+
<AutoApproveKeyboardShortcuts />
17961798
{(showAnnouncement || showAnnouncementModal) && (
17971799
<Announcement
17981800
hideAnnouncement={() => {

0 commit comments

Comments
 (0)