manage_profiles_modal.rs

  1mod profile_modal_header;
  2
  3use std::sync::Arc;
  4
  5use agent_settings::{AgentProfile, AgentProfileId, AgentSettings, builtin_profiles};
  6use assistant_tool::ToolWorkingSet;
  7use convert_case::{Case, Casing as _};
  8use editor::Editor;
  9use fs::Fs;
 10use gpui::{
 11    DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, WeakEntity,
 12    prelude::*,
 13};
 14use settings::{Settings as _, update_settings_file};
 15use ui::{
 16    KeyBinding, ListItem, ListItemSpacing, ListSeparator, Navigable, NavigableEntry, prelude::*,
 17};
 18use util::ResultExt as _;
 19use workspace::{ModalView, Workspace};
 20
 21use crate::agent_configuration::manage_profiles_modal::profile_modal_header::ProfileModalHeader;
 22use crate::agent_configuration::tool_picker::{ToolPicker, ToolPickerDelegate};
 23use crate::{AgentPanel, ManageProfiles, ThreadStore};
 24
 25use super::tool_picker::ToolPickerMode;
 26
 27enum Mode {
 28    ChooseProfile(ChooseProfileMode),
 29    NewProfile(NewProfileMode),
 30    ViewProfile(ViewProfileMode),
 31    ConfigureTools {
 32        profile_id: AgentProfileId,
 33        tool_picker: Entity<ToolPicker>,
 34        _subscription: Subscription,
 35    },
 36    ConfigureMcps {
 37        profile_id: AgentProfileId,
 38        tool_picker: Entity<ToolPicker>,
 39        _subscription: Subscription,
 40    },
 41}
 42
 43impl Mode {
 44    pub fn choose_profile(_window: &mut Window, cx: &mut Context<ManageProfilesModal>) -> Self {
 45        let settings = AgentSettings::get_global(cx);
 46
 47        let mut builtin_profiles = Vec::new();
 48        let mut custom_profiles = Vec::new();
 49
 50        for (profile_id, profile) in settings.profiles.iter() {
 51            let entry = ProfileEntry {
 52                id: profile_id.clone(),
 53                name: profile.name.clone(),
 54                navigation: NavigableEntry::focusable(cx),
 55            };
 56            if builtin_profiles::is_builtin(profile_id) {
 57                builtin_profiles.push(entry);
 58            } else {
 59                custom_profiles.push(entry);
 60            }
 61        }
 62
 63        builtin_profiles.sort_unstable_by(|a, b| a.name.cmp(&b.name));
 64        custom_profiles.sort_unstable_by(|a, b| a.name.cmp(&b.name));
 65
 66        Self::ChooseProfile(ChooseProfileMode {
 67            builtin_profiles,
 68            custom_profiles,
 69            add_new_profile: NavigableEntry::focusable(cx),
 70        })
 71    }
 72}
 73
 74#[derive(Clone)]
 75struct ProfileEntry {
 76    pub id: AgentProfileId,
 77    pub name: SharedString,
 78    pub navigation: NavigableEntry,
 79}
 80
 81#[derive(Clone)]
 82pub struct ChooseProfileMode {
 83    builtin_profiles: Vec<ProfileEntry>,
 84    custom_profiles: Vec<ProfileEntry>,
 85    add_new_profile: NavigableEntry,
 86}
 87
 88#[derive(Clone)]
 89pub struct ViewProfileMode {
 90    profile_id: AgentProfileId,
 91    fork_profile: NavigableEntry,
 92    configure_tools: NavigableEntry,
 93    configure_mcps: NavigableEntry,
 94    cancel_item: NavigableEntry,
 95}
 96
 97#[derive(Clone)]
 98pub struct NewProfileMode {
 99    name_editor: Entity<Editor>,
100    base_profile_id: Option<AgentProfileId>,
101}
102
103pub struct ManageProfilesModal {
104    fs: Arc<dyn Fs>,
105    tools: Entity<ToolWorkingSet>,
106    thread_store: WeakEntity<ThreadStore>,
107    focus_handle: FocusHandle,
108    mode: Mode,
109}
110
111impl ManageProfilesModal {
112    pub fn register(
113        workspace: &mut Workspace,
114        _window: Option<&mut Window>,
115        _cx: &mut Context<Workspace>,
116    ) {
117        workspace.register_action(|workspace, action: &ManageProfiles, window, cx| {
118            if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
119                let fs = workspace.app_state().fs.clone();
120                let thread_store = panel.read(cx).thread_store();
121                let tools = thread_store.read(cx).tools();
122                let thread_store = thread_store.downgrade();
123                workspace.toggle_modal(window, cx, |window, cx| {
124                    let mut this = Self::new(fs, tools, thread_store, window, cx);
125
126                    if let Some(profile_id) = action.customize_tools.clone() {
127                        this.configure_builtin_tools(profile_id, window, cx);
128                    }
129
130                    this
131                })
132            }
133        });
134    }
135
136    pub fn new(
137        fs: Arc<dyn Fs>,
138        tools: Entity<ToolWorkingSet>,
139        thread_store: WeakEntity<ThreadStore>,
140        window: &mut Window,
141        cx: &mut Context<Self>,
142    ) -> Self {
143        let focus_handle = cx.focus_handle();
144
145        Self {
146            fs,
147            tools,
148            thread_store,
149            focus_handle,
150            mode: Mode::choose_profile(window, cx),
151        }
152    }
153
154    fn choose_profile(&mut self, window: &mut Window, cx: &mut Context<Self>) {
155        self.mode = Mode::choose_profile(window, cx);
156        self.focus_handle(cx).focus(window);
157    }
158
159    fn new_profile(
160        &mut self,
161        base_profile_id: Option<AgentProfileId>,
162        window: &mut Window,
163        cx: &mut Context<Self>,
164    ) {
165        let name_editor = cx.new(|cx| Editor::single_line(window, cx));
166        name_editor.update(cx, |editor, cx| {
167            editor.set_placeholder_text("Profile name", cx);
168        });
169
170        self.mode = Mode::NewProfile(NewProfileMode {
171            name_editor,
172            base_profile_id,
173        });
174        self.focus_handle(cx).focus(window);
175    }
176
177    pub fn view_profile(
178        &mut self,
179        profile_id: AgentProfileId,
180        window: &mut Window,
181        cx: &mut Context<Self>,
182    ) {
183        self.mode = Mode::ViewProfile(ViewProfileMode {
184            profile_id,
185            fork_profile: NavigableEntry::focusable(cx),
186            configure_tools: NavigableEntry::focusable(cx),
187            configure_mcps: NavigableEntry::focusable(cx),
188            cancel_item: NavigableEntry::focusable(cx),
189        });
190        self.focus_handle(cx).focus(window);
191    }
192
193    fn configure_mcp_tools(
194        &mut self,
195        profile_id: AgentProfileId,
196        window: &mut Window,
197        cx: &mut Context<Self>,
198    ) {
199        let settings = AgentSettings::get_global(cx);
200        let Some(profile) = settings.profiles.get(&profile_id).cloned() else {
201            return;
202        };
203
204        let tool_picker = cx.new(|cx| {
205            let delegate = ToolPickerDelegate::new(
206                ToolPickerMode::McpTools,
207                self.fs.clone(),
208                self.tools.clone(),
209                self.thread_store.clone(),
210                profile_id.clone(),
211                profile,
212                cx,
213            );
214            ToolPicker::mcp_tools(delegate, window, cx)
215        });
216        let dismiss_subscription = cx.subscribe_in(&tool_picker, window, {
217            let profile_id = profile_id.clone();
218            move |this, _tool_picker, _: &DismissEvent, window, cx| {
219                this.view_profile(profile_id.clone(), window, cx);
220            }
221        });
222
223        self.mode = Mode::ConfigureMcps {
224            profile_id,
225            tool_picker,
226            _subscription: dismiss_subscription,
227        };
228        self.focus_handle(cx).focus(window);
229    }
230
231    fn configure_builtin_tools(
232        &mut self,
233        profile_id: AgentProfileId,
234        window: &mut Window,
235        cx: &mut Context<Self>,
236    ) {
237        let settings = AgentSettings::get_global(cx);
238        let Some(profile) = settings.profiles.get(&profile_id).cloned() else {
239            return;
240        };
241
242        let tool_picker = cx.new(|cx| {
243            let delegate = ToolPickerDelegate::new(
244                ToolPickerMode::BuiltinTools,
245                self.fs.clone(),
246                self.tools.clone(),
247                self.thread_store.clone(),
248                profile_id.clone(),
249                profile,
250                cx,
251            );
252            ToolPicker::builtin_tools(delegate, window, cx)
253        });
254        let dismiss_subscription = cx.subscribe_in(&tool_picker, window, {
255            let profile_id = profile_id.clone();
256            move |this, _tool_picker, _: &DismissEvent, window, cx| {
257                this.view_profile(profile_id.clone(), window, cx);
258            }
259        });
260
261        self.mode = Mode::ConfigureTools {
262            profile_id,
263            tool_picker,
264            _subscription: dismiss_subscription,
265        };
266        self.focus_handle(cx).focus(window);
267    }
268
269    fn confirm(&mut self, window: &mut Window, cx: &mut Context<Self>) {
270        match &self.mode {
271            Mode::ChooseProfile { .. } => {}
272            Mode::NewProfile(mode) => {
273                let settings = AgentSettings::get_global(cx);
274
275                let base_profile = mode
276                    .base_profile_id
277                    .as_ref()
278                    .and_then(|profile_id| settings.profiles.get(profile_id).cloned());
279
280                let name = mode.name_editor.read(cx).text(cx);
281                let profile_id = AgentProfileId(name.to_case(Case::Kebab).into());
282
283                let profile = AgentProfile {
284                    name: name.into(),
285                    tools: base_profile
286                        .as_ref()
287                        .map(|profile| profile.tools.clone())
288                        .unwrap_or_default(),
289                    enable_all_context_servers: base_profile
290                        .as_ref()
291                        .map(|profile| profile.enable_all_context_servers)
292                        .unwrap_or_default(),
293                    context_servers: base_profile
294                        .map(|profile| profile.context_servers)
295                        .unwrap_or_default(),
296                };
297
298                self.create_profile(profile_id.clone(), profile, cx);
299                self.view_profile(profile_id, window, cx);
300            }
301            Mode::ViewProfile(_) => {}
302            Mode::ConfigureTools { .. } => {}
303            Mode::ConfigureMcps { .. } => {}
304        }
305    }
306
307    fn cancel(&mut self, window: &mut Window, cx: &mut Context<Self>) {
308        match &self.mode {
309            Mode::ChooseProfile { .. } => {
310                cx.emit(DismissEvent);
311            }
312            Mode::NewProfile(mode) => {
313                if let Some(profile_id) = mode.base_profile_id.clone() {
314                    self.view_profile(profile_id, window, cx);
315                } else {
316                    self.choose_profile(window, cx);
317                }
318            }
319            Mode::ViewProfile(_) => self.choose_profile(window, cx),
320            Mode::ConfigureTools { profile_id, .. } => {
321                self.view_profile(profile_id.clone(), window, cx)
322            }
323            Mode::ConfigureMcps { profile_id, .. } => {
324                self.view_profile(profile_id.clone(), window, cx)
325            }
326        }
327    }
328
329    fn create_profile(
330        &self,
331        profile_id: AgentProfileId,
332        profile: AgentProfile,
333        cx: &mut Context<Self>,
334    ) {
335        update_settings_file::<AgentSettings>(self.fs.clone(), cx, {
336            move |settings, _cx| {
337                settings.create_profile(profile_id, profile).log_err();
338            }
339        });
340    }
341}
342
343impl ModalView for ManageProfilesModal {}
344
345impl Focusable for ManageProfilesModal {
346    fn focus_handle(&self, cx: &App) -> FocusHandle {
347        match &self.mode {
348            Mode::ChooseProfile(_) => self.focus_handle.clone(),
349            Mode::NewProfile(mode) => mode.name_editor.focus_handle(cx),
350            Mode::ViewProfile(_) => self.focus_handle.clone(),
351            Mode::ConfigureTools { tool_picker, .. } => tool_picker.focus_handle(cx),
352            Mode::ConfigureMcps { tool_picker, .. } => tool_picker.focus_handle(cx),
353        }
354    }
355}
356
357impl EventEmitter<DismissEvent> for ManageProfilesModal {}
358
359impl ManageProfilesModal {
360    fn render_profile(
361        &self,
362        profile: &ProfileEntry,
363        window: &mut Window,
364        cx: &mut Context<Self>,
365    ) -> impl IntoElement + use<> {
366        div()
367            .id(SharedString::from(format!("profile-{}", profile.id)))
368            .track_focus(&profile.navigation.focus_handle)
369            .on_action({
370                let profile_id = profile.id.clone();
371                cx.listener(move |this, _: &menu::Confirm, window, cx| {
372                    this.view_profile(profile_id.clone(), window, cx);
373                })
374            })
375            .child(
376                ListItem::new(SharedString::from(format!("profile-{}", profile.id)))
377                    .toggle_state(profile.navigation.focus_handle.contains_focused(window, cx))
378                    .inset(true)
379                    .spacing(ListItemSpacing::Sparse)
380                    .child(Label::new(profile.name.clone()))
381                    .end_slot(
382                        h_flex()
383                            .gap_1()
384                            .child(
385                                Label::new("Customize")
386                                    .size(LabelSize::Small)
387                                    .color(Color::Muted),
388                            )
389                            .children(KeyBinding::for_action_in(
390                                &menu::Confirm,
391                                &self.focus_handle,
392                                window,
393                                cx,
394                            )),
395                    )
396                    .on_click({
397                        let profile_id = profile.id.clone();
398                        cx.listener(move |this, _, window, cx| {
399                            this.view_profile(profile_id.clone(), window, cx);
400                        })
401                    }),
402            )
403    }
404
405    fn render_choose_profile(
406        &mut self,
407        mode: ChooseProfileMode,
408        window: &mut Window,
409        cx: &mut Context<Self>,
410    ) -> impl IntoElement {
411        Navigable::new(
412            div()
413                .track_focus(&self.focus_handle(cx))
414                .size_full()
415                .child(ProfileModalHeader::new("Agent Profiles", None))
416                .child(
417                    v_flex()
418                        .pb_1()
419                        .child(ListSeparator)
420                        .children(
421                            mode.builtin_profiles
422                                .iter()
423                                .map(|profile| self.render_profile(profile, window, cx)),
424                        )
425                        .when(!mode.custom_profiles.is_empty(), |this| {
426                            this.child(ListSeparator)
427                                .child(
428                                    div().pl_2().pb_1().child(
429                                        Label::new("Custom Profiles")
430                                            .size(LabelSize::Small)
431                                            .color(Color::Muted),
432                                    ),
433                                )
434                                .children(
435                                    mode.custom_profiles
436                                        .iter()
437                                        .map(|profile| self.render_profile(profile, window, cx)),
438                                )
439                        })
440                        .child(ListSeparator)
441                        .child(
442                            div()
443                                .id("new-profile")
444                                .track_focus(&mode.add_new_profile.focus_handle)
445                                .on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
446                                    this.new_profile(None, window, cx);
447                                }))
448                                .child(
449                                    ListItem::new("new-profile")
450                                        .toggle_state(
451                                            mode.add_new_profile
452                                                .focus_handle
453                                                .contains_focused(window, cx),
454                                        )
455                                        .inset(true)
456                                        .spacing(ListItemSpacing::Sparse)
457                                        .start_slot(Icon::new(IconName::Plus))
458                                        .child(Label::new("Add New Profile"))
459                                        .on_click({
460                                            cx.listener(move |this, _, window, cx| {
461                                                this.new_profile(None, window, cx);
462                                            })
463                                        }),
464                                ),
465                        ),
466                )
467                .into_any_element(),
468        )
469        .map(|mut navigable| {
470            for profile in mode.builtin_profiles {
471                navigable = navigable.entry(profile.navigation);
472            }
473            for profile in mode.custom_profiles {
474                navigable = navigable.entry(profile.navigation);
475            }
476
477            navigable
478        })
479        .entry(mode.add_new_profile)
480    }
481
482    fn render_new_profile(
483        &mut self,
484        mode: NewProfileMode,
485        _window: &mut Window,
486        cx: &mut Context<Self>,
487    ) -> impl IntoElement {
488        let settings = AgentSettings::get_global(cx);
489
490        let base_profile_name = mode.base_profile_id.as_ref().map(|base_profile_id| {
491            settings
492                .profiles
493                .get(base_profile_id)
494                .map(|profile| profile.name.clone())
495                .unwrap_or_else(|| "Unknown".into())
496        });
497
498        v_flex()
499            .id("new-profile")
500            .track_focus(&self.focus_handle(cx))
501            .child(ProfileModalHeader::new(
502                match &base_profile_name {
503                    Some(base_profile) => format!("Fork {base_profile}"),
504                    None => "New Profile".into(),
505                },
506                match base_profile_name {
507                    Some(_) => Some(IconName::Scissors),
508                    None => Some(IconName::Plus),
509                },
510            ))
511            .child(ListSeparator)
512            .child(h_flex().p_2().child(mode.name_editor.clone()))
513    }
514
515    fn render_view_profile(
516        &mut self,
517        mode: ViewProfileMode,
518        window: &mut Window,
519        cx: &mut Context<Self>,
520    ) -> impl IntoElement {
521        let settings = AgentSettings::get_global(cx);
522
523        let profile_id = &settings.default_profile;
524        let profile_name = settings
525            .profiles
526            .get(&mode.profile_id)
527            .map(|profile| profile.name.clone())
528            .unwrap_or_else(|| "Unknown".into());
529
530        let icon = match profile_id.as_str() {
531            "write" => IconName::Pencil,
532            "ask" => IconName::MessageBubbles,
533            _ => IconName::UserRoundPen,
534        };
535
536        Navigable::new(
537            div()
538                .track_focus(&self.focus_handle(cx))
539                .size_full()
540                .child(ProfileModalHeader::new(profile_name, Some(icon)))
541                .child(
542                    v_flex()
543                        .pb_1()
544                        .child(ListSeparator)
545                        .child(
546                            div()
547                                .id("fork-profile")
548                                .track_focus(&mode.fork_profile.focus_handle)
549                                .on_action({
550                                    let profile_id = mode.profile_id.clone();
551                                    cx.listener(move |this, _: &menu::Confirm, window, cx| {
552                                        this.new_profile(Some(profile_id.clone()), window, cx);
553                                    })
554                                })
555                                .child(
556                                    ListItem::new("fork-profile")
557                                        .toggle_state(
558                                            mode.fork_profile
559                                                .focus_handle
560                                                .contains_focused(window, cx),
561                                        )
562                                        .inset(true)
563                                        .spacing(ListItemSpacing::Sparse)
564                                        .start_slot(
565                                            Icon::new(IconName::Scissors)
566                                                .size(IconSize::Small)
567                                                .color(Color::Muted),
568                                        )
569                                        .child(Label::new("Fork Profile"))
570                                        .on_click({
571                                            let profile_id = mode.profile_id.clone();
572                                            cx.listener(move |this, _, window, cx| {
573                                                this.new_profile(
574                                                    Some(profile_id.clone()),
575                                                    window,
576                                                    cx,
577                                                );
578                                            })
579                                        }),
580                                ),
581                        )
582                        .child(
583                            div()
584                                .id("configure-builtin-tools")
585                                .track_focus(&mode.configure_tools.focus_handle)
586                                .on_action({
587                                    let profile_id = mode.profile_id.clone();
588                                    cx.listener(move |this, _: &menu::Confirm, window, cx| {
589                                        this.configure_builtin_tools(
590                                            profile_id.clone(),
591                                            window,
592                                            cx,
593                                        );
594                                    })
595                                })
596                                .child(
597                                    ListItem::new("configure-builtin-tools-item")
598                                        .toggle_state(
599                                            mode.configure_tools
600                                                .focus_handle
601                                                .contains_focused(window, cx),
602                                        )
603                                        .inset(true)
604                                        .spacing(ListItemSpacing::Sparse)
605                                        .start_slot(
606                                            Icon::new(IconName::Settings)
607                                                .size(IconSize::Small)
608                                                .color(Color::Muted),
609                                        )
610                                        .child(Label::new("Configure Built-in Tools"))
611                                        .on_click({
612                                            let profile_id = mode.profile_id.clone();
613                                            cx.listener(move |this, _, window, cx| {
614                                                this.configure_builtin_tools(
615                                                    profile_id.clone(),
616                                                    window,
617                                                    cx,
618                                                );
619                                            })
620                                        }),
621                                ),
622                        )
623                        .child(
624                            div()
625                                .id("configure-mcps")
626                                .track_focus(&mode.configure_mcps.focus_handle)
627                                .on_action({
628                                    let profile_id = mode.profile_id.clone();
629                                    cx.listener(move |this, _: &menu::Confirm, window, cx| {
630                                        this.configure_mcp_tools(profile_id.clone(), window, cx);
631                                    })
632                                })
633                                .child(
634                                    ListItem::new("configure-mcp-tools")
635                                        .toggle_state(
636                                            mode.configure_mcps
637                                                .focus_handle
638                                                .contains_focused(window, cx),
639                                        )
640                                        .inset(true)
641                                        .spacing(ListItemSpacing::Sparse)
642                                        .start_slot(
643                                            Icon::new(IconName::Hammer)
644                                                .size(IconSize::Small)
645                                                .color(Color::Muted),
646                                        )
647                                        .child(Label::new("Configure MCP Tools"))
648                                        .on_click({
649                                            let profile_id = mode.profile_id.clone();
650                                            cx.listener(move |this, _, window, cx| {
651                                                this.configure_mcp_tools(
652                                                    profile_id.clone(),
653                                                    window,
654                                                    cx,
655                                                );
656                                            })
657                                        }),
658                                ),
659                        )
660                        .child(ListSeparator)
661                        .child(
662                            div()
663                                .id("cancel-item")
664                                .track_focus(&mode.cancel_item.focus_handle)
665                                .on_action({
666                                    cx.listener(move |this, _: &menu::Confirm, window, cx| {
667                                        this.cancel(window, cx);
668                                    })
669                                })
670                                .child(
671                                    ListItem::new("cancel-item")
672                                        .toggle_state(
673                                            mode.cancel_item
674                                                .focus_handle
675                                                .contains_focused(window, cx),
676                                        )
677                                        .inset(true)
678                                        .spacing(ListItemSpacing::Sparse)
679                                        .start_slot(
680                                            Icon::new(IconName::ArrowLeft)
681                                                .size(IconSize::Small)
682                                                .color(Color::Muted),
683                                        )
684                                        .child(Label::new("Go Back"))
685                                        .end_slot(
686                                            div().children(
687                                                KeyBinding::for_action_in(
688                                                    &menu::Cancel,
689                                                    &self.focus_handle,
690                                                    window,
691                                                    cx,
692                                                )
693                                                .map(|kb| kb.size(rems_from_px(12.))),
694                                            ),
695                                        )
696                                        .on_click({
697                                            cx.listener(move |this, _, window, cx| {
698                                                this.cancel(window, cx);
699                                            })
700                                        }),
701                                ),
702                        ),
703                )
704                .into_any_element(),
705        )
706        .entry(mode.fork_profile)
707        .entry(mode.configure_tools)
708        .entry(mode.configure_mcps)
709        .entry(mode.cancel_item)
710    }
711}
712
713impl Render for ManageProfilesModal {
714    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
715        let settings = AgentSettings::get_global(cx);
716
717        let go_back_item = div()
718            .id("cancel-item")
719            .track_focus(&self.focus_handle)
720            .on_action({
721                cx.listener(move |this, _: &menu::Confirm, window, cx| {
722                    this.cancel(window, cx);
723                })
724            })
725            .child(
726                ListItem::new("cancel-item")
727                    .toggle_state(self.focus_handle.contains_focused(window, cx))
728                    .inset(true)
729                    .spacing(ListItemSpacing::Sparse)
730                    .start_slot(
731                        Icon::new(IconName::ArrowLeft)
732                            .size(IconSize::Small)
733                            .color(Color::Muted),
734                    )
735                    .child(Label::new("Go Back"))
736                    .end_slot(
737                        div().children(
738                            KeyBinding::for_action_in(
739                                &menu::Cancel,
740                                &self.focus_handle,
741                                window,
742                                cx,
743                            )
744                            .map(|kb| kb.size(rems_from_px(12.))),
745                        ),
746                    )
747                    .on_click({
748                        cx.listener(move |this, _, window, cx| {
749                            this.cancel(window, cx);
750                        })
751                    }),
752            );
753
754        div()
755            .elevation_3(cx)
756            .w(rems(34.))
757            .key_context("ManageProfilesModal")
758            .on_action(cx.listener(|this, _: &menu::Cancel, window, cx| this.cancel(window, cx)))
759            .on_action(cx.listener(|this, _: &menu::Confirm, window, cx| this.confirm(window, cx)))
760            .capture_any_mouse_down(cx.listener(|this, _, window, cx| {
761                this.focus_handle(cx).focus(window);
762            }))
763            .on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
764            .child(match &self.mode {
765                Mode::ChooseProfile(mode) => self
766                    .render_choose_profile(mode.clone(), window, cx)
767                    .into_any_element(),
768                Mode::NewProfile(mode) => self
769                    .render_new_profile(mode.clone(), window, cx)
770                    .into_any_element(),
771                Mode::ViewProfile(mode) => self
772                    .render_view_profile(mode.clone(), window, cx)
773                    .into_any_element(),
774                Mode::ConfigureTools {
775                    profile_id,
776                    tool_picker,
777                    ..
778                } => {
779                    let profile_name = settings
780                        .profiles
781                        .get(profile_id)
782                        .map(|profile| profile.name.clone())
783                        .unwrap_or_else(|| "Unknown".into());
784
785                    v_flex()
786                        .pb_1()
787                        .child(ProfileModalHeader::new(
788                            format!("{profile_name} — Configure Built-in Tools"),
789                            Some(IconName::Cog),
790                        ))
791                        .child(ListSeparator)
792                        .child(tool_picker.clone())
793                        .child(ListSeparator)
794                        .child(go_back_item)
795                        .into_any_element()
796                }
797                Mode::ConfigureMcps {
798                    profile_id,
799                    tool_picker,
800                    ..
801                } => {
802                    let profile_name = settings
803                        .profiles
804                        .get(profile_id)
805                        .map(|profile| profile.name.clone())
806                        .unwrap_or_else(|| "Unknown".into());
807
808                    v_flex()
809                        .pb_1()
810                        .child(ProfileModalHeader::new(
811                            format!("{profile_name} — Configure MCP Tools"),
812                            Some(IconName::Hammer),
813                        ))
814                        .child(ListSeparator)
815                        .child(tool_picker.clone())
816                        .child(ListSeparator)
817                        .child(go_back_item)
818                        .into_any_element()
819                }
820            })
821    }
822}