agent_ui: Improve API key configuration UI display (#42306)

Improve the layout and text display of API key configuration in multiple
language model providers to ensure proper text wrapping and ellipsis
handling when API URLs are long.

Before:

<img width="320" alt="image"
src="https://github.com/user-attachments/assets/2f89182c-34a0-4f95-a43a-c2be98d34873"
/>

After:

<img width="320" alt="image"
src="https://github.com/user-attachments/assets/09bf5cc3-07f0-47bc-b21a-d84b8b1caa67"
/>

Changes include:
- Add proper flex layout with overflow handling
- Replace truncate_and_trailoff with CSS text ellipsis
- Ensure consistent UI behavior across all providers

Release Notes:

- Improved API key configuration display in language model settings
This commit is contained in:
chenmi
2025-11-09 13:00:31 -03:00
committed by GitHub
parent 5d08c1b35f
commit cc1d66b530
9 changed files with 305 additions and 194 deletions
@@ -22,7 +22,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
use crate::api_key::ApiKeyState;
@@ -953,30 +953,42 @@ impl Render for ConfigurationView {
.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(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 {}", truncate_and_trailoff(&api_url, 32))
}
})),
.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(
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))),
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()
}
+26 -21
View File
@@ -21,7 +21,7 @@ use std::sync::{Arc, LazyLock};
use ui::{Icon, IconName, List, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -640,32 +640,37 @@ impl Render for ConfigurationView {
.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(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 {
.child(div().w_full().overflow_x_hidden().text_ellipsis().child(
Label::new(if env_var_set {
format!(
"API key configured for {}",
truncate_and_trailoff(&api_url, 32)
"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(
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)),
),
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()
}
+35 -21
View File
@@ -30,7 +30,7 @@ use std::sync::{
use strum::IntoEnumIterator;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::EnvVar;
use crate::api_key::ApiKey;
@@ -876,30 +876,44 @@ impl Render for ConfigurationView {
.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(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 {}", truncate_and_trailoff(&api_url, 32))
}
})),
.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(
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))),
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()
}
+47 -31
View File
@@ -21,7 +21,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -998,40 +998,56 @@ impl Render for ConfigurationView {
.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(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 {}",
truncate_and_trailoff(&api_url, 32)
)
}
})),
.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(
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)
})),
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)
})),
),
),
)
.child(self.render_codestral_api_key_editor(cx))
+35 -21
View File
@@ -22,7 +22,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{ElevationIndex, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -807,30 +807,44 @@ impl Render for ConfigurationView {
.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(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 {}", truncate_and_trailoff(&api_url, 32))
}
})),
.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(
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))),
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()
};
@@ -15,7 +15,7 @@ use settings::{Settings, SettingsStore};
use std::sync::Arc;
use ui::{ElevationIndex, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::EnvVar;
use crate::api_key::ApiKeyState;
@@ -455,25 +455,39 @@ impl Render for ConfigurationView {
.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(if env_var_set {
format!("API key set in {env_var_name} environment variable")
} else {
format!("API key configured for {}", truncate_and_trailoff(&state.settings.api_url, 32))
})),
.child(
div()
.w_full()
.overflow_x_hidden()
.text_ellipsis()
.child(Label::new(
if env_var_set {
format!("API key set in {env_var_name} environment variable")
} else {
format!("API key configured for {}", &state.settings.api_url)
}
))
),
)
.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 {env_var_name} environment variable.")))
})
.on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
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 {env_var_name} environment variable.")))
})
.on_click(cx.listener(|this, _, window, cx| this.reset_api_key(window, cx))),
),
)
.into_any()
};
@@ -19,7 +19,7 @@ use std::str::FromStr as _;
use std::sync::{Arc, LazyLock};
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -818,30 +818,42 @@ impl Render for ConfigurationView {
.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(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 {}", truncate_and_trailoff(&api_url, 32))
}
})),
.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(
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))),
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()
}
+33 -21
View File
@@ -16,7 +16,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{ElevationIndex, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use vercel::{Model, VERCEL_API_URL};
use zed_env_vars::{EnvVar, env_var};
@@ -489,30 +489,42 @@ impl Render for ConfigurationView {
.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(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 {}", truncate_and_trailoff(&api_url, 32))
}
})),
.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(
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))),
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()
};
+33 -21
View File
@@ -16,7 +16,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{ElevationIndex, List, Tooltip, prelude::*};
use ui_input::InputField;
use util::{ResultExt, truncate_and_trailoff};
use util::ResultExt;
use x_ai::{Model, XAI_API_URL};
use zed_env_vars::{EnvVar, env_var};
@@ -486,30 +486,42 @@ impl Render for ConfigurationView {
.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(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 {}", truncate_and_trailoff(&api_url, 32))
}
})),
.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(
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))),
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()
};