@@ -12,14 +12,6 @@ import {
1212 Badge ,
1313 Separator ,
1414 StandardTooltip ,
15- AlertDialog ,
16- AlertDialogContent ,
17- AlertDialogHeader ,
18- AlertDialogTitle ,
19- AlertDialogDescription ,
20- AlertDialogFooter ,
21- AlertDialogCancel ,
22- AlertDialogAction ,
2315} from "@src/components/ui"
2416import { cn } from "@/lib/utils"
2517import type { ModeConfig } from "@roo-code/types"
@@ -60,13 +52,6 @@ interface ModeEnableDisableDialogProps {
6052 onSave : ( updatedModes : ModeWithSource [ ] ) => void
6153}
6254
63- interface DeleteState {
64- open : boolean
65- // action: 'delete' | 'restore' - determines dialog wording
66- action ?: "delete" | "restore"
67- tMode ?: { slug : string ; name : string ; source ?: string ; rulesFolderPath ?: string } | null
68- }
69-
7055interface GroupedModes {
7156 builtin : ModeWithSource [ ]
7257 global : ModeWithSource [ ]
@@ -102,10 +87,17 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
10287} ) => {
10388 const [ localModes , setLocalModes ] = useState < ModeWithSource [ ] > ( modes )
10489 const [ hasChanges , setHasChanges ] = useState ( false )
90+ const [ confirmOpen , setConfirmOpen ] = useState ( false )
91+ const [ pendingAction , setPendingAction ] = useState <
92+ | { type : "mode" ; payload : string }
93+ | { type : "source" ; payload : { source : ModeSource ; enable : boolean } }
94+ | { type : "all" ; payload : { enable : boolean } }
95+ | null
96+ > ( null )
10597
106- const { t } = useAppTranslation ( )
98+ useAppTranslation ( )
10799
108- const [ deleteState , setDeleteState ] = useState < DeleteState > ( { open : false , tMode : null } )
100+ // Delete handled in the mode settings window; no delete UI in this dialog.
109101
110102 // Update local state when props change
111103 useEffect ( ( ) => {
@@ -134,32 +126,69 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
134126 } , [ localModes ] )
135127
136128 // Toggle a single mode's disabled state
137- const toggleMode = ( slug : string ) => {
129+ const doToggleModeImmediate = ( slug : string ) => {
138130 setLocalModes ( ( prev ) => {
139131 const updated = prev . map ( ( mode ) => ( mode . slug === slug ? { ...mode , disabled : ! mode . disabled } : mode ) )
140132 return updated
141133 } )
142134 setHasChanges ( true )
143135 }
144136
145- // Toggle all modes in a source group
146- const toggleSourceGroup = ( source : ModeSource , enable : boolean ) => {
137+ const attemptToggleMode = ( slug : string ) => {
138+ const mode = localModes . find ( ( m ) => m . slug === slug )
139+ if ( ! mode ) return
140+ // If disabling a builtin that is currently enabled, show confirmation
141+ if ( mode . source === "builtin" && ! mode . disabled ) {
142+ setPendingAction ( { type : "mode" , payload : slug } )
143+ setConfirmOpen ( true )
144+ return
145+ }
146+ doToggleModeImmediate ( slug )
147+ }
148+
149+ // Toggle all modes in a source group (with confirmation for builtin disables)
150+ const doToggleSourceGroupImmediate = ( source : ModeSource , enable : boolean ) => {
147151 setLocalModes ( ( prev ) => {
148152 const updated = prev . map ( ( mode ) => ( mode . source === source ? { ...mode , disabled : ! enable } : mode ) )
149153 return updated
150154 } )
151155 setHasChanges ( true )
152156 }
153157
154- // Enable/disable all modes
155- const toggleAllModes = ( enable : boolean ) => {
158+ const attemptToggleSourceGroup = ( source : ModeSource , enable : boolean ) => {
159+ if ( source === "builtin" && enable === false ) {
160+ // find builtin slugs that would be disabled
161+ const toDisable = localModes . filter ( ( m ) => m . source === "builtin" && ! m . disabled ) . map ( ( m ) => m . slug )
162+ if ( toDisable . length > 0 ) {
163+ setPendingAction ( { type : "source" , payload : { source, enable } } )
164+ setConfirmOpen ( true )
165+ return
166+ }
167+ }
168+ doToggleSourceGroupImmediate ( source , enable )
169+ }
170+
171+ // Enable/disable all modes (with confirmation for builtin disables)
172+ const doToggleAllModesImmediate = ( enable : boolean ) => {
156173 setLocalModes ( ( prev ) => {
157174 const updated = prev . map ( ( mode ) => ( { ...mode , disabled : ! enable } ) )
158175 return updated
159176 } )
160177 setHasChanges ( true )
161178 }
162179
180+ const attemptToggleAllModes = ( enable : boolean ) => {
181+ if ( enable === false ) {
182+ const toDisable = localModes . filter ( ( m ) => m . source === "builtin" && ! m . disabled ) . map ( ( m ) => m . slug )
183+ if ( toDisable . length > 0 ) {
184+ setPendingAction ( { type : "all" , payload : { enable } } )
185+ setConfirmOpen ( true )
186+ return
187+ }
188+ }
189+ doToggleAllModesImmediate ( enable )
190+ }
191+
163192 // Handle save
164193 const handleSave = ( ) => {
165194 onSave ( localModes )
@@ -184,7 +213,7 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
184213 < div className = "flex items-center gap-3 flex-1 min-w-0" >
185214 < Checkbox
186215 checked = { ! mode . disabled }
187- onCheckedChange = { ( ) => toggleMode ( mode . slug ) }
216+ onCheckedChange = { ( ) => attemptToggleMode ( mode . slug ) }
188217 className = "flex-shrink-0"
189218 />
190219 < div className = "flex-1 min-w-0" >
@@ -212,12 +241,6 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
212241 size = "icon"
213242 onClick = { ( ) => {
214243 // Ask the extension to check for rules folder and return path via message
215- setDeleteState ( {
216- open : false ,
217- action : "delete" ,
218- tMode : { slug : mode . slug , name : mode . name , source : mode . source } ,
219- } )
220- // Request checkOnly first
221244 window . parent . postMessage (
222245 { type : "deleteCustomMode" , slug : mode . slug , checkOnly : true } ,
223246 "*" ,
@@ -232,12 +255,6 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
232255 size = "icon"
233256 onClick = { ( ) => {
234257 // Ask the extension to check for rules folder and return path via message
235- setDeleteState ( {
236- open : false ,
237- action : "restore" ,
238- tMode : { slug : mode . slug , name : mode . name , source : mode . source } ,
239- } )
240- // Request checkOnly first - reuse deleteCustomMode flow but interpret as restore in dialog
241258 window . parent . postMessage (
242259 { type : "deleteCustomMode" , slug : mode . slug , checkOnly : true } ,
243260 "*" ,
@@ -252,32 +269,7 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
252269 </ div >
253270 )
254271
255- // Listen for delete check responses from extension
256- useEffect ( ( ) => {
257- const handler = ( e : MessageEvent ) => {
258- const message = e . data
259- if ( message . type === "deleteCustomModeCheck" ) {
260- if ( message . slug && deleteState . tMode && deleteState . tMode . slug === message . slug ) {
261- // Preserve action (delete vs restore) when opening the confirmation dialog
262- setDeleteState ( ( prev ) => ( {
263- ...prev ,
264- open : true ,
265- tMode : { ...prev . tMode ! , rulesFolderPath : message . rulesFolderPath } ,
266- } ) )
267- }
268- }
269- }
270- window . addEventListener ( "message" , handler )
271- return ( ) => window . removeEventListener ( "message" , handler )
272- } , [ deleteState . tMode ] )
273-
274- const confirmDelete = ( ) => {
275- if ( ! deleteState . tMode ) return
276- window . parent . postMessage ( { type : "deleteCustomMode" , slug : deleteState . tMode . slug } , "*" )
277- setDeleteState ( { open : false , tMode : null } )
278- // Close dialog after request; backend will refresh state
279- onOpenChange ( false )
280- }
272+ // No delete handlers here — deletion lives in the dedicated mode settings UI.
281273
282274 // Source group component
283275 const SourceGroup : React . FC < { source : ModeSource ; modes : ModeWithSource [ ] } > = ( { source, modes } ) => {
@@ -307,15 +299,15 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
307299 < Button
308300 variant = "outline"
309301 size = "sm"
310- onClick = { ( ) => toggleSourceGroup ( source , true ) }
302+ onClick = { ( ) => attemptToggleSourceGroup ( source , true ) }
311303 disabled = { allEnabled }
312304 className = "enable-disable-button text-xs h-7 px-2" >
313305 Enable All
314306 </ Button >
315307 < Button
316308 variant = "outline"
317309 size = "sm"
318- onClick = { ( ) => toggleSourceGroup ( source , false ) }
310+ onClick = { ( ) => attemptToggleSourceGroup ( source , false ) }
319311 disabled = { noneEnabled }
320312 className = "enable-disable-button text-xs h-7 px-2" >
321313 Disable All
@@ -362,7 +354,7 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
362354 < Button
363355 variant = "outline"
364356 size = "sm"
365- onClick = { ( ) => toggleAllModes ( true ) }
357+ onClick = { ( ) => attemptToggleAllModes ( true ) }
366358 disabled = { stats . enabled === stats . total }
367359 className = "enable-disable-button text-xs" >
368360 < Check className = "size-3 mr-1" />
@@ -371,7 +363,7 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
371363 < Button
372364 variant = "outline"
373365 size = "sm"
374- onClick = { ( ) => toggleAllModes ( false ) }
366+ onClick = { ( ) => attemptToggleAllModes ( false ) }
375367 disabled = { stats . disabled === stats . total }
376368 className = "enable-disable-button text-xs" >
377369 < X className = "size-3 mr-1" />
@@ -397,6 +389,51 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
397389 ) }
398390 </ div >
399391
392+ { /* Confirmation dialog shown when disabling built-in modes */ }
393+ { confirmOpen && (
394+ < div className = "fixed inset-0 z-50 flex items-center justify-center bg-black/40" >
395+ < div className = "bg-white p-6 rounded-lg max-w-lg w-full" >
396+ < h3 className = "text-lg font-semibold" > Disabling built-in mode(s)</ h3 >
397+ < p className = "text-sm text-gray-700 mt-2" >
398+ Disabling a built-in mode will copy it to your custom_model.yaml so you can modify or
399+ delete the custom copy. You can restore the original built-in mode later by deleting the
400+ custom mode in Mode Settings.
401+ </ p >
402+ < div className = "mt-4 flex justify-end gap-2" >
403+ < Button
404+ variant = "outline"
405+ onClick = { ( ) => {
406+ // Cancel confirmation
407+ setConfirmOpen ( false )
408+ setPendingAction ( null )
409+ } } >
410+ Cancel
411+ </ Button >
412+ < Button
413+ onClick = { ( ) => {
414+ // Proceed with pending action
415+ if ( pendingAction ) {
416+ if ( pendingAction . type === "mode" ) {
417+ doToggleModeImmediate ( pendingAction . payload )
418+ } else if ( pendingAction . type === "source" ) {
419+ doToggleSourceGroupImmediate (
420+ pendingAction . payload . source ,
421+ pendingAction . payload . enable ,
422+ )
423+ } else if ( pendingAction . type === "all" ) {
424+ doToggleAllModesImmediate ( pendingAction . payload . enable )
425+ }
426+ }
427+ setConfirmOpen ( false )
428+ setPendingAction ( null )
429+ } } >
430+ Proceed
431+ </ Button >
432+ </ div >
433+ </ div >
434+ </ div >
435+ ) }
436+
400437 < DialogFooter className = "flex items-center justify-between" >
401438 < div className = "text-xs text-gray-500" > { hasChanges && "You have unsaved changes" } </ div >
402439 < div className = "flex items-center gap-2" >
@@ -409,56 +446,7 @@ export const ModeEnableDisableDialog: React.FC<ModeEnableDisableDialogProps> = (
409446 </ div >
410447 </ DialogFooter >
411448
412- { /* Delete confirmation dialog for global custom modes */ }
413- < AlertDialog open = { ! ! deleteState . open } onOpenChange = { ( open ) => setDeleteState ( ( s ) => ( { ...s , open } ) ) } >
414- < AlertDialogContent >
415- < AlertDialogHeader >
416- < AlertDialogTitle >
417- { deleteState . action === "restore"
418- ? t
419- ? t ( "prompts:restoreMode.title" )
420- : "Restore built-in mode"
421- : t
422- ? t ( "prompts:deleteMode.title" )
423- : "Delete mode" }
424- </ AlertDialogTitle >
425- < AlertDialogDescription >
426- { deleteState . tMode && (
427- < >
428- { deleteState . action === "restore"
429- ? t
430- ? t ( "prompts:restoreMode.message" , { modeName : deleteState . tMode . name } )
431- : `Restore built-in mode ${ deleteState . tMode . name } ?`
432- : t
433- ? t ( "prompts:deleteMode.message" , { modeName : deleteState . tMode . name } )
434- : `Delete ${ deleteState . tMode . name } ?` }
435- { deleteState . tMode . rulesFolderPath && (
436- < div className = "mt-2" >
437- { t
438- ? t ( "prompts:deleteMode.rulesFolder" , {
439- folderPath : deleteState . tMode . rulesFolderPath ,
440- } )
441- : deleteState . tMode . rulesFolderPath }
442- </ div >
443- ) }
444- </ >
445- ) }
446- </ AlertDialogDescription >
447- </ AlertDialogHeader >
448- < AlertDialogFooter >
449- < AlertDialogCancel > { t ? t ( "prompts:deleteMode.cancel" ) : "Cancel" } </ AlertDialogCancel >
450- < AlertDialogAction onClick = { confirmDelete } >
451- { deleteState . action === "restore"
452- ? t
453- ? t ( "prompts:restoreMode.confirm" )
454- : "Restore"
455- : t
456- ? t ( "prompts:deleteMode.confirm" )
457- : "Delete" }
458- </ AlertDialogAction >
459- </ AlertDialogFooter >
460- </ AlertDialogContent >
461- </ AlertDialog >
449+ { /* Delete handled in settings; confirmation dialog removed from this popup */ }
462450 </ DialogContent >
463451 </ Dialog >
464452 )
0 commit comments