agent: Make the profile switcher a picker (#39218)

David and Danilo Leal created

Split off from https://github.com/zed-industries/zed/pull/39175

Adds a search bar to the 'profile' panel, so that we can switch profiles
without having to use the mouse or `tab` a few times

![2025-09-30 13 32
55](https://github.com/user-attachments/assets/2fc1f32b-9e25-4059-aae1-d195334a5fdb)

Release Notes:

- agent: Added the ability to search profiles in the agent panel's
profile picker.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/agent_ui/src/profile_selector.rs | 779 +++++++++++++++++++++-----
1 file changed, 632 insertions(+), 147 deletions(-)

Detailed changes

crates/agent_ui/src/profile_selector.rs đź”—

@@ -3,12 +3,19 @@ use agent_settings::{
     AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles,
 };
 use fs::Fs;
-use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*};
-use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file};
-use std::sync::Arc;
+use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
+use gpui::{
+    Action, AnyElement, App, BackgroundExecutor, Context, DismissEvent, Entity, FocusHandle,
+    Focusable, SharedString, Subscription, Task, Window,
+};
+use picker::{Picker, PickerDelegate, popover_menu::PickerPopoverMenu};
+use settings::{Settings as _, SettingsStore, update_settings_file};
+use std::{
+    sync::atomic::Ordering,
+    sync::{Arc, atomic::AtomicBool},
+};
 use ui::{
-    ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu,
-    PopoverMenuHandle, TintColor, Tooltip, prelude::*,
+    HighlightedLabel, ListItem, ListItemSpacing, PopoverMenuHandle, TintColor, Tooltip, prelude::*,
 };
 
 /// Trait for types that can provide and manage agent profiles
@@ -25,9 +32,11 @@ pub trait ProfileProvider {
 
 pub struct ProfileSelector {
     profiles: AvailableProfiles,
+    pending_refresh: bool,
     fs: Arc<dyn Fs>,
     provider: Arc<dyn ProfileProvider>,
-    menu_handle: PopoverMenuHandle<ContextMenu>,
+    picker: Option<Entity<Picker<ProfilePickerDelegate>>>,
+    picker_handle: PopoverMenuHandle<Picker<ProfilePickerDelegate>>,
     focus_handle: FocusHandle,
     _subscriptions: Vec<Subscription>,
 }
@@ -40,188 +49,664 @@ impl ProfileSelector {
         cx: &mut Context<Self>,
     ) -> Self {
         let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
-            this.refresh_profiles(cx);
+            this.pending_refresh = true;
+            cx.notify();
         });
 
         Self {
             profiles: AgentProfile::available_profiles(cx),
+            pending_refresh: false,
             fs,
             provider,
-            menu_handle: PopoverMenuHandle::default(),
+            picker: None,
+            picker_handle: PopoverMenuHandle::default(),
             focus_handle,
             _subscriptions: vec![settings_subscription],
         }
     }
 
-    pub fn menu_handle(&self) -> PopoverMenuHandle<ContextMenu> {
-        self.menu_handle.clone()
-    }
-
-    fn refresh_profiles(&mut self, cx: &mut Context<Self>) {
-        self.profiles = AgentProfile::available_profiles(cx);
+    pub fn menu_handle(&self) -> PopoverMenuHandle<Picker<ProfilePickerDelegate>> {
+        self.picker_handle.clone()
     }
 
-    fn build_context_menu(
-        &self,
+    fn ensure_picker(
+        &mut self,
         window: &mut Window,
         cx: &mut Context<Self>,
-    ) -> Entity<ContextMenu> {
-        ContextMenu::build(window, cx, |mut menu, _window, cx| {
-            let settings = AgentSettings::get_global(cx);
-
-            let mut found_non_builtin = false;
-            for (profile_id, profile_name) in self.profiles.iter() {
-                if !builtin_profiles::is_builtin(profile_id) {
-                    found_non_builtin = true;
-                    continue;
-                }
-                menu = menu.item(self.menu_entry_for_profile(
-                    profile_id.clone(),
-                    profile_name,
-                    settings,
-                    cx,
-                ));
-            }
+    ) -> Entity<Picker<ProfilePickerDelegate>> {
+        if self.picker.is_none() {
+            let delegate = ProfilePickerDelegate::new(
+                self.fs.clone(),
+                self.provider.clone(),
+                self.profiles.clone(),
+                cx.background_executor().clone(),
+                cx,
+            );
 
-            if found_non_builtin {
-                menu = menu.separator().header("Custom Profiles");
-                for (profile_id, profile_name) in self.profiles.iter() {
-                    if builtin_profiles::is_builtin(profile_id) {
-                        continue;
-                    }
-                    menu = menu.item(self.menu_entry_for_profile(
-                        profile_id.clone(),
-                        profile_name,
-                        settings,
-                        cx,
-                    ));
-                }
+            let picker = cx.new(|cx| {
+                Picker::list(delegate, window, cx)
+                    .show_scrollbar(true)
+                    .width(rems(20.))
+                    .max_height(Some(rems(16.).into()))
+            });
+
+            self.picker = Some(picker);
+        }
+
+        if self.pending_refresh {
+            if let Some(picker) = &self.picker {
+                let profiles = AgentProfile::available_profiles(cx);
+                self.profiles = profiles.clone();
+                picker.update(cx, |picker, cx| {
+                    let query = picker.query(cx);
+                    picker
+                        .delegate
+                        .refresh_profiles(profiles.clone(), query, cx);
+                });
             }
+            self.pending_refresh = false;
+        }
 
-            menu = menu.separator();
-            menu = menu.item(ContextMenuEntry::new("Configure Profiles…").handler(
-                move |window, cx| {
-                    window.dispatch_action(ManageProfiles::default().boxed_clone(), cx);
-                },
-            ));
+        self.picker.as_ref().unwrap().clone()
+    }
+}
 
-            menu
-        })
+impl Focusable for ProfileSelector {
+    fn focus_handle(&self, cx: &App) -> FocusHandle {
+        if let Some(picker) = &self.picker {
+            picker.focus_handle(cx)
+        } else {
+            self.focus_handle.clone()
+        }
     }
+}
 
-    fn menu_entry_for_profile(
-        &self,
-        profile_id: AgentProfileId,
-        profile_name: &SharedString,
-        settings: &AgentSettings,
-        cx: &App,
-    ) -> ContextMenuEntry {
-        let documentation = match profile_name.to_lowercase().as_str() {
+impl Render for ProfileSelector {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        if !self.provider.profiles_supported(cx) {
+            return Button::new("tools-not-supported-button", "Tools Unsupported")
+                .disabled(true)
+                .label_size(LabelSize::Small)
+                .color(Color::Muted)
+                .tooltip(Tooltip::text("This model does not support tools."))
+                .into_any_element();
+        }
+
+        let picker = self.ensure_picker(window, cx);
+
+        let settings = AgentSettings::get_global(cx);
+        let profile_id = self.provider.profile_id(cx);
+        let profile = settings.profiles.get(&profile_id);
+
+        let selected_profile = profile
+            .map(|profile| profile.name.clone())
+            .unwrap_or_else(|| "Unknown".into());
+        let focus_handle = self.focus_handle.clone();
+
+        let trigger_button = Button::new("profile-selector", selected_profile)
+            .label_size(LabelSize::Small)
+            .color(Color::Muted)
+            .icon(IconName::ChevronDown)
+            .icon_size(IconSize::XSmall)
+            .icon_position(IconPosition::End)
+            .icon_color(Color::Muted)
+            .selected_style(ButtonStyle::Tinted(TintColor::Accent));
+
+        PickerPopoverMenu::new(
+            picker,
+            trigger_button,
+            move |window, cx| {
+                Tooltip::for_action_in(
+                    "Toggle Profile Menu",
+                    &ToggleProfileSelector,
+                    &focus_handle,
+                    window,
+                    cx,
+                )
+            },
+            gpui::Corner::BottomRight,
+            cx,
+        )
+        .with_handle(self.picker_handle.clone())
+        .render(window, cx)
+        .into_any_element()
+    }
+}
+
+#[derive(Clone)]
+struct ProfileCandidate {
+    id: AgentProfileId,
+    name: SharedString,
+    is_builtin: bool,
+}
+
+#[derive(Clone)]
+struct ProfileMatchEntry {
+    candidate_index: usize,
+    positions: Vec<usize>,
+}
+
+enum ProfilePickerEntry {
+    Header(SharedString),
+    Profile(ProfileMatchEntry),
+}
+
+pub(crate) struct ProfilePickerDelegate {
+    fs: Arc<dyn Fs>,
+    provider: Arc<dyn ProfileProvider>,
+    background: BackgroundExecutor,
+    candidates: Vec<ProfileCandidate>,
+    string_candidates: Arc<Vec<StringMatchCandidate>>,
+    filtered_entries: Vec<ProfilePickerEntry>,
+    selected_index: usize,
+    query: String,
+    cancel: Option<Arc<AtomicBool>>,
+}
+
+impl ProfilePickerDelegate {
+    fn new(
+        fs: Arc<dyn Fs>,
+        provider: Arc<dyn ProfileProvider>,
+        profiles: AvailableProfiles,
+        background: BackgroundExecutor,
+        cx: &mut Context<ProfileSelector>,
+    ) -> Self {
+        let candidates = Self::candidates_from(profiles);
+        let string_candidates = Arc::new(Self::string_candidates(&candidates));
+        let filtered_entries = Self::entries_from_candidates(&candidates);
+
+        let mut this = Self {
+            fs,
+            provider,
+            background,
+            candidates,
+            string_candidates,
+            filtered_entries,
+            selected_index: 0,
+            query: String::new(),
+            cancel: None,
+        };
+
+        this.selected_index = this
+            .index_of_profile(&this.provider.profile_id(cx))
+            .unwrap_or_else(|| this.first_selectable_index().unwrap_or(0));
+
+        this
+    }
+
+    fn refresh_profiles(
+        &mut self,
+        profiles: AvailableProfiles,
+        query: String,
+        cx: &mut Context<Picker<Self>>,
+    ) {
+        self.candidates = Self::candidates_from(profiles);
+        self.string_candidates = Arc::new(Self::string_candidates(&self.candidates));
+        self.query = query;
+
+        if self.query.is_empty() {
+            self.filtered_entries = Self::entries_from_candidates(&self.candidates);
+        } else {
+            let matches = self.search_blocking(&self.query);
+            self.filtered_entries = self.entries_from_matches(matches);
+        }
+
+        self.selected_index = self
+            .index_of_profile(&self.provider.profile_id(cx))
+            .unwrap_or_else(|| self.first_selectable_index().unwrap_or(0));
+        cx.notify();
+    }
+
+    fn candidates_from(profiles: AvailableProfiles) -> Vec<ProfileCandidate> {
+        profiles
+            .into_iter()
+            .map(|(id, name)| ProfileCandidate {
+                is_builtin: builtin_profiles::is_builtin(&id),
+                id,
+                name,
+            })
+            .collect()
+    }
+
+    fn string_candidates(candidates: &[ProfileCandidate]) -> Vec<StringMatchCandidate> {
+        candidates
+            .iter()
+            .enumerate()
+            .map(|(index, candidate)| StringMatchCandidate::new(index, candidate.name.as_ref()))
+            .collect()
+    }
+
+    fn documentation(candidate: &ProfileCandidate) -> Option<&'static str> {
+        match candidate.id.as_str() {
             builtin_profiles::WRITE => Some("Get help to write anything."),
             builtin_profiles::ASK => Some("Chat about your codebase."),
             builtin_profiles::MINIMAL => Some("Chat about anything with no tools."),
             _ => None,
-        };
-        let thread_profile_id = self.provider.profile_id(cx);
+        }
+    }
 
-        let entry = ContextMenuEntry::new(profile_name.clone())
-            .toggleable(IconPosition::End, profile_id == thread_profile_id);
+    fn entries_from_candidates(candidates: &[ProfileCandidate]) -> Vec<ProfilePickerEntry> {
+        let mut entries = Vec::new();
+        let mut inserted_custom_header = false;
 
-        let entry = if let Some(doc_text) = documentation {
-            entry.documentation_aside(
-                documentation_side(settings.dock),
-                DocumentationEdge::Top,
-                move |_| Label::new(doc_text).into_any_element(),
-            )
-        } else {
-            entry
-        };
+        for (idx, candidate) in candidates.iter().enumerate() {
+            if !candidate.is_builtin && !inserted_custom_header {
+                if !entries.is_empty() {
+                    entries.push(ProfilePickerEntry::Header("Custom Profiles".into()));
+                }
+                inserted_custom_header = true;
+            }
 
-        entry.handler({
-            let fs = self.fs.clone();
-            let provider = self.provider.clone();
-            move |_window, cx| {
-                update_settings_file(fs.clone(), cx, {
-                    let profile_id = profile_id.clone();
-                    move |settings, _cx| {
-                        settings
-                            .agent
-                            .get_or_insert_default()
-                            .set_profile(profile_id.0);
-                    }
-                });
+            entries.push(ProfilePickerEntry::Profile(ProfileMatchEntry {
+                candidate_index: idx,
+                positions: Vec::new(),
+            }));
+        }
+
+        entries
+    }
 
-                provider.set_profile(profile_id.clone(), cx);
+    fn entries_from_matches(&self, matches: Vec<StringMatch>) -> Vec<ProfilePickerEntry> {
+        let mut entries = Vec::new();
+        for mat in matches {
+            if self.candidates.get(mat.candidate_id).is_some() {
+                entries.push(ProfilePickerEntry::Profile(ProfileMatchEntry {
+                    candidate_index: mat.candidate_id,
+                    positions: mat.positions,
+                }));
             }
+        }
+        entries
+    }
+
+    fn first_selectable_index(&self) -> Option<usize> {
+        self.filtered_entries
+            .iter()
+            .position(|entry| matches!(entry, ProfilePickerEntry::Profile(_)))
+    }
+
+    fn index_of_profile(&self, profile_id: &AgentProfileId) -> Option<usize> {
+        self.filtered_entries.iter().position(|entry| {
+            matches!(entry, ProfilePickerEntry::Profile(profile) if self
+                .candidates
+                .get(profile.candidate_index)
+                .map(|candidate| &candidate.id == profile_id)
+                .unwrap_or(false))
         })
     }
+
+    fn search_blocking(&self, query: &str) -> Vec<StringMatch> {
+        if query.is_empty() {
+            return self
+                .string_candidates
+                .iter()
+                .map(|candidate| StringMatch {
+                    candidate_id: candidate.id,
+                    score: 0.0,
+                    positions: Vec::new(),
+                    string: candidate.string.clone(),
+                })
+                .collect();
+        }
+
+        let cancel_flag = AtomicBool::new(false);
+
+        self.background.block(match_strings(
+            self.string_candidates.as_ref(),
+            query,
+            false,
+            true,
+            100,
+            &cancel_flag,
+            self.background.clone(),
+        ))
+    }
 }
 
-impl Render for ProfileSelector {
-    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        let settings = AgentSettings::get_global(cx);
-        let profile_id = self.provider.profile_id(cx);
-        let profile = settings.profiles.get(&profile_id);
+impl PickerDelegate for ProfilePickerDelegate {
+    type ListItem = AnyElement;
 
-        let selected_profile = profile
-            .map(|profile| profile.name.clone())
-            .unwrap_or_else(|| "Unknown".into());
+    fn placeholder_text(&self, _: &mut Window, _: &mut App) -> Arc<str> {
+        "Search profiles…".into()
+    }
 
-        if self.provider.profiles_supported(cx) {
-            let this = cx.entity();
-            let focus_handle = self.focus_handle.clone();
-            let trigger_button = Button::new("profile-selector-model", selected_profile)
-                .label_size(LabelSize::Small)
-                .color(Color::Muted)
-                .icon(IconName::ChevronDown)
-                .icon_size(IconSize::XSmall)
-                .icon_position(IconPosition::End)
-                .icon_color(Color::Muted)
-                .selected_style(ButtonStyle::Tinted(TintColor::Accent));
-
-            PopoverMenu::new("profile-selector")
-                .trigger_with_tooltip(trigger_button, {
-                    move |window, cx| {
-                        Tooltip::for_action_in(
-                            "Toggle Profile Menu",
-                            &ToggleProfileSelector,
-                            &focus_handle,
-                            window,
-                            cx,
+    fn no_matches_text(&self, _window: &mut Window, _cx: &mut App) -> Option<SharedString> {
+        let text = if self.candidates.is_empty() {
+            "No profiles.".into()
+        } else {
+            "No profiles match your search.".into()
+        };
+        Some(text)
+    }
+
+    fn match_count(&self) -> usize {
+        self.filtered_entries.len()
+    }
+
+    fn selected_index(&self) -> usize {
+        self.selected_index
+    }
+
+    fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<Picker<Self>>) {
+        self.selected_index = ix.min(self.filtered_entries.len().saturating_sub(1));
+        cx.notify();
+    }
+
+    fn can_select(
+        &mut self,
+        ix: usize,
+        _window: &mut Window,
+        _cx: &mut Context<Picker<Self>>,
+    ) -> bool {
+        match self.filtered_entries.get(ix) {
+            Some(ProfilePickerEntry::Profile(_)) => true,
+            Some(ProfilePickerEntry::Header(_)) | None => false,
+        }
+    }
+
+    fn update_matches(
+        &mut self,
+        query: String,
+        window: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Task<()> {
+        if query.is_empty() {
+            self.query.clear();
+            self.filtered_entries = Self::entries_from_candidates(&self.candidates);
+            self.selected_index = self
+                .index_of_profile(&self.provider.profile_id(cx))
+                .unwrap_or_else(|| self.first_selectable_index().unwrap_or(0));
+            cx.notify();
+            return Task::ready(());
+        }
+
+        if let Some(prev) = &self.cancel {
+            prev.store(true, Ordering::Relaxed);
+        }
+        let cancel = Arc::new(AtomicBool::new(false));
+        self.cancel = Some(cancel.clone());
+
+        let string_candidates = self.string_candidates.clone();
+        let background = self.background.clone();
+        let provider = self.provider.clone();
+        self.query = query.clone();
+
+        let cancel_for_future = cancel;
+
+        cx.spawn_in(window, async move |this, cx| {
+            let matches = match_strings(
+                string_candidates.as_ref(),
+                &query,
+                false,
+                true,
+                100,
+                cancel_for_future.as_ref(),
+                background,
+            )
+            .await;
+
+            this.update_in(cx, |this, _, cx| {
+                if this.delegate.query != query {
+                    return;
+                }
+
+                this.delegate.filtered_entries = this.delegate.entries_from_matches(matches);
+                this.delegate.selected_index = this
+                    .delegate
+                    .index_of_profile(&provider.profile_id(cx))
+                    .unwrap_or_else(|| this.delegate.first_selectable_index().unwrap_or(0));
+                cx.notify();
+            })
+            .ok();
+        })
+    }
+
+    fn confirm(&mut self, _: bool, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
+        match self.filtered_entries.get(self.selected_index) {
+            Some(ProfilePickerEntry::Profile(entry)) => {
+                if let Some(candidate) = self.candidates.get(entry.candidate_index) {
+                    let profile_id = candidate.id.clone();
+                    let fs = self.fs.clone();
+                    let provider = self.provider.clone();
+
+                    update_settings_file(fs, cx, {
+                        let profile_id = profile_id.clone();
+                        move |settings, _cx| {
+                            settings
+                                .agent
+                                .get_or_insert_default()
+                                .set_profile(profile_id.0);
+                        }
+                    });
+
+                    provider.set_profile(profile_id.clone(), cx);
+
+                    telemetry::event!(
+                        "agent_profile_switched",
+                        profile_id = profile_id.as_str(),
+                        source = "picker"
+                    );
+                }
+
+                cx.emit(DismissEvent);
+            }
+            _ => {}
+        }
+    }
+
+    fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<Self>>) {
+        cx.defer_in(window, |picker, window, cx| {
+            picker.set_query("", window, cx);
+        });
+        cx.emit(DismissEvent);
+    }
+
+    fn render_match(
+        &self,
+        ix: usize,
+        selected: bool,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<Self::ListItem> {
+        match self.filtered_entries.get(ix)? {
+            ProfilePickerEntry::Header(label) => Some(
+                div()
+                    .px_2p5()
+                    .pb_0p5()
+                    .when(ix > 0, |this| {
+                        this.mt_1p5()
+                            .pt_2()
+                            .border_t_1()
+                            .border_color(cx.theme().colors().border_variant)
+                    })
+                    .child(
+                        Label::new(label.clone())
+                            .size(LabelSize::XSmall)
+                            .color(Color::Muted),
+                    )
+                    .into_any_element(),
+            ),
+            ProfilePickerEntry::Profile(entry) => {
+                let candidate = self.candidates.get(entry.candidate_index)?;
+                let active_id = self.provider.profile_id(cx);
+                let is_active = active_id == candidate.id;
+
+                Some(
+                    ListItem::new(SharedString::from(candidate.id.0.clone()))
+                        .inset(true)
+                        .spacing(ListItemSpacing::Sparse)
+                        .toggle_state(selected)
+                        .child(
+                            v_flex()
+                                .child(HighlightedLabel::new(
+                                    candidate.name.clone(),
+                                    entry.positions.clone(),
+                                ))
+                                .when_some(Self::documentation(candidate), |this, doc| {
+                                    this.child(
+                                        Label::new(doc).size(LabelSize::Small).color(Color::Muted),
+                                    )
+                                }),
                         )
-                    }
-                })
-                .anchor(
-                    if documentation_side(settings.dock) == DocumentationSide::Left {
-                        gpui::Corner::BottomRight
-                    } else {
-                        gpui::Corner::BottomLeft
-                    },
+                        .when(is_active, |this| {
+                            this.end_slot(
+                                div()
+                                    .pr_2()
+                                    .child(Icon::new(IconName::Check).color(Color::Accent)),
+                            )
+                        })
+                        .into_any_element(),
                 )
-                .with_handle(self.menu_handle.clone())
-                .menu(move |window, cx| {
-                    Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
-                })
-                .offset(gpui::Point {
-                    x: px(0.0),
-                    y: px(-2.0),
-                })
-                .into_any_element()
-        } else {
-            Button::new("tools-not-supported-button", "Tools Unsupported")
-                .disabled(true)
-                .label_size(LabelSize::Small)
-                .color(Color::Muted)
-                .tooltip(Tooltip::text("This model does not support tools."))
-                .into_any_element()
+            }
         }
     }
+
+    fn render_footer(
+        &self,
+        _: &mut Window,
+        cx: &mut Context<Picker<Self>>,
+    ) -> Option<gpui::AnyElement> {
+        Some(
+            h_flex()
+                .w_full()
+                .border_t_1()
+                .border_color(cx.theme().colors().border_variant)
+                .p_1()
+                .gap_4()
+                .justify_between()
+                .child(
+                    Button::new("configure", "Configure")
+                        .icon(IconName::Settings)
+                        .icon_size(IconSize::Small)
+                        .icon_color(Color::Muted)
+                        .icon_position(IconPosition::Start)
+                        .on_click(|_, window, cx| {
+                            window.dispatch_action(ManageProfiles::default().boxed_clone(), cx);
+                        }),
+                )
+                .into_any(),
+        )
+    }
 }
 
-fn documentation_side(position: DockPosition) -> DocumentationSide {
-    match position {
-        DockPosition::Left => DocumentationSide::Right,
-        DockPosition::Bottom => DocumentationSide::Left,
-        DockPosition::Right => DocumentationSide::Left,
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use fs::FakeFs;
+    use gpui::TestAppContext;
+
+    #[gpui::test]
+    fn entries_include_custom_profiles(_cx: &mut TestAppContext) {
+        let candidates = vec![
+            ProfileCandidate {
+                id: AgentProfileId("write".into()),
+                name: SharedString::from("Write"),
+                is_builtin: true,
+            },
+            ProfileCandidate {
+                id: AgentProfileId("my-custom".into()),
+                name: SharedString::from("My Custom"),
+                is_builtin: false,
+            },
+        ];
+
+        let entries = ProfilePickerDelegate::entries_from_candidates(&candidates);
+
+        assert!(entries.iter().any(|entry| matches!(
+            entry,
+            ProfilePickerEntry::Profile(profile)
+                if candidates[profile.candidate_index].id.as_str() == "my-custom"
+        )));
+        assert!(entries.iter().any(|entry| matches!(
+            entry,
+            ProfilePickerEntry::Header(label) if label.as_ref() == "Custom Profiles"
+        )));
+    }
+
+    #[gpui::test]
+    fn fuzzy_filter_returns_no_results_and_keeps_configure(cx: &mut TestAppContext) {
+        let candidates = vec![ProfileCandidate {
+            id: AgentProfileId("write".into()),
+            name: SharedString::from("Write"),
+            is_builtin: true,
+        }];
+
+        let delegate = ProfilePickerDelegate {
+            fs: FakeFs::new(cx.executor()),
+            provider: Arc::new(TestProfileProvider::new(AgentProfileId("write".into()))),
+            background: cx.executor(),
+            candidates,
+            string_candidates: Arc::new(Vec::new()),
+            filtered_entries: Vec::new(),
+            selected_index: 0,
+            query: String::new(),
+            cancel: None,
+        };
+
+        let matches = Vec::new(); // No matches
+        let _entries = delegate.entries_from_matches(matches);
+    }
+
+    #[gpui::test]
+    fn active_profile_selection_logic_works(cx: &mut TestAppContext) {
+        let candidates = vec![
+            ProfileCandidate {
+                id: AgentProfileId("write".into()),
+                name: SharedString::from("Write"),
+                is_builtin: true,
+            },
+            ProfileCandidate {
+                id: AgentProfileId("ask".into()),
+                name: SharedString::from("Ask"),
+                is_builtin: true,
+            },
+        ];
+
+        let delegate = ProfilePickerDelegate {
+            fs: FakeFs::new(cx.executor()),
+            provider: Arc::new(TestProfileProvider::new(AgentProfileId("write".into()))),
+            background: cx.executor(),
+            candidates,
+            string_candidates: Arc::new(Vec::new()),
+            filtered_entries: vec![
+                ProfilePickerEntry::Profile(ProfileMatchEntry {
+                    candidate_index: 0,
+                    positions: Vec::new(),
+                }),
+                ProfilePickerEntry::Profile(ProfileMatchEntry {
+                    candidate_index: 1,
+                    positions: Vec::new(),
+                }),
+            ],
+            selected_index: 0,
+            query: String::new(),
+            cancel: None,
+        };
+
+        // Active profile should be found at index 0
+        let active_index = delegate.index_of_profile(&AgentProfileId("write".into()));
+        assert_eq!(active_index, Some(0));
+    }
+
+    struct TestProfileProvider {
+        profile_id: AgentProfileId,
+    }
+
+    impl TestProfileProvider {
+        fn new(profile_id: AgentProfileId) -> Self {
+            Self { profile_id }
+        }
+    }
+
+    impl ProfileProvider for TestProfileProvider {
+        fn profile_id(&self, _cx: &App) -> AgentProfileId {
+            self.profile_id.clone()
+        }
+
+        fn set_profile(&self, _profile_id: AgentProfileId, _cx: &mut App) {}
+
+        fn profiles_supported(&self, _cx: &App) -> bool {
+            true
+        }
     }
 }