From e27f6a984f1f8c10af1945e7d84e54980de115c8 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:24:53 -0300 Subject: [PATCH] agent: Simplify design of the settings view (#29041) 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. Release Notes: - N/A --- crates/agent/src/assistant_configuration.rs | 76 +++++++---------- .../language_models/src/provider/anthropic.rs | 12 ++- .../language_models/src/provider/bedrock.rs | 45 +++++----- .../src/provider/copilot_chat.rs | 83 +++++++----------- .../language_models/src/provider/deepseek.rs | 16 +++- crates/language_models/src/provider/google.rs | 12 ++- .../language_models/src/provider/lmstudio.rs | 83 ++++++++---------- .../language_models/src/provider/mistral.rs | 12 ++- crates/language_models/src/provider/ollama.rs | 85 ++++++++----------- .../language_models/src/provider/open_ai.rs | 12 ++- crates/markdown/src/markdown.rs | 5 -- 11 files changed, 205 insertions(+), 236 deletions(-) diff --git a/crates/agent/src/assistant_configuration.rs b/crates/agent/src/assistant_configuration.rs index 18dc4b5e21..33b97a771e 100644 --- a/crates/agent/src/assistant_configuration.rs +++ b/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) -> 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( diff --git a/crates/language_models/src/provider/anthropic.rs b/crates/language_models/src/provider/anthropic.rs index ee0f941afa..da796a3f2b 100644 --- a/crates/language_models/src/provider/anthropic.rs +++ b/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) diff --git a/crates/language_models/src/provider/bedrock.rs b/crates/language_models/src/provider/bedrock.rs index aa603534e0..48b7e62757 100644 --- a/crates/language_models/src/provider/bedrock.rs +++ b/crates/language_models/src/provider/bedrock.rs @@ -1145,7 +1145,7 @@ impl ConfigurationView { fn make_input_styles(&self, cx: &Context) -> 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) -> 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() } diff --git a/crates/language_models/src/provider/copilot_chat.rs b/crates/language_models/src/provider/copilot_chat.rs index 3951e69d5d..39c005babc 100644 --- a/crates/language_models/src/provider/copilot_chat.rs +++ b/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) -> impl IntoElement { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> 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)), ) } }, diff --git a/crates/language_models/src/provider/deepseek.rs b/crates/language_models/src/provider/deepseek.rs index 6c6a8f333c..e4f1cd830a 100644 --- a/crates/language_models/src/provider/deepseek.rs +++ b/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)), diff --git a/crates/language_models/src/provider/google.rs b/crates/language_models/src/provider/google.rs index edf187fdbf..bec5a8768d 100644 --- a/crates/language_models/src/provider/google.rs +++ b/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) diff --git a/crates/language_models/src/provider/lmstudio.rs b/crates/language_models/src/provider/lmstudio.rs index d59ed46e05..bd8b6303f8 100644 --- a/crates/language_models/src/provider/lmstudio.rs +++ b/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(), + ), ) - .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() + } 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() diff --git a/crates/language_models/src/provider/mistral.rs b/crates/language_models/src/provider/mistral.rs index fca0b61698..b9017398e6 100644 --- a/crates/language_models/src/provider/mistral.rs +++ b/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) diff --git a/crates/language_models/src/provider/ollama.rs b/crates/language_models/src/provider/ollama.rs index e24c89c239..465dc0d659 100644 --- a/crates/language_models/src/provider/ollama.rs +++ b/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) -> 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() } diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 774f1f7b2a..6b14744a30 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/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) diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 68b3788966..0c1539824e 100644 --- a/crates/markdown/src/markdown.rs +++ b/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 {