agent: Simplify design of the settings view (#29041)

Danilo Leal created

Containing everything in boxes wasn't super necessary here. Want to
still improve the switch color contrast here, but will probably do that
in a separate PR.

<img
src="https://github.com/user-attachments/assets/f826a7a8-beaf-45d0-9dc2-36dc210c418e"
width="700"/>

Release Notes:

- N/A

Change summary

crates/agent/src/assistant_configuration.rs         | 76 +++++--------
crates/language_models/src/provider/anthropic.rs    | 12 +
crates/language_models/src/provider/bedrock.rs      | 45 ++++---
crates/language_models/src/provider/copilot_chat.rs | 83 +++++---------
crates/language_models/src/provider/deepseek.rs     | 16 ++
crates/language_models/src/provider/google.rs       | 12 +
crates/language_models/src/provider/lmstudio.rs     | 83 ++++++--------
crates/language_models/src/provider/mistral.rs      | 12 +
crates/language_models/src/provider/ollama.rs       | 85 ++++++--------
crates/language_models/src/provider/open_ai.rs      | 12 +
crates/markdown/src/markdown.rs                     |  5 
11 files changed, 205 insertions(+), 236 deletions(-)

Detailed changes

crates/agent/src/assistant_configuration.rs 🔗

@@ -132,7 +132,11 @@ impl AssistantConfiguration {
             .cloned();
 
         v_flex()
+            .pt_3()
+            .pb_1()
             .gap_1p5()
+            .border_t_1()
+            .border_color(cx.theme().colors().border.opacity(0.6))
             .child(
                 h_flex()
                     .justify_between()
@@ -144,7 +148,7 @@ impl AssistantConfiguration {
                                     .size(IconSize::Small)
                                     .color(Color::Muted),
                             )
-                            .child(Label::new(provider_name.clone())),
+                            .child(Label::new(provider_name.clone()).size(LabelSize::Large)),
                     )
                     .when(provider.is_authenticated(cx), |parent| {
                         parent.child(
@@ -169,20 +173,12 @@ impl AssistantConfiguration {
                         )
                     }),
             )
-            .child(
-                div()
-                    .p(DynamicSpacing::Base08.rems(cx))
-                    .bg(cx.theme().colors().editor_background)
-                    .border_1()
-                    .border_color(cx.theme().colors().border)
-                    .rounded_sm()
-                    .map(|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}",
-                        )))),
-                    }),
-            )
+            .map(|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}",
+                )))),
+            })
     }
 
     fn render_provider_configuration_section(
@@ -199,7 +195,7 @@ impl AssistantConfiguration {
             .child(
                 v_flex()
                     .gap_0p5()
-                    .child(Headline::new("LLM Providers").size(HeadlineSize::Small))
+                    .child(Headline::new("LLM Providers"))
                     .child(
                         Label::new("Add at least one provider to use AI-powered features.")
                             .color(Color::Muted),
@@ -215,21 +211,16 @@ impl AssistantConfiguration {
     fn render_command_permission(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
         let always_allow_tool_actions = AssistantSettings::get_global(cx).always_allow_tool_actions;
 
-        const HEADING: &str = "Allow running tools without asking for confirmation";
+        const HEADING: &str = "Allow running editing tools without asking for confirmation";
 
         v_flex()
             .p(DynamicSpacing::Base16.rems(cx))
             .pr(DynamicSpacing::Base20.rems(cx))
             .gap_2()
             .flex_1()
-            .child(Headline::new("General Settings").size(HeadlineSize::Small))
+            .child(Headline::new("General Settings"))
             .child(
                 h_flex()
-                    .p_2p5()
-                    .rounded_sm()
-                    .bg(cx.theme().colors().editor_background)
-                    .border_1()
-                    .border_color(cx.theme().colors().border)
                     .gap_4()
                     .justify_between()
                     .flex_wrap()
@@ -277,10 +268,7 @@ impl AssistantConfiguration {
             .child(
                 v_flex()
                     .gap_0p5()
-                    .child(
-                        Headline::new("Model Context Protocol (MCP) Servers")
-                            .size(HeadlineSize::Small),
-                    )
+                    .child(Headline::new("Model Context Protocol (MCP) Servers"))
                     .child(Label::new(SUBHEADING).color(Color::Muted)),
             )
             .children(context_servers.into_iter().map(|context_server| {
@@ -301,9 +289,9 @@ impl AssistantConfiguration {
                 v_flex()
                     .id(SharedString::from(context_server.id()))
                     .border_1()
-                    .rounded_sm()
+                    .rounded_md()
                     .border_color(cx.theme().colors().border)
-                    .bg(cx.theme().colors().editor_background)
+                    .bg(cx.theme().colors().background.opacity(0.25))
                     .child(
                         h_flex()
                             .p_1()
@@ -386,34 +374,28 @@ impl AssistantConfiguration {
                             return parent;
                         }
 
-                        parent.child(v_flex().children(tools.into_iter().enumerate().map(
-                            |(ix, tool)| {
+                        parent.child(v_flex().py_1p5().px_1().gap_1().children(
+                            tools.into_iter().enumerate().map(|(ix, tool)| {
                                 h_flex()
-                                    .id("tool-item")
-                                    .pl_2()
-                                    .pr_1()
-                                    .py_1()
+                                    .id(("tool-item", ix))
+                                    .px_1()
                                     .gap_2()
                                     .justify_between()
-                                    .when(ix < tool_count - 1, |element| {
-                                        element
-                                            .border_b_1()
-                                            .border_color(cx.theme().colors().border_variant)
-                                    })
+                                    .hover(|style| style.bg(cx.theme().colors().element_hover))
+                                    .rounded_sm()
                                     .child(
                                         Label::new(tool.name())
                                             .buffer_font(cx)
                                             .size(LabelSize::Small),
                                     )
                                     .child(
-                                        IconButton::new(("tool-description", ix), IconName::Info)
-                                            .shape(ui::IconButtonShape::Square)
-                                            .icon_size(IconSize::Small)
-                                            .icon_color(Color::Ignored)
-                                            .tooltip(Tooltip::text(tool.description())),
+                                        Icon::new(IconName::Info)
+                                            .size(IconSize::Small)
+                                            .color(Color::Ignored),
                                     )
-                            },
-                        )))
+                                    .tooltip(Tooltip::text(tool.description()))
+                            }),
+                        ))
                     })
             }))
             .child(

crates/language_models/src/provider/anthropic.rs 🔗

@@ -934,7 +934,7 @@ impl Render for ConfigurationView {
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
                         .border_1()
-                        .border_color(cx.theme().colors().border_variant)
+                        .border_color(cx.theme().colors().border)
                         .rounded_sm()
                         .child(self.render_api_key_editor(cx)),
                 )
@@ -948,8 +948,13 @@ impl Render for ConfigurationView {
                 .into_any()
         } else {
             h_flex()
-                .size_full()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
@@ -961,7 +966,8 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset key")
+                    Button::new("reset-key", "Reset Key")
+                        .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)

crates/language_models/src/provider/bedrock.rs 🔗

@@ -1145,7 +1145,7 @@ impl ConfigurationView {
 
     fn make_input_styles(&self, cx: &Context<Self>) -> Div {
         let bg_color = cx.theme().colors().editor_background;
-        let border_color = cx.theme().colors().border_variant;
+        let border_color = cx.theme().colors().border;
 
         h_flex()
             .w_full()
@@ -1173,8 +1173,13 @@ impl Render for ConfigurationView {
 
         if let Some(auth) = self.should_render_editor(cx) {
             return h_flex()
-                .size_full()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
@@ -1186,16 +1191,16 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset key")
+                    Button::new("reset-key", "Reset Key")
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)
-                        .disabled(env_var_set || creds_type)
+                        // .disabled(env_var_set || creds_type)
                         .when(env_var_set, |this| {
                             this.tooltip(Tooltip::text(format!("To reset your credentials, unset the {ZED_BEDROCK_ACCESS_KEY_ID_VAR}, {ZED_BEDROCK_SECRET_ACCESS_KEY_VAR}, and {ZED_BEDROCK_REGION_VAR} environment variables.")))
                         })
                         .when(creds_type, |this| {
-                            this.tooltip(Tooltip::text("You cannot reset credentials as they're being derived, check Zed settings to understand how"))
+                            this.tooltip(Tooltip::text("You cannot reset credentials as they're being derived, check Zed settings to understand how."))
                         })
                         .on_click(cx.listener(|this, _, window, cx| this.reset_credentials(window, cx))),
                 )
@@ -1206,19 +1211,19 @@ impl Render for ConfigurationView {
             .size_full()
             .on_action(cx.listener(ConfigurationView::save_credentials))
             .child(Label::new("To use Zed's assistant with Bedrock, you can set a custom authentication strategy through the settings.json, or use static credentials."))
-            .child(Label::new("Though to access models on AWS first, you will have to: "))
+            .child(Label::new("But, to access models on AWS, you need to:").mt_1())
             .child(
                 List::new()
                     .child(
                         InstructionListItem::new(
-                            "Grant permissions to the strategy you plan to use according to this documentation: ",
+                            "Grant permissions to the strategy you'll use according to the:",
                             Some("Prerequisites"),
                             Some("https://docs.aws.amazon.com/bedrock/latest/userguide/inference-prereq.html"),
                         )
                     )
                     .child(
                         InstructionListItem::new(
-                            "Select the models you would like access to: ",
+                            "Select the models you would like access to:",
                             Some("Bedrock Model Catalog"),
                             Some("https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/modelaccess"),
                         )
@@ -1228,7 +1233,15 @@ impl Render for ConfigurationView {
             .child(self.render_common_fields(cx))
             .child(
                 Label::new(
-                    format!("You can also assign the {ZED_BEDROCK_ACCESS_KEY_ID_VAR}, {ZED_BEDROCK_SECRET_ACCESS_KEY_VAR} AND {ZED_BEDROCK_REGION_VAR} environment variables and restart Zed.\n Optionally, if your environment uses AWS CLI profiles, you can set {ZED_AWS_PROFILE_VAR}; if it requires a custom endpoint, you can set {ZED_AWS_ENDPOINT_VAR}; and if it requires a Session Token, you can set {ZED_BEDROCK_SESSION_TOKEN_VAR}."),
+                    format!("You can also assign the {ZED_BEDROCK_ACCESS_KEY_ID_VAR}, {ZED_BEDROCK_SECRET_ACCESS_KEY_VAR} AND {ZED_BEDROCK_REGION_VAR} environment variables and restart Zed."),
+                )
+                    .size(LabelSize::Small)
+                    .color(Color::Muted)
+                    .my_1(),
+            )
+            .child(
+                Label::new(
+                    format!("Optionally, if your environment uses AWS CLI profiles, you can set {ZED_AWS_PROFILE_VAR}; if it requires a custom endpoint, you can set {ZED_AWS_ENDPOINT_VAR}; and if it requires a Session Token, you can set {ZED_BEDROCK_SESSION_TOKEN_VAR}."),
                 )
                     .size(LabelSize::Small)
                     .color(Color::Muted),
@@ -1307,7 +1320,6 @@ impl ConfigurationView {
                 Label::new(
                     "This method uses your AWS access key ID and secret access key directly.",
                 )
-                    .size(LabelSize::Small),
             )
             .child(
                 List::new()
@@ -1357,16 +1369,11 @@ impl ConfigurationView {
 
     fn render_common_fields(&self, cx: &mut Context<Self>) -> AnyElement {
         v_flex()
-            .my_2()
-            .gap_1p5()
+            .gap_0p5()
+            .child(Label::new("Region").size(LabelSize::Small))
             .child(
-                v_flex()
-                    .gap_0p5()
-                    .child(Label::new("Region").size(LabelSize::Small))
-                    .child(
-                        self.make_input_styles(cx)
-                            .child(self.render_region_editor(cx)),
-                    ),
+                self.make_input_styles(cx)
+                    .child(self.render_region_editor(cx)),
             )
             .into_any_element()
     }

crates/language_models/src/provider/copilot_chat.rs 🔗

@@ -530,60 +530,51 @@ impl ConfigurationView {
 }
 
 impl Render for ConfigurationView {
-    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         if self.state.read(cx).is_authenticated(cx) {
-            const LABEL: &str = "Authorized.";
             h_flex()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
                         .child(Icon::new(IconName::Check).color(Color::Success))
-                        .child(Label::new(LABEL)),
+                        .child(Label::new("Authorized")),
                 )
                 .child(
                     Button::new("sign_out", "Sign Out")
-                        .style(ui::ButtonStyle::Filled)
+                        .label_size(LabelSize::Small)
                         .on_click(|_, window, cx| {
                             window.dispatch_action(copilot::SignOut.boxed_clone(), cx);
                         }),
                 )
         } else {
-            let loading_icon = svg()
-                .size_8()
-                .path(IconName::ArrowCircle.path())
-                .text_color(window.text_style().color)
-                .with_animation(
-                    "icon_circle_arrow",
-                    Animation::new(Duration::from_secs(2)).repeat(),
-                    |svg, delta| svg.with_transformation(Transformation::rotate(percentage(delta))),
-                );
+            let loading_icon = Icon::new(IconName::ArrowCircle).with_animation(
+                "arrow-circle",
+                Animation::new(Duration::from_secs(4)).repeat(),
+                |icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
+            );
 
             const ERROR_LABEL: &str = "Copilot Chat requires an active GitHub Copilot subscription. Please ensure Copilot is configured and try again, or use a different Assistant provider.";
 
             match &self.copilot_status {
                 Some(status) => match status {
-                    Status::Starting { task: _ } => {
-                        const LABEL: &str = "Starting Copilot...";
-                        v_flex()
-                            .gap_6()
-                            .justify_center()
-                            .items_center()
-                            .child(Label::new(LABEL))
-                            .child(loading_icon)
-                    }
+                    Status::Starting { task: _ } => h_flex()
+                        .gap_2()
+                        .child(loading_icon)
+                        .child(Label::new("Starting Copilot…")),
                     Status::SigningIn { prompt: _ }
                     | Status::SignedOut {
                         awaiting_signing_in: true,
-                    } => {
-                        const LABEL: &str = "Signing in to Copilot...";
-                        v_flex()
-                            .gap_6()
-                            .justify_center()
-                            .items_center()
-                            .child(Label::new(LABEL))
-                            .child(loading_icon)
-                    }
+                    } => h_flex()
+                        .gap_2()
+                        .child(loading_icon)
+                        .child(Label::new("Signing into Copilot…")),
                     Status::Error(_) => {
                         const LABEL: &str = "Copilot had issues starting. Please try restarting it. If the issue persists, try reinstalling Copilot.";
                         v_flex()
@@ -593,28 +584,14 @@ impl Render for ConfigurationView {
                     }
                     _ => {
                         const LABEL: &str = "To use Zed's assistant with GitHub Copilot, you need to be logged in to GitHub. Note that your GitHub account must have an active Copilot Chat subscription.";
-                        v_flex().gap_6().child(Label::new(LABEL)).child(
-                            v_flex()
-                                .gap_2()
-                                .child(
-                                    Button::new("sign_in", "Sign In")
-                                        .icon_color(Color::Muted)
-                                        .icon(IconName::Github)
-                                        .icon_position(IconPosition::Start)
-                                        .icon_size(IconSize::Medium)
-                                        .style(ui::ButtonStyle::Filled)
-                                        .full_width()
-                                        .on_click(|_, window, cx| {
-                                            copilot::initiate_sign_in(window, cx)
-                                        }),
-                                )
-                                .child(
-                                    div().flex().w_full().items_center().child(
-                                        Label::new("Sign in to start using Github Copilot Chat.")
-                                            .color(Color::Muted)
-                                            .size(ui::LabelSize::Small),
-                                    ),
-                                ),
+                        v_flex().gap_2().child(Label::new(LABEL)).child(
+                            Button::new("sign_in", "Sign in to use GitHub Copilot")
+                                .icon_color(Color::Muted)
+                                .icon(IconName::Github)
+                                .icon_position(IconPosition::Start)
+                                .icon_size(IconSize::Medium)
+                                .full_width()
+                                .on_click(|_, window, cx| copilot::initiate_sign_in(window, cx)),
                         )
                     }
                 },

crates/language_models/src/provider/deepseek.rs 🔗

@@ -580,7 +580,7 @@ impl Render for ConfigurationView {
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
                         .border_1()
-                        .border_color(cx.theme().colors().border_variant)
+                        .border_color(cx.theme().colors().border)
                         .rounded_sm()
                         .child(self.render_api_key_editor(cx)),
                 )
@@ -595,8 +595,13 @@ impl Render for ConfigurationView {
                 .into_any()
         } else {
             h_flex()
-                .size_full()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
@@ -608,8 +613,11 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset")
-                        .icon(IconName::Trash)
+                    Button::new("reset-key", "Reset Key")
+                        .label_size(LabelSize::Small)
+                        .icon(Some(IconName::Trash))
+                        .icon_size(IconSize::Small)
+                        .icon_position(IconPosition::Start)
                         .disabled(env_var_set)
                         .on_click(
                             cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)),

crates/language_models/src/provider/google.rs 🔗

@@ -740,7 +740,7 @@ impl Render for ConfigurationView {
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
                         .border_1()
-                        .border_color(cx.theme().colors().border_variant)
+                        .border_color(cx.theme().colors().border)
                         .rounded_sm()
                         .child(self.render_api_key_editor(cx)),
                 )
@@ -753,8 +753,13 @@ impl Render for ConfigurationView {
                 .into_any()
         } else {
             h_flex()
-                .size_full()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
@@ -766,7 +771,8 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset key")
+                    Button::new("reset-key", "Reset Key")
+                        .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)

crates/language_models/src/provider/lmstudio.rs 🔗

@@ -16,10 +16,11 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use std::{collections::BTreeMap, sync::Arc};
-use ui::{ButtonLike, Indicator, prelude::*};
+use ui::{ButtonLike, Indicator, List, prelude::*};
 use util::ResultExt;
 
 use crate::AllLanguageModelSettings;
+use crate::ui::InstructionListItem;
 
 const LMSTUDIO_DOWNLOAD_URL: &str = "https://lmstudio.ai/download";
 const LMSTUDIO_CATALOG_URL: &str = "https://lmstudio.ai/models";
@@ -408,40 +409,26 @@ impl Render for ConfigurationView {
         let is_authenticated = self.state.read(cx).is_authenticated();
 
         let lmstudio_intro = "Run local LLMs like Llama, Phi, and Qwen.";
-        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 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()
         } else {
             v_flex()
-                .size_full()
-                .gap_3()
+                .gap_2()
                 .child(
-                    v_flex()
-                        .size_full()
-                        .gap_2()
-                        .p_1()
-                        .child(Label::new(lmstudio_intro))
-                        .child(Label::new(lmstudio_reqs))
-                        .child(
-                            h_flex()
-                                .gap_0p5()
-                                .child(Label::new("To get your first model, try running"))
-                                .child(
-                                    div()
-                                        .bg(inline_code_bg)
-                                        .px_1p5()
-                                        .rounded_sm()
-                                        .child(Label::new("lms get qwen2.5-coder-7b")),
-                                ),
-                        ),
+                    v_flex().gap_1().child(Label::new(lmstudio_intro)).child(
+                        List::new()
+                            .child(InstructionListItem::text_only(
+                                "LM Studio needs to be running with at least one model downloaded.",
+                            ))
+                            .child(InstructionListItem::text_only(
+                                "To get your first model, try running `lms get qwen2.5-coder-7b`",
+                            )),
+                    ),
                 )
                 .child(
                     h_flex()
                         .w_full()
-                        .pt_2()
                         .justify_between()
                         .gap_2()
                         .child(
@@ -489,29 +476,31 @@ impl Render for ConfigurationView {
                                         }),
                                 ),
                         )
-                        .child(if is_authenticated {
-                            // This is only a button to ensure the spacing is correct
-                            // it should stay disabled
-                            ButtonLike::new("connected")
-                                .disabled(true)
-                                // Since this won't ever be clickable, we can use the arrow cursor
-                                .cursor_style(gpui::CursorStyle::Arrow)
-                                .child(
-                                    h_flex()
-                                        .gap_2()
-                                        .child(Indicator::dot().color(Color::Success))
-                                        .child(Label::new("Connected"))
-                                        .into_any_element(),
+                        .map(|this| {
+                            if is_authenticated {
+                                this.child(
+                                    ButtonLike::new("connected")
+                                        .disabled(true)
+                                        .cursor_style(gpui::CursorStyle::Arrow)
+                                        .child(
+                                            h_flex()
+                                                .gap_2()
+                                                .child(Indicator::dot().color(Color::Success))
+                                                .child(Label::new("Connected"))
+                                                .into_any_element(),
+                                        ),
+                                )
+                            } else {
+                                this.child(
+                                    Button::new("retry_lmstudio_models", "Connect")
+                                        .icon_position(IconPosition::Start)
+                                        .icon_size(IconSize::XSmall)
+                                        .icon(IconName::Play)
+                                        .on_click(cx.listener(move |this, _, _window, cx| {
+                                            this.retry_connection(cx)
+                                        })),
                                 )
-                                .into_any_element()
-                        } else {
-                            Button::new("retry_lmstudio_models", "Connect")
-                                .icon_position(IconPosition::Start)
-                                .icon(IconName::ArrowCircle)
-                                .on_click(cx.listener(move |this, _, _window, cx| {
-                                    this.retry_connection(cx)
-                                }))
-                                .into_any_element()
+                            }
                         }),
                 )
                 .into_any()

crates/language_models/src/provider/mistral.rs 🔗

@@ -554,7 +554,7 @@ impl Render for ConfigurationView {
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
                         .border_1()
-                        .border_color(cx.theme().colors().border_variant)
+                        .border_color(cx.theme().colors().border)
                         .rounded_sm()
                         .child(self.render_api_key_editor(cx)),
                 )
@@ -567,8 +567,13 @@ impl Render for ConfigurationView {
                 .into_any()
         } else {
             h_flex()
-                .size_full()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
@@ -580,7 +585,8 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset key")
+                    Button::new("reset-key", "Reset Key")
+                        .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)

crates/language_models/src/provider/ollama.rs 🔗

@@ -16,10 +16,11 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use std::{collections::BTreeMap, sync::Arc};
-use ui::{ButtonLike, Indicator, prelude::*};
+use ui::{ButtonLike, Indicator, List, prelude::*};
 use util::ResultExt;
 
 use crate::AllLanguageModelSettings;
+use crate::ui::InstructionListItem;
 
 const OLLAMA_DOWNLOAD_URL: &str = "https://ollama.com/download";
 const OLLAMA_LIBRARY_URL: &str = "https://ollama.com/library";
@@ -399,42 +400,26 @@ impl Render for ConfigurationView {
     fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let is_authenticated = self.state.read(cx).is_authenticated();
 
-        let ollama_intro = "Get up and running with Llama 3.3, Mistral, Gemma 2, and other large language models with Ollama.";
-        let ollama_reqs =
-            "Ollama must be running with at least one model installed to use it in the assistant.";
-
-        let inline_code_bg = cx.theme().colors().editor_foreground.opacity(0.05);
+        let ollama_intro =
+            "Get up & running with Llama 3.3, Mistral, Gemma 2, and other LLMs with Ollama.";
 
         if self.loading_models_task.is_some() {
             div().child(Label::new("Loading models...")).into_any()
         } else {
             v_flex()
-                .size_full()
-                .gap_3()
+                .gap_2()
                 .child(
-                    v_flex()
-                        .size_full()
-                        .gap_2()
-                        .p_1()
-                        .child(Label::new(ollama_intro))
-                        .child(Label::new(ollama_reqs))
-                        .child(
-                            h_flex()
-                                .gap_0p5()
-                                .child(Label::new("Once installed, try "))
-                                .child(
-                                    div()
-                                        .bg(inline_code_bg)
-                                        .px_1p5()
-                                        .rounded_sm()
-                                        .child(Label::new("ollama run llama3.2")),
-                                ),
-                        ),
+                    v_flex().gap_1().child(Label::new(ollama_intro)).child(
+                        List::new()
+                            .child(InstructionListItem::text_only("Ollama must be running with at least one model installed to use it in the assistant."))
+                            .child(InstructionListItem::text_only(
+                                "Once installed, try `ollama run llama3.2`",
+                            )),
+                    ),
                 )
                 .child(
                     h_flex()
                         .w_full()
-                        .pt_2()
                         .justify_between()
                         .gap_2()
                         .child(
@@ -478,30 +463,32 @@ impl Render for ConfigurationView {
                                         .on_click(move |_, _, cx| cx.open_url(OLLAMA_LIBRARY_URL)),
                                 ),
                         )
-                        .child(if is_authenticated {
-                            // This is only a button to ensure the spacing is correct
-                            // it should stay disabled
-                            ButtonLike::new("connected")
-                                .disabled(true)
-                                // Since this won't ever be clickable, we can use the arrow cursor
-                                .cursor_style(gpui::CursorStyle::Arrow)
-                                .child(
-                                    h_flex()
-                                        .gap_2()
-                                        .child(Indicator::dot().color(Color::Success))
-                                        .child(Label::new("Connected"))
-                                        .into_any_element(),
+                        .map(|this| {
+                            if is_authenticated {
+                                this.child(
+                                    ButtonLike::new("connected")
+                                        .disabled(true)
+                                        .cursor_style(gpui::CursorStyle::Arrow)
+                                        .child(
+                                            h_flex()
+                                                .gap_2()
+                                                .child(Indicator::dot().color(Color::Success))
+                                                .child(Label::new("Connected"))
+                                                .into_any_element(),
+                                        ),
                                 )
-                                .into_any_element()
-                        } else {
-                            Button::new("retry_ollama_models", "Connect")
-                                .icon_position(IconPosition::Start)
-                                .icon(IconName::ArrowCircle)
-                                .on_click(
-                                    cx.listener(move |this, _, _, cx| this.retry_connection(cx)),
+                            } else {
+                                this.child(
+                                    Button::new("retry_ollama_models", "Connect")
+                                        .icon_position(IconPosition::Start)
+                                        .icon_size(IconSize::XSmall)
+                                        .icon(IconName::Play)
+                                        .on_click(cx.listener(move |this, _, _, cx| {
+                                            this.retry_connection(cx)
+                                        })),
                                 )
-                                .into_any_element()
-                        }),
+                            }
+                        })
                 )
                 .into_any()
         }

crates/language_models/src/provider/open_ai.rs 🔗

@@ -693,7 +693,7 @@ impl Render for ConfigurationView {
                         .py_1()
                         .bg(cx.theme().colors().editor_background)
                         .border_1()
-                        .border_color(cx.theme().colors().border_variant)
+                        .border_color(cx.theme().colors().border)
                         .rounded_sm()
                         .child(self.render_api_key_editor(cx)),
                 )
@@ -712,8 +712,13 @@ impl Render for ConfigurationView {
                 .into_any()
         } else {
             h_flex()
-                .size_full()
+                .mt_1()
+                .p_1()
                 .justify_between()
+                .rounded_md()
+                .border_1()
+                .border_color(cx.theme().colors().border)
+                .bg(cx.theme().colors().background)
                 .child(
                     h_flex()
                         .gap_1()
@@ -725,7 +730,8 @@ impl Render for ConfigurationView {
                         })),
                 )
                 .child(
-                    Button::new("reset-key", "Reset key")
+                    Button::new("reset-key", "Reset Key")
+                        .label_size(LabelSize::Small)
                         .icon(Some(IconName::Trash))
                         .icon_size(IconSize::Small)
                         .icon_position(IconPosition::Start)

crates/markdown/src/markdown.rs 🔗

@@ -753,11 +753,6 @@ impl Element for MarkdownElement {
                                             }
                                         });
 
-                                    // Apply border if required
-                                    // Usage examples:
-                                    // CodeBlockRenderer::Default { copy_button: true, border: true } - Both copy button and border
-                                    // CodeBlockRenderer::Default { copy_button: false, border: true } - Border only
-                                    // CodeBlockRenderer::Default { copy_button: true, border: false } - Copy button only (default)
                                     if let CodeBlockRenderer::Default { border: true, .. } =
                                         &self.code_block_renderer
                                     {