agent: Allow to collapse provider sections in the settings view (#30437)

Chris Kelly and Danilo Leal created

This is my first time contributing, so happy to make changes as needed.

## Problem

I found the LLM Provider settings to be pretty difficult to scan as I
was looking to enter my API credentials for a provider. Because all of
the provider configuration is exposed by default, providers that come at
the end of the list are pushed fairly far down and require scrolling. As
this list increases the problem only get worse.

## Solution

This is strictly a UI change.

* I put each provider configuration in a Disclosure that is closed by
default. This made scanning for my provider easy, and exposing the
configuration takes a single click. No scrolling is required to see all
providers on my 956px high laptop screen.
* I also added the success checkmark to authenticated providers to make
it even easier to find them to update a key or sign out.
* The `Start New Thread` had a class applied that was overriding the
default hover behavior of other buttons, so I removed it.

## Before
![CleanShot 2025-05-09 at 14 06
04@2x](https://github.com/user-attachments/assets/48d1e7ea-0dc8-4adc-845c-5227ec965130)

## After
![CleanShot 2025-05-09 at 14 33
23](https://github.com/user-attachments/assets/67e842a7-3251-46e5-ab18-7c4e600b84d8)

Release Notes:

- Improved Agent Panel settings view scannability by making each
provider block collapsible by default.

---------

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

Change summary

crates/agent/src/agent_configuration.rs | 85 +++++++++++++++++++-------
1 file changed, 60 insertions(+), 25 deletions(-)

Detailed changes

crates/agent/src/agent_configuration.rs 🔗

@@ -36,6 +36,7 @@ pub struct AgentConfiguration {
     configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
     context_server_store: Entity<ContextServerStore>,
     expanded_context_server_tools: HashMap<ContextServerId, bool>,
+    expanded_provider_configurations: HashMap<LanguageModelProviderId, bool>,
     tools: Entity<ToolWorkingSet>,
     _registry_subscription: Subscription,
     scroll_handle: ScrollHandle,
@@ -78,6 +79,7 @@ impl AgentConfiguration {
             configuration_views_by_provider: HashMap::default(),
             context_server_store,
             expanded_context_server_tools: HashMap::default(),
+            expanded_provider_configurations: HashMap::default(),
             tools,
             _registry_subscription: registry_subscription,
             scroll_handle,
@@ -96,6 +98,7 @@ impl AgentConfiguration {
 
     fn remove_provider_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
         self.configuration_views_by_provider.remove(provider_id);
+        self.expanded_provider_configurations.remove(provider_id);
     }
 
     fn add_provider_configuration_view(
@@ -135,9 +138,14 @@ impl AgentConfiguration {
             .get(&provider.id())
             .cloned();
 
+        let is_expanded = self
+            .expanded_provider_configurations
+            .get(&provider.id())
+            .copied()
+            .unwrap_or(true);
+
         v_flex()
             .pt_3()
-            .pb_1()
             .gap_1p5()
             .border_t_1()
             .border_color(cx.theme().colors().border.opacity(0.6))
@@ -152,32 +160,59 @@ impl AgentConfiguration {
                                     .size(IconSize::Small)
                                     .color(Color::Muted),
                             )
-                            .child(Label::new(provider_name.clone()).size(LabelSize::Large)),
+                            .child(Label::new(provider_name.clone()).size(LabelSize::Large))
+                            .when(provider.is_authenticated(cx) && !is_expanded, |parent| {
+                                parent.child(Icon::new(IconName::Check).color(Color::Success))
+                            }),
                     )
-                    .when(provider.is_authenticated(cx), |parent| {
-                        parent.child(
-                            Button::new(
-                                SharedString::from(format!("new-thread-{provider_id}")),
-                                "Start New Thread",
-                            )
-                            .icon_position(IconPosition::Start)
-                            .icon(IconName::Plus)
-                            .icon_size(IconSize::Small)
-                            .style(ButtonStyle::Filled)
-                            .layer(ElevationIndex::ModalSurface)
-                            .label_size(LabelSize::Small)
-                            .on_click(cx.listener({
-                                let provider = provider.clone();
-                                move |_this, _event, _window, cx| {
-                                    cx.emit(AssistantConfigurationEvent::NewThread(
-                                        provider.clone(),
-                                    ))
-                                }
-                            })),
-                        )
-                    }),
+                    .child(
+                        h_flex()
+                            .gap_1()
+                            .when(provider.is_authenticated(cx), |parent| {
+                                parent.child(
+                                    Button::new(
+                                        SharedString::from(format!("new-thread-{provider_id}")),
+                                        "Start New Thread",
+                                    )
+                                    .icon_position(IconPosition::Start)
+                                    .icon(IconName::Plus)
+                                    .icon_size(IconSize::Small)
+                                    .layer(ElevationIndex::ModalSurface)
+                                    .label_size(LabelSize::Small)
+                                    .on_click(cx.listener({
+                                        let provider = provider.clone();
+                                        move |_this, _event, _window, cx| {
+                                            cx.emit(AssistantConfigurationEvent::NewThread(
+                                                provider.clone(),
+                                            ))
+                                        }
+                                    })),
+                                )
+                            })
+                            .child(
+                                Disclosure::new(
+                                    SharedString::from(format!(
+                                        "provider-disclosure-{provider_id}"
+                                    )),
+                                    is_expanded,
+                                )
+                                .opened_icon(IconName::ChevronUp)
+                                .closed_icon(IconName::ChevronDown)
+                                .on_click(cx.listener({
+                                    let provider_id = provider.id().clone();
+                                    move |this, _event, _window, _cx| {
+                                        let is_open = this
+                                            .expanded_provider_configurations
+                                            .entry(provider_id.clone())
+                                            .or_insert(true);
+
+                                        *is_open = !*is_open;
+                                    }
+                                })),
+                            ),
+                    ),
             )
-            .map(|parent| match configuration_view {
+            .when(is_expanded, |parent| match configuration_view {
                 Some(configuration_view) => parent.child(configuration_view),
                 None => parent.child(div().child(Label::new(format!(
                     "No configuration view for {provider_name}",