Skip to content

Commit 592155b

Browse files
committed
feat: add keyboard shortcut for toggling auto-approve (Cmd/Ctrl+Alt+A)
1 parent 6a4dab0 commit 592155b

File tree

10 files changed

+111
-6
lines changed

10 files changed

+111
-6
lines changed

packages/types/src/vscode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const commandIds = [
5353
"focusInput",
5454
"acceptInput",
5555
"focusPanel",
56+
"toggleAutoApprove",
5657
] as const
5758

5859
export type CommandId = (typeof commandIds)[number]

src/activate/registerCommands.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,38 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
221221

222222
visibleProvider.postMessageToWebview({ type: "acceptInput" })
223223
},
224+
toggleAutoApprove: async () => {
225+
const visibleProvider = getVisibleProviderOrLog(outputChannel)
226+
227+
if (!visibleProvider) {
228+
// If no visible provider, try to get the global state directly
229+
const currentState = context.globalState.get<boolean>("autoApprovalEnabled") ?? false
230+
const newState = !currentState
231+
await context.globalState.update("autoApprovalEnabled", newState)
232+
233+
// Show notification even without visible provider
234+
const message = newState ? t("notification.autoApprove.enabled") : t("notification.autoApprove.disabled")
235+
vscode.window.showInformationMessage(message)
236+
return
237+
}
238+
239+
// Toggle the auto-approval state
240+
const currentState = visibleProvider.getValue("autoApprovalEnabled") ?? false
241+
const newState = !currentState
242+
243+
// Update the state
244+
await visibleProvider.setValue("autoApprovalEnabled", newState)
245+
246+
// Post message to webview to update UI
247+
visibleProvider.postMessageToWebview({
248+
type: "action",
249+
action: "toggleAutoApprove",
250+
})
251+
252+
// Show toast notification
253+
const message = newState ? t("notification.autoApprove.enabled") : t("notification.autoApprove.disabled")
254+
vscode.window.showInformationMessage(message)
255+
},
224256
})
225257

226258
export const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {

src/i18n/locales/en/common.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@
144144
"mode_exported": "Mode '{{mode}}' exported successfully",
145145
"mode_imported": "Mode imported successfully"
146146
},
147+
"notification": {
148+
"autoApprove": {
149+
"enabled": "Auto-approve enabled",
150+
"disabled": "Auto-approve disabled"
151+
}
152+
},
147153
"answers": {
148154
"yes": "Yes",
149155
"no": "No",

src/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@
174174
"command": "roo-cline.acceptInput",
175175
"title": "%command.acceptInput.title%",
176176
"category": "%configuration.title%"
177+
},
178+
{
179+
"command": "roo-cline.toggleAutoApprove",
180+
"title": "%command.toggleAutoApprove.title%",
181+
"category": "%configuration.title%"
177182
}
178183
],
179184
"menus": {
@@ -310,6 +315,13 @@
310315
"win": "ctrl+y",
311316
"linux": "ctrl+y",
312317
"when": "editorTextFocus && editorHasSelection"
318+
},
319+
{
320+
"command": "roo-cline.toggleAutoApprove",
321+
"key": "cmd+alt+a",
322+
"mac": "cmd+alt+a",
323+
"win": "ctrl+alt+a",
324+
"linux": "ctrl+alt+a"
313325
}
314326
],
315327
"submenus": [

src/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"command.terminal.fixCommand.title": "Fix This Command",
2727
"command.terminal.explainCommand.title": "Explain This Command",
2828
"command.acceptInput.title": "Accept Input/Suggestion",
29+
"command.toggleAutoApprove.title": "Toggle Auto-Approve",
2930
"configuration.title": "Roo Code",
3031
"commands.allowedCommands.description": "Commands that can be auto-executed when 'Always approve execute operations' is enabled",
3132
"commands.deniedCommands.description": "Command prefixes that will be automatically denied without asking for approval. In case of conflicts with allowed commands, the longest prefix match takes precedence. Add * to deny all commands.",

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export interface ExtensionMessage {
137137
| "didBecomeVisible"
138138
| "focusInput"
139139
| "switchTab"
140+
| "toggleAutoApprove"
140141
invoke?: "newChat" | "sendMessage" | "primaryButtonClick" | "secondaryButtonClick" | "setChatBoxMessage"
141142
state?: ExtensionState
142143
images?: string[]

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Popover, PopoverContent, PopoverTrigger, StandardTooltip, ToggleSwitch
1010
import { AutoApproveSetting, autoApproveSettingsConfig } from "../settings/AutoApproveToggle"
1111
import { useAutoApprovalToggles } from "@/hooks/useAutoApprovalToggles"
1212
import { useAutoApprovalState } from "@/hooks/useAutoApprovalState"
13+
import { getAutoApproveShortcut } from "@/utils/keyboard"
1314

1415
interface AutoApproveDropdownProps {
1516
disabled?: boolean
@@ -296,11 +297,13 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
296297
}
297298
handleAutoApprovalToggle()
298299
}}>
299-
<ToggleSwitch
300-
checked={effectiveAutoApprovalEnabled}
301-
aria-label="Toggle auto-approval"
302-
onChange={handleAutoApprovalToggle}
303-
/>
300+
<StandardTooltip content={`Toggle auto-approve ${getAutoApproveShortcut()}`}>
301+
<ToggleSwitch
302+
checked={effectiveAutoApprovalEnabled}
303+
aria-label="Toggle auto-approval"
304+
onChange={handleAutoApprovalToggle}
305+
/>
306+
</StandardTooltip>
304307
<span className={cn("text-sm font-bold select-none")}>Enabled</span>
305308
</label>
306309
</div>

webview-ui/src/components/settings/AutoApproveSettings.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { MaxLimitInputs } from "./MaxLimitInputs"
1414
import { useExtensionState } from "@/context/ExtensionStateContext"
1515
import { useAutoApprovalState } from "@/hooks/useAutoApprovalState"
1616
import { useAutoApprovalToggles } from "@/hooks/useAutoApprovalToggles"
17+
import { getAutoApproveShortcut } from "@/utils/keyboard"
1718

1819
type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
1920
alwaysAllowReadOnly?: boolean
@@ -135,7 +136,7 @@ export const AutoApproveSettings = ({
135136
<span className="font-medium">{t("settings:autoApprove.enabled")}</span>
136137
</VSCodeCheckbox>
137138
<div className="text-vscode-descriptionForeground text-sm mt-1">
138-
{t("settings:autoApprove.description")}
139+
{t("settings:autoApprove.description")} {getAutoApproveShortcut()}
139140
</div>
140141
</div>
141142

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,18 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
326326
}
327327
break
328328
}
329+
case "action": {
330+
if (message.action === "toggleAutoApprove") {
331+
// Toggle the auto-approval state
332+
setState((prevState) => {
333+
const newValue = !(prevState.autoApprovalEnabled ?? false)
334+
// Also send the update to the extension
335+
vscode.postMessage({ type: "autoApprovalEnabled", bool: newValue })
336+
return { ...prevState, autoApprovalEnabled: newValue }
337+
})
338+
}
339+
break
340+
}
329341
case "theme": {
330342
if (message.text) {
331343
setTheme(convertTextMateToHljs(JSON.parse(message.text)))

webview-ui/src/utils/keyboard.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Utility functions for keyboard shortcuts
3+
*/
4+
5+
/**
6+
* Detects if the current platform is macOS
7+
*/
8+
export const isMac = (): boolean => {
9+
// Check if we're in a browser environment
10+
if (typeof navigator !== "undefined" && navigator.platform) {
11+
return navigator.platform.toUpperCase().indexOf("MAC") >= 0
12+
}
13+
// Fallback for non-browser environments
14+
return false
15+
}
16+
17+
/**
18+
* Gets the platform-specific modifier key label
19+
*/
20+
export const getModifierKey = (): string => {
21+
return isMac() ? "Cmd" : "Ctrl"
22+
}
23+
24+
/**
25+
* Gets the platform-specific keyboard shortcut for toggling auto-approve
26+
*/
27+
export const getAutoApproveShortcut = (): string => {
28+
return isMac() ? "Cmd+Option+A" : "Ctrl+Alt+A"
29+
}
30+
31+
/**
32+
* Formats a keyboard shortcut for display
33+
*/
34+
export const formatShortcut = (shortcut: string): string => {
35+
return `(${shortcut})`
36+
}

0 commit comments

Comments
 (0)