assistant2: Tweak the settings UI (#23845)

Danilo Leal created

This PR does some somewhat light UI adjustment to the Assistant 2
settings view. The Prompt Library section should feature the default
prompts in the future, so that's why it's been separated that way.

<img width="800" alt="Screenshot 2025-01-29 at 2 59 59β€―PM"
src="https://github.com/user-attachments/assets/7b033bde-51ab-44d5-9e53-3f72b8ff5f51"
/>

Release Notes:

- N/A

Change summary

crates/assistant2/src/assistant_configuration.rs | 90 +++++++++++------
crates/assistant2/src/assistant_panel.rs         |  4 
crates/language_models/src/provider/anthropic.rs |  4 
crates/language_models/src/provider/deepseek.rs  |  6 
crates/language_models/src/provider/google.rs    |  4 
crates/language_models/src/provider/lmstudio.rs  | 11 +-
crates/language_models/src/provider/ollama.rs    |  9 -
crates/language_models/src/provider/open_ai.rs   |  4 
8 files changed, 82 insertions(+), 50 deletions(-)

Detailed changes

crates/assistant2/src/assistant_configuration.rs πŸ”—

@@ -3,7 +3,7 @@ use std::sync::Arc;
 use collections::HashMap;
 use gpui::{AnyView, App, EventEmitter, FocusHandle, Focusable, Subscription};
 use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
-use ui::{prelude::*, ElevationIndex};
+use ui::{prelude::*, Divider, DividerColor, ElevationIndex};
 use zed_actions::assistant::DeployPromptLibrary;
 
 pub struct AssistantConfiguration {
@@ -91,38 +91,47 @@ impl AssistantConfiguration {
             .cloned();
 
         v_flex()
-            .gap_2()
+            .gap_1p5()
             .child(
                 h_flex()
                     .justify_between()
-                    .child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
+                    .child(
+                        h_flex()
+                            .gap_2()
+                            .child(
+                                Icon::new(provider.icon())
+                                    .size(IconSize::Small)
+                                    .color(Color::Muted),
+                            )
+                            .child(Label::new(provider_name.clone())),
+                    )
                     .when(provider.is_authenticated(cx), |parent| {
                         parent.child(
-                            h_flex().justify_end().child(
-                                Button::new(
-                                    SharedString::from(format!("new-thread-{provider_id}")),
-                                    "Open New Thread",
-                                )
-                                .icon_position(IconPosition::Start)
-                                .icon(IconName::Plus)
-                                .style(ButtonStyle::Filled)
-                                .layer(ElevationIndex::ModalSurface)
-                                .on_click(cx.listener({
-                                    let provider = provider.clone();
-                                    move |_this, _event, _window, cx| {
-                                        cx.emit(AssistantConfigurationEvent::NewThread(
-                                            provider.clone(),
-                                        ))
-                                    }
-                                })),
-                            ),
+                            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(
                 div()
                     .p(DynamicSpacing::Base08.rems(cx))
-                    .bg(cx.theme().colors().surface_background)
+                    .bg(cx.theme().colors().editor_background)
                     .border_1()
                     .border_color(cx.theme().colors().border_variant)
                     .rounded_md()
@@ -143,26 +152,43 @@ impl Render for AssistantConfiguration {
         v_flex()
             .id("assistant-configuration")
             .track_focus(&self.focus_handle(cx))
-            .bg(cx.theme().colors().editor_background)
+            .bg(cx.theme().colors().panel_background)
             .size_full()
             .overflow_y_scroll()
             .child(
-                h_flex().p(DynamicSpacing::Base16.rems(cx)).child(
-                    Button::new("open-prompt-library", "Open Prompt Library")
-                        .style(ButtonStyle::Filled)
-                        .full_width()
-                        .icon(IconName::Book)
-                        .icon_size(IconSize::Small)
-                        .icon_position(IconPosition::Start)
-                        .on_click(|_event, _window, cx| cx.dispatch_action(&DeployPromptLibrary)),
-                ),
+                v_flex()
+                    .p(DynamicSpacing::Base16.rems(cx))
+                    .gap_1()
+                    .child(Headline::new("Prompt Library").size(HeadlineSize::Small))
+                    .child(
+                        Button::new("open-prompt-library", "Open Prompt Library")
+                            .style(ButtonStyle::Filled)
+                            .layer(ElevationIndex::ModalSurface)
+                            .full_width()
+                            .icon(IconName::Book)
+                            .icon_size(IconSize::Small)
+                            .icon_position(IconPosition::Start)
+                            .on_click(|_event, _window, cx| {
+                                cx.dispatch_action(&DeployPromptLibrary)
+                            }),
+                    ),
             )
+            .child(Divider::horizontal().color(DividerColor::Border))
             .child(
                 v_flex()
                     .p(DynamicSpacing::Base16.rems(cx))
                     .mt_1()
                     .gap_6()
                     .flex_1()
+                    .child(
+                        v_flex()
+                            .gap_0p5()
+                            .child(Headline::new("LLM Providers").size(HeadlineSize::Small))
+                            .child(
+                                Label::new("Add at least one provider to use AI-powered features.")
+                                    .color(Color::Muted),
+                            ),
+                    )
                     .children(
                         providers
                             .into_iter()

crates/assistant2/src/assistant_panel.rs πŸ”—

@@ -613,7 +613,7 @@ impl AssistantPanel {
                 })
                 .unwrap_or_else(|| SharedString::from("Loading Summary…")),
             ActiveView::History | ActiveView::PromptEditorHistory => "History".into(),
-            ActiveView::Configuration => "Configuration".into(),
+            ActiveView::Configuration => "Assistant Settings".into(),
         };
 
         let sub_title = match self.active_view {
@@ -700,7 +700,7 @@ impl AssistantPanel {
                         IconButton::new("configure-assistant", IconName::Settings)
                             .icon_size(IconSize::Small)
                             .style(ButtonStyle::Subtle)
-                            .tooltip(Tooltip::text("Configure Assistant"))
+                            .tooltip(Tooltip::text("Assistant Settings"))
                             .on_click(move |_event, window, cx| {
                                 window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
                             }),

crates/language_models/src/provider/anthropic.rs πŸ”—

@@ -689,7 +689,7 @@ impl Render for ConfigurationView {
                 .child(h_flex().child(Label::new(INSTRUCTIONS[1])).child(
                     Button::new("anthropic_console", ANTHROPIC_CONSOLE_URL)
                         .style(ButtonStyle::Subtle)
-                        .icon(IconName::ExternalLink)
+                        .icon(IconName::ArrowUpRight)
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
                         .on_click(move |_, _, cx| cx.open_url(ANTHROPIC_CONSOLE_URL))
@@ -703,6 +703,8 @@ impl Render for ConfigurationView {
                         .px_2()
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
+                        .border_1()
+                        .border_color(cx.theme().colors().border_variant)
                         .rounded_md()
                         .child(self.render_api_key_editor(cx)),
                 )

crates/language_models/src/provider/deepseek.rs πŸ”—

@@ -506,7 +506,7 @@ impl Render for ConfigurationView {
                     h_flex().child(Label::new(INSTRUCTIONS[1])).child(
                         Button::new("deepseek_console", DEEPSEEK_CONSOLE_URL)
                             .style(ButtonStyle::Subtle)
-                            .icon(IconName::ExternalLink)
+                            .icon(IconName::ArrowUpRight)
                             .icon_size(IconSize::XSmall)
                             .icon_color(Color::Muted)
                             .on_click(move |_, _window, cx| cx.open_url(DEEPSEEK_CONSOLE_URL)),
@@ -520,12 +520,14 @@ impl Render for ConfigurationView {
                         .px_2()
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
+                        .border_1()
+                        .border_color(cx.theme().colors().border_variant)
                         .rounded_md()
                         .child(self.render_api_key_editor(cx)),
                 )
                 .child(
                     Label::new(format!(
-                        "Or set {} environment variable",
+                        "Or set the {} environment variable.",
                         DEEPSEEK_API_KEY_VAR
                     ))
                     .size(LabelSize::Small),

crates/language_models/src/provider/google.rs πŸ”—

@@ -452,7 +452,7 @@ impl Render for ConfigurationView {
                 .child(h_flex().child(Label::new(INSTRUCTIONS[1])).child(
                     Button::new("google_console", GOOGLE_CONSOLE_URL)
                         .style(ButtonStyle::Subtle)
-                        .icon(IconName::ExternalLink)
+                        .icon(IconName::ArrowUpRight)
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
                         .on_click(move |_, _, cx| cx.open_url(GOOGLE_CONSOLE_URL))
@@ -466,6 +466,8 @@ impl Render for ConfigurationView {
                         .px_2()
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
+                        .border_1()
+                        .border_color(cx.theme().colors().border_variant)
                         .rounded_md()
                         .child(self.render_api_key_editor(cx)),
                 )

crates/language_models/src/provider/lmstudio.rs πŸ”—

@@ -413,8 +413,7 @@ impl Render for ConfigurationView {
         let lmstudio_reqs =
             "To use LM Studio as a provider for Zed assistant, it needs to be running with at least one model downloaded.";
 
-        let mut inline_code_bg = cx.theme().colors().editor_background;
-        inline_code_bg.fade_out(0.5);
+        let inline_code_bg = cx.theme().colors().editor_foreground.opacity(0.05);
 
         if self.loading_models_task.is_some() {
             div().child(Label::new("Loading models...")).into_any()
@@ -432,7 +431,7 @@ impl Render for ConfigurationView {
                         .child(
                             h_flex()
                                 .gap_0p5()
-                                .child(Label::new("To get your first model, try running "))
+                                .child(Label::new("To get your first model, try running"))
                                 .child(
                                     div()
                                         .bg(inline_code_bg)
@@ -457,7 +456,7 @@ impl Render for ConfigurationView {
                                         this.child(
                                             Button::new("lmstudio-site", "LM Studio")
                                                 .style(ButtonStyle::Subtle)
-                                                .icon(IconName::ExternalLink)
+                                                .icon(IconName::ArrowUpRight)
                                                 .icon_size(IconSize::XSmall)
                                                 .icon_color(Color::Muted)
                                                 .on_click(move |_, _window, cx| {
@@ -472,7 +471,7 @@ impl Render for ConfigurationView {
                                                 "Download LM Studio",
                                             )
                                             .style(ButtonStyle::Subtle)
-                                            .icon(IconName::ExternalLink)
+                                            .icon(IconName::ArrowUpRight)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .on_click(move |_, _window, cx| {
@@ -485,7 +484,7 @@ impl Render for ConfigurationView {
                                 .child(
                                     Button::new("view-models", "Model Catalog")
                                         .style(ButtonStyle::Subtle)
-                                        .icon(IconName::ExternalLink)
+                                        .icon(IconName::ArrowUpRight)
                                         .icon_size(IconSize::XSmall)
                                         .icon_color(Color::Muted)
                                         .on_click(move |_, _window, cx| {

crates/language_models/src/provider/ollama.rs πŸ”—

@@ -452,8 +452,7 @@ impl Render for ConfigurationView {
         let ollama_reqs =
             "Ollama must be running with at least one model installed to use it in the assistant.";
 
-        let mut inline_code_bg = cx.theme().colors().editor_background;
-        inline_code_bg.fade_out(0.5);
+        let inline_code_bg = cx.theme().colors().editor_foreground.opacity(0.05);
 
         if self.loading_models_task.is_some() {
             div().child(Label::new("Loading models...")).into_any()
@@ -496,7 +495,7 @@ impl Render for ConfigurationView {
                                         this.child(
                                             Button::new("ollama-site", "Ollama")
                                                 .style(ButtonStyle::Subtle)
-                                                .icon(IconName::ExternalLink)
+                                                .icon(IconName::ArrowUpRight)
                                                 .icon_size(IconSize::XSmall)
                                                 .icon_color(Color::Muted)
                                                 .on_click(move |_, _, cx| cx.open_url(OLLAMA_SITE))
@@ -509,7 +508,7 @@ impl Render for ConfigurationView {
                                                 "Download Ollama",
                                             )
                                             .style(ButtonStyle::Subtle)
-                                            .icon(IconName::ExternalLink)
+                                            .icon(IconName::ArrowUpRight)
                                             .icon_size(IconSize::XSmall)
                                             .icon_color(Color::Muted)
                                             .on_click(move |_, _, cx| {
@@ -522,7 +521,7 @@ impl Render for ConfigurationView {
                                 .child(
                                     Button::new("view-models", "All Models")
                                         .style(ButtonStyle::Subtle)
-                                        .icon(IconName::ExternalLink)
+                                        .icon(IconName::ArrowUpRight)
                                         .icon_size(IconSize::XSmall)
                                         .icon_color(Color::Muted)
                                         .on_click(move |_, _, cx| cx.open_url(OLLAMA_LIBRARY_URL)),

crates/language_models/src/provider/open_ai.rs πŸ”—

@@ -502,7 +502,7 @@ impl Render for ConfigurationView {
                 .child(h_flex().child(Label::new(INSTRUCTIONS[1])).child(
                     Button::new("openai_console", OPENAI_CONSOLE_URL)
                         .style(ButtonStyle::Subtle)
-                        .icon(IconName::ExternalLink)
+                        .icon(IconName::ArrowUpRight)
                         .icon_size(IconSize::XSmall)
                         .icon_color(Color::Muted)
                         .on_click(move |_, _, cx| cx.open_url(OPENAI_CONSOLE_URL))
@@ -518,6 +518,8 @@ impl Render for ConfigurationView {
                         .px_2()
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
+                        .border_1()
+                        .border_color(cx.theme().colors().border_variant)
                         .rounded_md()
                         .child(self.render_api_key_editor(cx)),
                 )