11import React from "react"
2- import { ListChecks , LayoutList , Settings , CheckCheck } from "lucide-react"
2+ import { ListChecks , LayoutList , Settings , CheckCheck , X } from "lucide-react"
33
44import { vscode } from "@/utils/vscode"
55import { cn } from "@/lib/utils"
66import { useExtensionState } from "@/context/ExtensionStateContext"
77import { useAppTranslation } from "@/i18n/TranslationContext"
88import { useRooPortal } from "@/components/ui/hooks/useRooPortal"
9- import { Popover , PopoverContent , PopoverTrigger , StandardTooltip } from "@/components/ui"
9+ import { Popover , PopoverContent , PopoverTrigger , StandardTooltip , ToggleSwitch } from "@/components/ui"
1010import { AutoApproveSetting , autoApproveSettingsConfig } from "../settings/AutoApproveToggle"
1111import { useAutoApprovalToggles } from "@/hooks/useAutoApprovalToggles"
12+ import { useAutoApprovalState } from "@/hooks/useAutoApprovalState"
1213
1314interface AutoApproveDropdownProps {
1415 disabled ?: boolean
@@ -124,20 +125,24 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
124125 Object . keys ( autoApproveSettingsConfig ) . forEach ( ( key ) => {
125126 onAutoApproveToggle ( key as AutoApproveSetting , false )
126127 } )
127- // Disable master auto-approval
128- if ( autoApprovalEnabled ) {
129- setAutoApprovalEnabled ( false )
130- vscode . postMessage ( { type : "autoApprovalEnabled" , bool : false } )
131- }
132- } , [ onAutoApproveToggle , autoApprovalEnabled , setAutoApprovalEnabled ] )
128+ } , [ onAutoApproveToggle ] )
133129
134130 const handleOpenSettings = React . useCallback (
135131 ( ) =>
136132 window . postMessage ( { type : "action" , action : "settingsButtonClicked" , values : { section : "autoApprove" } } ) ,
137133 [ ] ,
138134 )
139135
136+ // Handle the main auto-approval toggle
137+ const handleAutoApprovalToggle = React . useCallback ( ( ) => {
138+ const newValue = ! ( autoApprovalEnabled ?? false )
139+ setAutoApprovalEnabled ( newValue )
140+ vscode . postMessage ( { type : "autoApprovalEnabled" , bool : newValue } )
141+ } , [ autoApprovalEnabled , setAutoApprovalEnabled ] )
142+
140143 // Calculate enabled and total counts as separate properties
144+ const settingsArray = Object . values ( autoApproveSettingsConfig )
145+
141146 const enabledCount = React . useMemo ( ( ) => {
142147 return Object . values ( toggles ) . filter ( ( value ) => ! ! value ) . length
143148 } , [ toggles ] )
@@ -146,11 +151,7 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
146151 return Object . keys ( toggles ) . length
147152 } , [ toggles ] )
148153
149- // Split settings into two columns
150- const settingsArray = Object . values ( autoApproveSettingsConfig )
151- const halfLength = Math . ceil ( settingsArray . length / 2 )
152- const firstColumn = settingsArray . slice ( 0 , halfLength )
153- const secondColumn = settingsArray . slice ( halfLength )
154+ const { effectiveAutoApprovalEnabled } = useAutoApprovalState ( toggles , autoApprovalEnabled )
154155
155156 return (
156157 < Popover open = { open } onOpenChange = { setOpen } data-testid = "auto-approve-dropdown-root" >
@@ -167,19 +168,26 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
167168 : "opacity-90 hover:opacity-100 hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)] cursor-pointer" ,
168169 triggerClassName ,
169170 ) } >
170- < CheckCheck className = "size-3 flex-shrink-0" />
171+ { ! effectiveAutoApprovalEnabled ? (
172+ < X className = "size-3 flex-shrink-0" />
173+ ) : (
174+ < CheckCheck className = "size-3 flex-shrink-0" />
175+ ) }
176+
171177 < span className = "truncate min-w-0" >
172- { enabledCount === totalCount
173- ? t ( "chat:autoApprove.triggerLabelAll" )
174- : t ( "chat:autoApprove.triggerLabel" , { count : enabledCount } ) }
178+ { ! effectiveAutoApprovalEnabled
179+ ? t ( "chat:autoApprove.triggerLabelOff" )
180+ : enabledCount === totalCount
181+ ? t ( "chat:autoApprove.triggerLabelAll" )
182+ : t ( "chat:autoApprove.triggerLabel" , { count : enabledCount } ) }
175183 </ span >
176184 </ PopoverTrigger >
177185 </ StandardTooltip >
178186 < PopoverContent
179187 align = "start"
180188 sideOffset = { 4 }
181189 container = { portalContainer }
182- className = "p-0 overflow-hidden min-w-96 max-w-9/10"
190+ className = "p-0 overflow-hidden min-w-90 max-w-9/10"
183191 onOpenAutoFocus = { ( e ) => e . preventDefault ( ) } >
184192 < div className = "flex flex-col w-full" >
185193 { /* Header with description */ }
@@ -197,66 +205,32 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
197205 { t ( "chat:autoApprove.description" ) }
198206 </ p >
199207 </ div >
200-
201- { /* Two-column layout for approval options */ }
202- < div className = "p-3" >
203- < div className = "grid grid-cols-2 gap-x-4 gap-y-2" >
204- { /* First Column */ }
205- < div className = "space-y-2" >
206- { firstColumn . map ( ( { key, labelKey, descriptionKey, icon } ) => {
207- const isEnabled = toggles [ key ]
208- return (
209- < StandardTooltip key = { key } content = { t ( descriptionKey ) } >
210- < button
211- onClick = { ( ) => onAutoApproveToggle ( key , ! isEnabled ) }
212- className = { cn (
213- "w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left" ,
214- "transition-all duration-150" ,
215- "hover:bg-vscode-list-hoverBackground" ,
216- isEnabled
217- ? "bg-vscode-button-background text-vscode-button-foreground"
218- : "bg-transparent text-vscode-foreground opacity-70 hover:opacity-100" ,
219- ) }
220- data-testid = { `auto-approve-${ key } ` } >
221- < span className = { `codicon codicon-${ icon } text-sm flex-shrink-0` } />
222- < span className = "flex-1 truncate" > { t ( labelKey ) } </ span >
223- { isEnabled && (
224- < span className = "codicon codicon-check text-xs flex-shrink-0" />
225- ) }
226- </ button >
227- </ StandardTooltip >
228- )
229- } ) }
230- </ div >
231-
232- { /* Second Column */ }
233- < div className = "space-y-2" >
234- { secondColumn . map ( ( { key, labelKey, descriptionKey, icon } ) => {
235- const isEnabled = toggles [ key ]
236- return (
237- < StandardTooltip key = { key } content = { t ( descriptionKey ) } >
238- < button
239- onClick = { ( ) => onAutoApproveToggle ( key , ! isEnabled ) }
240- className = { cn (
241- "w-full flex items-center gap-2 px-2 py-1.5 rounded text-xs text-left" ,
242- "transition-all duration-150" ,
243- "hover:bg-vscode-list-hoverBackground" ,
244- isEnabled
245- ? "bg-vscode-button-background text-vscode-button-foreground"
246- : "bg-transparent text-vscode-foreground opacity-70 hover:opacity-100" ,
247- ) }
248- data-testid = { `auto-approve-${ key } ` } >
249- < span className = { `codicon codicon-${ icon } text-sm flex-shrink-0` } />
250- < span className = "flex-1 truncate" > { t ( labelKey ) } </ span >
251- { isEnabled && (
252- < span className = "codicon codicon-check text-xs flex-shrink-0" />
253- ) }
254- </ button >
255- </ StandardTooltip >
256- )
257- } ) }
258- </ div >
259- </ div >
208+ < div className = "grid grid-cols-2 gap-x-2 gap-y-2 p-3" >
209+ { settingsArray . map ( ( { key, labelKey, descriptionKey, icon } ) => {
210+ const isEnabled = toggles [ key ]
211+ return (
212+ < StandardTooltip key = { key } content = { t ( descriptionKey ) } >
213+ < button
214+ onClick = { ( ) => onAutoApproveToggle ( key , ! isEnabled ) }
215+ className = { cn (
216+ "flex items-center gap-2 px-2 py-2 rounded text-sm text-left" ,
217+ "transition-all duration-150" ,
218+ "opacity-100 hover:opacity-70" ,
219+ "cursor-pointer" ,
220+ ! effectiveAutoApprovalEnabled &&
221+ "opacity-50 cursor-not-allowed hover:opacity-50" ,
222+ isEnabled
223+ ? "bg-vscode-button-background text-vscode-button-foreground"
224+ : "bg-vscode-button-background/15 text-vscode-foreground hover:bg-vscode-list-hoverBackground" ,
225+ ) }
226+ disabled = { ! effectiveAutoApprovalEnabled }
227+ data-testid = { `auto-approve-${ key } ` } >
228+ < span className = { `codicon codicon-${ icon } text-sm flex-shrink-0` } />
229+ < span className = "flex-1 truncate" > { t ( labelKey ) } </ span >
230+ </ button >
231+ </ StandardTooltip >
232+ )
233+ } ) }
260234 </ div >
261235
262236 { /* Bottom bar with Select All/None buttons */ }
@@ -265,6 +239,7 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
265239 < button
266240 aria-label = { t ( "chat:autoApprove.selectAll" ) }
267241 onClick = { handleSelectAll }
242+ disabled = { ! effectiveAutoApprovalEnabled }
268243 className = { cn (
269244 "relative inline-flex items-center justify-center gap-1" ,
270245 "bg-transparent border-none px-2 py-1" ,
@@ -275,13 +250,15 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
275250 "focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder" ,
276251 "active:bg-[rgba(255,255,255,0.1)]" ,
277252 "cursor-pointer" ,
253+ ! effectiveAutoApprovalEnabled && "opacity-50 hover:opacity-50 cursor-not-allowed" ,
278254 ) } >
279255 < ListChecks className = "w-3.5 h-3.5" />
280256 < span > { t ( "chat:autoApprove.all" ) } </ span >
281257 </ button >
282258 < button
283259 aria-label = { t ( "chat:autoApprove.selectNone" ) }
284260 onClick = { handleSelectNone }
261+ disabled = { ! effectiveAutoApprovalEnabled }
285262 className = { cn (
286263 "relative inline-flex items-center justify-center gap-1" ,
287264 "bg-transparent border-none px-2 py-1" ,
@@ -292,11 +269,30 @@ export const AutoApproveDropdown = ({ disabled = false, triggerClassName = "" }:
292269 "focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder" ,
293270 "active:bg-[rgba(255,255,255,0.1)]" ,
294271 "cursor-pointer" ,
272+ ! effectiveAutoApprovalEnabled && "opacity-50 hover:opacity-50 cursor-not-allowed" ,
295273 ) } >
296274 < LayoutList className = "w-3.5 h-3.5" />
297275 < span > { t ( "chat:autoApprove.none" ) } </ span >
298276 </ button >
299277 </ div >
278+
279+ < label
280+ className = "flex items-center gap-2 pr-2 cursor-pointer"
281+ onClick = { ( e ) => {
282+ // Prevent label click when clicking on the toggle switch itself
283+ if ( ( e . target as HTMLElement ) . closest ( '[role="switch"]' ) ) {
284+ e . preventDefault ( )
285+ return
286+ }
287+ handleAutoApprovalToggle ( )
288+ } } >
289+ < ToggleSwitch
290+ checked = { effectiveAutoApprovalEnabled }
291+ aria-label = "Toggle auto-approval"
292+ onChange = { handleAutoApprovalToggle }
293+ />
294+ < span className = { cn ( "text-sm font-bold select-none" ) } > Enabled</ span >
295+ </ label >
300296 </ div >
301297 </ div >
302298 </ PopoverContent >
0 commit comments