Detailed changes
@@ -20,13 +20,13 @@ use std::pin::Pin;
use std::str::FromStr;
use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
-use ui::{Icon, IconName, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
use crate::api_key::ApiKeyState;
-use crate::ui::InstructionListItem;
+use crate::ui::{ConfiguredApiCard, InstructionListItem};
pub use settings::AnthropicAvailableModel as AvailableModel;
@@ -909,9 +909,21 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = AnthropicLanguageModelProvider::api_url(cx);
+ if api_url == ANTHROPIC_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
if self.load_credentials_task.is_some() {
- div().child(Label::new("Loading credentials...")).into_any()
+ div()
+ .child(Label::new("Loading credentials..."))
+ .into_any_element()
} else if self.should_render_editor(cx) {
v_flex()
.size_full()
@@ -941,56 +953,17 @@ impl Render for ConfigurationView {
.size(LabelSize::Small)
.color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(Label::new(if env_var_set {
- format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
- } else {
- let api_url = AnthropicLanguageModelProvider::api_url(cx);
- if api_url == ANTHROPIC_API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }))
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- 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)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!(
+ "To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable."
+ ))
+ })
+ .into_any_element()
}
}
}
@@ -2,7 +2,7 @@ use std::pin::Pin;
use std::str::FromStr;
use std::sync::Arc;
-use crate::ui::InstructionListItem;
+use crate::ui::{ConfiguredApiCard, InstructionListItem};
use anyhow::{Context as _, Result, anyhow};
use aws_config::stalled_stream_protection::StalledStreamProtectionConfig;
use aws_config::{BehaviorVersion, Region};
@@ -41,7 +41,7 @@ use serde_json::Value;
use settings::{BedrockAvailableModel as AvailableModel, Settings, SettingsStore};
use smol::lock::OnceCell;
use strum::{EnumIter, IntoEnumIterator, IntoStaticStr};
-use ui::{Icon, IconName, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
@@ -1155,47 +1155,37 @@ impl Render for ConfigurationView {
return div().child(Label::new("Loading credentials...")).into_any();
}
+ let configured_label = if env_var_set {
+ format!(
+ "Access Key ID is set in {ZED_BEDROCK_ACCESS_KEY_ID_VAR}, Secret Key is set in {ZED_BEDROCK_SECRET_ACCESS_KEY_VAR}, Region is set in {ZED_BEDROCK_REGION_VAR} environment variables."
+ )
+ } else {
+ match bedrock_method {
+ Some(BedrockAuthMethod::Automatic) => "You are using automatic credentials.".into(),
+ Some(BedrockAuthMethod::NamedProfile) => "You are using named profile.".into(),
+ Some(BedrockAuthMethod::SingleSignOn) => {
+ "You are using a single sign on profile.".into()
+ }
+ None => "You are using static credentials.".into(),
+ }
+ };
+
+ let tooltip_label = if env_var_set {
+ Some(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."
+ ))
+ } else if bedrock_method.is_some() {
+ Some("You cannot reset credentials as they're being derived, check Zed settings to understand how.".to_string())
+ } else {
+ None
+ };
+
if self.should_render_editor(cx) {
- return 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(if env_var_set {
- format!("Access Key ID is set in {ZED_BEDROCK_ACCESS_KEY_ID_VAR}, Secret Key is set in {ZED_BEDROCK_SECRET_ACCESS_KEY_VAR}, Region is set in {ZED_BEDROCK_REGION_VAR} environment variables.")
- } else {
- match bedrock_method {
- Some(BedrockAuthMethod::Automatic) => "You are using automatic credentials".into(),
- Some(BedrockAuthMethod::NamedProfile) => {
- "You are using named profile".into()
- },
- Some(BedrockAuthMethod::SingleSignOn) => "You are using a single sign on profile".into(),
- None => "You are using static credentials".into(),
- }
- })),
- )
- .child(
- Button::new("reset-key", "Reset Key")
- .icon(Some(IconName::Trash))
- .icon_size(IconSize::Small)
- .icon_position(IconPosition::Start)
- .disabled(env_var_set || bedrock_method.is_some())
- .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(bedrock_method.is_some(), |this| {
- 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))),
- )
- .into_any();
+ return ConfiguredApiCard::new(configured_label)
+ .disabled(env_var_set || bedrock_method.is_some())
+ .on_click(cx.listener(|this, _, window, cx| this.reset_credentials(window, cx)))
+ .when_some(tooltip_label, |this, label| this.tooltip_label(label))
+ .into_any_element();
}
v_flex()
@@ -1241,7 +1231,7 @@ impl Render for ConfigurationView {
}
impl ConfigurationView {
- fn render_static_credentials_ui(&self) -> AnyElement {
+ fn render_static_credentials_ui(&self) -> impl IntoElement {
v_flex()
.my_2()
.gap_1p5()
@@ -1278,6 +1268,5 @@ impl ConfigurationView {
.child(self.secret_access_key_editor.clone())
.child(self.session_token_editor.clone())
.child(self.region_editor.clone())
- .into_any_element()
}
}
@@ -29,6 +29,8 @@ use settings::SettingsStore;
use ui::{CommonAnimationExt, prelude::*};
use util::debug_panic;
+use crate::ui::ConfiguredApiCard;
+
const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("copilot_chat");
const PROVIDER_NAME: LanguageModelProviderName =
LanguageModelProviderName::new("GitHub Copilot Chat");
@@ -1326,27 +1328,12 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if self.state.read(cx).is_authenticated(cx) {
- 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("Authorized")),
- )
- .child(
- Button::new("sign_out", "Sign Out")
- .label_size(LabelSize::Small)
- .on_click(|_, window, cx| {
- window.dispatch_action(copilot::SignOut.boxed_clone(), cx);
- }),
- )
+ ConfiguredApiCard::new("Authorized")
+ .button_label("Sign Out")
+ .on_click(|_, window, cx| {
+ window.dispatch_action(copilot::SignOut.boxed_clone(), cx);
+ })
+ .into_any_element()
} else {
let loading_icon = Icon::new(IconName::ArrowCircle).with_rotate_animation(4);
@@ -1357,37 +1344,49 @@ impl Render for ConfigurationView {
Status::Starting { task: _ } => h_flex()
.gap_2()
.child(loading_icon)
- .child(Label::new("Starting Copilot…")),
+ .child(Label::new("Starting Copilot…"))
+ .into_any_element(),
Status::SigningIn { prompt: _ }
| Status::SignedOut {
awaiting_signing_in: true,
} => h_flex()
.gap_2()
.child(loading_icon)
- .child(Label::new("Signing into Copilot…")),
+ .child(Label::new("Signing into Copilot…"))
+ .into_any_element(),
Status::Error(_) => {
const LABEL: &str = "Copilot had issues starting. Please try restarting it. If the issue persists, try reinstalling Copilot.";
v_flex()
.gap_6()
.child(Label::new(LABEL))
.child(svg().size_8().path(IconName::CopilotError.path()))
+ .into_any_element()
}
_ => {
const LABEL: &str = "To use Zed's agent 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_2().child(Label::new(LABEL)).child(
- Button::new("sign_in", "Sign in to use GitHub Copilot")
- .full_width()
- .style(ButtonStyle::Outlined)
- .icon_color(Color::Muted)
- .icon(IconName::Github)
- .icon_position(IconPosition::Start)
- .icon_size(IconSize::Small)
- .on_click(|_, window, cx| copilot::initiate_sign_in(window, cx)),
- )
+ v_flex()
+ .gap_2()
+ .child(Label::new(LABEL))
+ .child(
+ Button::new("sign_in", "Sign in to use GitHub Copilot")
+ .full_width()
+ .style(ButtonStyle::Outlined)
+ .icon_color(Color::Muted)
+ .icon(IconName::Github)
+ .icon_position(IconPosition::Start)
+ .icon_size(IconSize::Small)
+ .on_click(|_, window, cx| {
+ copilot::initiate_sign_in(window, cx)
+ }),
+ )
+ .into_any_element()
}
},
- None => v_flex().gap_6().child(Label::new(ERROR_LABEL)),
+ None => v_flex()
+ .gap_6()
+ .child(Label::new(ERROR_LABEL))
+ .into_any_element(),
}
}
}
@@ -19,11 +19,12 @@ use std::pin::Pin;
use std::str::FromStr;
use std::sync::{Arc, LazyLock};
-use ui::{Icon, IconName, List, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
+use crate::ui::ConfiguredApiCard;
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("deepseek");
@@ -601,9 +602,21 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = DeepSeekLanguageModelProvider::api_url(cx);
+ if api_url == DEEPSEEK_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
if self.load_credentials_task.is_some() {
- div().child(Label::new("Loading credentials...")).into_any()
+ div()
+ .child(Label::new("Loading credentials..."))
+ .into_any_element()
} else if self.should_render_editor(cx) {
v_flex()
.size_full()
@@ -628,51 +641,12 @@ impl Render for ConfigurationView {
.size(LabelSize::Small)
.color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(div().w_full().overflow_x_hidden().text_ellipsis().child(
- Label::new(if env_var_set {
- format!(
- "API key set in {API_KEY_ENV_VAR_NAME} environment variable"
- )
- } else {
- let api_url = DeepSeekLanguageModelProvider::api_url(cx);
- if api_url == DEEPSEEK_API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }),
- )),
- )
- .child(
- h_flex().flex_shrink_0().child(
- 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)),
- ),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .into_any_element()
}
}
}
@@ -28,14 +28,14 @@ use std::sync::{
atomic::{self, AtomicU64},
};
use strum::IntoEnumIterator;
-use ui::{Icon, IconName, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use zed_env_vars::EnvVar;
use crate::api_key::ApiKey;
use crate::api_key::ApiKeyState;
-use crate::ui::InstructionListItem;
+use crate::ui::{ConfiguredApiCard, InstructionListItem};
const PROVIDER_ID: LanguageModelProviderId = language_model::GOOGLE_PROVIDER_ID;
const PROVIDER_NAME: LanguageModelProviderName = language_model::GOOGLE_PROVIDER_NAME;
@@ -835,9 +835,24 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!(
+ "API key set in {} environment variable",
+ API_KEY_ENV_VAR.name
+ )
+ } else {
+ let api_url = GoogleLanguageModelProvider::api_url(cx);
+ if api_url == google_ai::API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
if self.load_credentials_task.is_some() {
- div().child(Label::new("Loading credentials...")).into_any()
+ div()
+ .child(Label::new("Loading credentials..."))
+ .into_any_element()
} else if self.should_render_editor(cx) {
v_flex()
.size_full()
@@ -864,58 +879,15 @@ impl Render for ConfigurationView {
)
.size(LabelSize::Small).color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(Label::new(
- if env_var_set {
- format!("API key set in {} environment variable", API_KEY_ENV_VAR.name)
- } else {
- let api_url = GoogleLanguageModelProvider::api_url(cx);
- if api_url == google_ai::API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }
- ))
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- 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)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, make sure {GEMINI_API_KEY_VAR_NAME} and {GOOGLE_AI_API_KEY_VAR_NAME} environment variables are unset.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!("To reset your API key, make sure {GEMINI_API_KEY_VAR_NAME} and {GOOGLE_AI_API_KEY_VAR_NAME} environment variables are unset."))
+ })
+ .into_any_element()
}
}
}
@@ -19,11 +19,12 @@ use std::pin::Pin;
use std::str::FromStr;
use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
-use ui::{Icon, IconName, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
+use crate::ui::ConfiguredApiCard;
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("mistral");
@@ -883,6 +884,12 @@ impl ConfigurationView {
let key_state = &self.state.read(cx).codestral_api_key_state;
let should_show_editor = !key_state.has_key();
let env_var_set = key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {CODESTRAL_API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ "Codestral API key configured".to_string()
+ };
+
if should_show_editor {
v_flex()
.id("codestral")
@@ -910,42 +917,19 @@ impl ConfigurationView {
.size(LabelSize::Small).color(Color::Muted),
).into_any()
} else {
- h_flex()
- .id("codestral")
- .mt_2()
- .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(if env_var_set {
- format!("API key set in {CODESTRAL_API_KEY_ENV_VAR_NAME} environment variable")
- } else {
- "Codestral API key configured".to_string()
- })),
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!(
+ "To reset your API key, \
+ unset the {CODESTRAL_API_KEY_ENV_VAR_NAME} environment variable."
+ ))
+ })
+ .on_click(
+ cx.listener(|this, _, window, cx| this.reset_codestral_api_key(window, cx)),
)
- .child(
- 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)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!(
- "To reset your API key, \
- unset the {CODESTRAL_API_KEY_ENV_VAR_NAME} environment variable."
- )))
- })
- .on_click(
- cx.listener(|this, _, window, cx| this.reset_codestral_api_key(window, cx)),
- ),
- ).into_any()
+ .into_any_element()
}
}
}
@@ -953,6 +937,16 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = MistralLanguageModelProvider::api_url(cx);
+ if api_url == MISTRAL_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
if self.load_credentials_task.is_some() {
div().child(Label::new("Loading credentials...")).into_any()
@@ -987,68 +981,17 @@ impl Render for ConfigurationView {
} else {
v_flex()
.size_full()
+ .gap_1()
.child(
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(
- Label::new(
- if env_var_set {
- format!(
- "API key set in {API_KEY_ENV_VAR_NAME} environment variable"
- )
- } else {
- let api_url = MistralLanguageModelProvider::api_url(cx);
- if api_url == MISTRAL_API_URL {
- "API key configured".to_string()
- } else {
- format!(
- "API key configured for {}",
- api_url
- )
- }
- }
- )
- ),
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- 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)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!(
- "To reset your API key, \
- unset the {API_KEY_ENV_VAR_NAME} environment variable."
- )))
- })
- .on_click(cx.listener(|this, _, window, cx| {
- this.reset_api_key(window, cx)
- })),
- ),
- ),
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!(
+ "To reset your API key, \
+ unset the {API_KEY_ENV_VAR_NAME} environment variable."
+ ))
+ }),
)
.child(self.render_codestral_api_key_editor(cx))
.into_any()
@@ -28,7 +28,7 @@ use zed_env_vars::{EnvVar, env_var};
use crate::AllLanguageModelSettings;
use crate::api_key::ApiKeyState;
-use crate::ui::InstructionListItem;
+use crate::ui::{ConfiguredApiCard, InstructionListItem};
const OLLAMA_DOWNLOAD_URL: &str = "https://ollama.com/download";
const OLLAMA_LIBRARY_URL: &str = "https://ollama.com/library";
@@ -749,9 +749,14 @@ impl ConfigurationView {
))
}
- fn render_api_key_editor(&self, cx: &Context<Self>) -> Div {
+ fn render_api_key_editor(&self, cx: &Context<Self>) -> impl IntoElement {
let state = self.state.read(cx);
let env_var_set = state.api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
+ } else {
+ "API key configured".to_string()
+ };
if !state.api_key_state.has_key() {
v_flex()
@@ -764,40 +769,15 @@ impl ConfigurationView {
.size(LabelSize::Small)
.color(Color::Muted),
)
+ .into_any_element()
} else {
- h_flex()
- .p_3()
- .justify_between()
- .rounded_md()
- .border_1()
- .border_color(cx.theme().colors().border)
- .bg(cx.theme().colors().elevated_surface_background)
- .child(
- h_flex()
- .gap_2()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- Label::new(
- if env_var_set {
- format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
- } else {
- "API key configured".to_string()
- }
- )
- )
- )
- .child(
- Button::new("reset-api-key", "Reset API Key")
- .label_size(LabelSize::Small)
- .icon(IconName::Undo)
- .icon_size(IconSize::Small)
- .icon_position(IconPosition::Start)
- .layer(ElevationIndex::ModalSurface)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- )
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable."))
+ })
+ .into_any_element()
}
}
@@ -909,7 +889,7 @@ impl Render for ConfigurationView {
)
.child(
IconButton::new("refresh-models", IconName::RotateCcw)
- .tooltip(Tooltip::text("Refresh models"))
+ .tooltip(Tooltip::text("Refresh Models"))
.on_click(cx.listener(|this, _, _, cx| {
this.state.update(cx, |state, _| {
state.fetched_models.clear();
@@ -20,11 +20,12 @@ use std::pin::Pin;
use std::str::FromStr as _;
use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
-use ui::{ElevationIndex, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
+use crate::ui::ConfiguredApiCard;
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
const PROVIDER_ID: LanguageModelProviderId = language_model::OPEN_AI_PROVIDER_ID;
@@ -762,6 +763,16 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = OpenAiLanguageModelProvider::api_url(cx);
+ if api_url == OPEN_AI_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
let api_key_section = if self.should_render_editor(cx) {
v_flex()
@@ -795,58 +806,15 @@ impl Render for ConfigurationView {
)
.size(LabelSize::Small).color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(Label::new(
- if env_var_set {
- format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
- } else {
- let api_url = OpenAiLanguageModelProvider::api_url(cx);
- if api_url == OPEN_AI_API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }
- ))
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- Button::new("reset-api-key", "Reset API Key")
- .label_size(LabelSize::Small)
- .icon(IconName::Undo)
- .icon_size(IconSize::Small)
- .icon_position(IconPosition::Start)
- .layer(ElevationIndex::ModalSurface)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable."))
+ })
+ .into_any_element()
};
let compatible_api_section = h_flex()
@@ -17,11 +17,12 @@ use settings::{OpenRouterAvailableModel as AvailableModel, Settings, SettingsSto
use std::pin::Pin;
use std::str::FromStr as _;
use std::sync::{Arc, LazyLock};
-use ui::{Icon, IconName, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
+use crate::ui::ConfiguredApiCard;
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("openrouter");
@@ -777,9 +778,21 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = OpenRouterLanguageModelProvider::api_url(cx);
+ if api_url == OPEN_ROUTER_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
if self.load_credentials_task.is_some() {
- div().child(Label::new("Loading credentials...")).into_any()
+ div()
+ .child(Label::new("Loading credentials..."))
+ .into_any_element()
} else if self.should_render_editor(cx) {
v_flex()
.size_full()
@@ -806,56 +819,15 @@ impl Render for ConfigurationView {
)
.size(LabelSize::Small).color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(Label::new(if env_var_set {
- format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
- } else {
- let api_url = OpenRouterLanguageModelProvider::api_url(cx);
- if api_url == OPEN_ROUTER_API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }))
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- 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)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable."))
+ })
+ .into_any_element()
}
}
}
@@ -14,13 +14,16 @@ pub use settings::VercelAvailableModel as AvailableModel;
use settings::{Settings, SettingsStore};
use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
-use ui::{ElevationIndex, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use vercel::{Model, VERCEL_API_URL};
use zed_env_vars::{EnvVar, env_var};
-use crate::{api_key::ApiKeyState, ui::InstructionListItem};
+use crate::{
+ api_key::ApiKeyState,
+ ui::{ConfiguredApiCard, InstructionListItem},
+};
const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("vercel");
const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("Vercel");
@@ -448,6 +451,16 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = VercelLanguageModelProvider::api_url(cx);
+ if api_url == VERCEL_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
let api_key_section = if self.should_render_editor(cx) {
v_flex()
@@ -477,56 +490,15 @@ impl Render for ConfigurationView {
.size(LabelSize::Small)
.color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(Label::new(if env_var_set {
- format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
- } else {
- let api_url = VercelLanguageModelProvider::api_url(cx);
- if api_url == VERCEL_API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }))
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- Button::new("reset-api-key", "Reset API Key")
- .label_size(LabelSize::Small)
- .icon(IconName::Undo)
- .icon_size(IconSize::Small)
- .icon_position(IconPosition::Start)
- .layer(ElevationIndex::ModalSurface)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable."))
+ })
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .into_any_element()
};
if self.load_credentials_task.is_some() {
@@ -14,13 +14,16 @@ pub use settings::XaiAvailableModel as AvailableModel;
use settings::{Settings, SettingsStore};
use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
-use ui::{ElevationIndex, List, Tooltip, prelude::*};
+use ui::{List, prelude::*};
use ui_input::InputField;
use util::ResultExt;
use x_ai::{Model, XAI_API_URL};
use zed_env_vars::{EnvVar, env_var};
-use crate::{api_key::ApiKeyState, ui::InstructionListItem};
+use crate::{
+ api_key::ApiKeyState,
+ ui::{ConfiguredApiCard, InstructionListItem},
+};
const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("x_ai");
const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("xAI");
@@ -445,6 +448,16 @@ impl ConfigurationView {
impl Render for ConfigurationView {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let env_var_set = self.state.read(cx).api_key_state.is_from_env_var();
+ let configured_card_label = if env_var_set {
+ format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
+ } else {
+ let api_url = XAiLanguageModelProvider::api_url(cx);
+ if api_url == XAI_API_URL {
+ "API key configured".to_string()
+ } else {
+ format!("API key configured for {}", api_url)
+ }
+ };
let api_key_section = if self.should_render_editor(cx) {
v_flex()
@@ -474,56 +487,15 @@ impl Render for ConfigurationView {
.size(LabelSize::Small)
.color(Color::Muted),
)
- .into_any()
+ .into_any_element()
} else {
- 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()
- .flex_1()
- .min_w_0()
- .gap_1()
- .child(Icon::new(IconName::Check).color(Color::Success))
- .child(
- div()
- .w_full()
- .overflow_x_hidden()
- .text_ellipsis()
- .child(Label::new(if env_var_set {
- format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
- } else {
- let api_url = XAiLanguageModelProvider::api_url(cx);
- if api_url == XAI_API_URL {
- "API key configured".to_string()
- } else {
- format!("API key configured for {}", api_url)
- }
- }))
- ),
- )
- .child(
- h_flex()
- .flex_shrink_0()
- .child(
- Button::new("reset-api-key", "Reset API Key")
- .label_size(LabelSize::Small)
- .icon(IconName::Undo)
- .icon_size(IconSize::Small)
- .icon_position(IconPosition::Start)
- .layer(ElevationIndex::ModalSurface)
- .when(env_var_set, |this| {
- this.tooltip(Tooltip::text(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable.")))
- })
- .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
- ),
- )
- .into_any()
+ ConfiguredApiCard::new(configured_card_label)
+ .disabled(env_var_set)
+ .when(env_var_set, |this| {
+ this.tooltip_label(format!("To reset your API key, unset the {API_KEY_ENV_VAR_NAME} environment variable."))
+ })
+ .on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx)))
+ .into_any_element()
};
if self.load_credentials_task.is_some() {
@@ -1,2 +1,4 @@
+pub mod configured_api_card;
pub mod instruction_list_item;
+pub use configured_api_card::ConfiguredApiCard;
pub use instruction_list_item::InstructionListItem;
@@ -0,0 +1,86 @@
+use gpui::{ClickEvent, IntoElement, ParentElement, SharedString};
+use ui::{Tooltip, prelude::*};
+
+#[derive(IntoElement)]
+pub struct ConfiguredApiCard {
+ label: SharedString,
+ button_label: Option<SharedString>,
+ tooltip_label: Option<SharedString>,
+ disabled: bool,
+ on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
+}
+
+impl ConfiguredApiCard {
+ pub fn new(label: impl Into<SharedString>) -> Self {
+ Self {
+ label: label.into(),
+ button_label: None,
+ tooltip_label: None,
+ disabled: false,
+ on_click: None,
+ }
+ }
+
+ pub fn on_click(
+ mut self,
+ handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ ) -> Self {
+ self.on_click = Some(Box::new(handler));
+ self
+ }
+
+ pub fn button_label(mut self, button_label: impl Into<SharedString>) -> Self {
+ self.button_label = Some(button_label.into());
+ self
+ }
+
+ pub fn tooltip_label(mut self, tooltip_label: impl Into<SharedString>) -> Self {
+ self.tooltip_label = Some(tooltip_label.into());
+ self
+ }
+
+ pub fn disabled(mut self, disabled: bool) -> Self {
+ self.disabled = disabled;
+ self
+ }
+}
+
+impl RenderOnce for ConfiguredApiCard {
+ fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
+ let button_label = self.button_label.unwrap_or("Reset Key".into());
+ let button_id = SharedString::new(format!("id-{}", button_label));
+
+ h_flex()
+ .mt_0p5()
+ .p_1()
+ .justify_between()
+ .rounded_md()
+ .border_1()
+ .border_color(cx.theme().colors().border)
+ .bg(cx.theme().colors().background)
+ .child(
+ h_flex()
+ .flex_1()
+ .min_w_0()
+ .gap_1()
+ .child(Icon::new(IconName::Check).color(Color::Success))
+ .child(Label::new(self.label).truncate()),
+ )
+ .child(
+ Button::new(button_id, button_label)
+ .label_size(LabelSize::Small)
+ .icon(IconName::Undo)
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Muted)
+ .icon_position(IconPosition::Start)
+ .disabled(self.disabled)
+ .when_some(self.tooltip_label, |this, label| {
+ this.tooltip(Tooltip::text(label))
+ })
+ .when_some(
+ self.on_click.filter(|_| !self.disabled),
+ |this, on_click| this.on_click(on_click),
+ ),
+ )
+ }
+}