agent_ui: Add component to standardize the configured LLM card (#42314)

Danilo Leal created

This PR adds a new component to the `language_models` crate called
`ConfiguredApiCard`:

<img width="500" height="420" alt="Screenshot 2025-11-09 at 2  07@2x"
src="https://github.com/user-attachments/assets/655ea941-2df8-4489-a4da-bba34acf33a9"
/>

We were previously recreating this component from scratch with regular
divs in all LLM providers render function, which was redundant as they
all essentially looked the same and didn't have any major variations
aside from labels. We can clean up a bunch of similar code with this
change, which is cool!

Release Notes:

- N/A

Change summary

crates/language_models/src/provider/anthropic.rs     |  77 ++-----
crates/language_models/src/provider/bedrock.rs       |  77 +++----
crates/language_models/src/provider/copilot_chat.rs  |  67 +++---
crates/language_models/src/provider/deepseek.rs      |  66 ++----
crates/language_models/src/provider/google.rs        |  80 ++-----
crates/language_models/src/provider/mistral.rs       | 137 ++++---------
crates/language_models/src/provider/ollama.rs        |  52 +---
crates/language_models/src/provider/open_ai.rs       |  72 ++-----
crates/language_models/src/provider/open_router.rs   |  74 ++-----
crates/language_models/src/provider/vercel.rs        |  74 ++-----
crates/language_models/src/provider/x_ai.rs          |  74 ++-----
crates/language_models/src/ui.rs                     |   2 
crates/language_models/src/ui/configured_api_card.rs |  86 ++++++++
13 files changed, 370 insertions(+), 568 deletions(-)

Detailed changes

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

@@ -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()
         }
     }
 }

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

@@ -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()
     }
 }

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

@@ -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(),
             }
         }
     }

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

@@ -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()
         }
     }
 }

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

@@ -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()
         }
     }
 }

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

@@ -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()

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

@@ -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();

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

@@ -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()

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

@@ -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()
         }
     }
 }

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

@@ -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() {

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

@@ -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() {

crates/language_models/src/ui.rs 🔗

@@ -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;

crates/language_models/src/ui/configured_api_card.rs 🔗

@@ -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),
+                    ),
+            )
+    }
+}