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. <img src="https://github.com/user-attachments/assets/f826a7a8-beaf-45d0-9dc2-36dc210c418e" width="700"/> Release Notes: - N/A
This commit is contained in:
parent
cce661b64b
commit
e27f6a984f
11 changed files with 205 additions and 236 deletions
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue