agent_api_keys_onboarding.rs

  1use gpui::{Action, IntoElement, ParentElement, RenderOnce, point};
  2use language_model::{IconOrSvg, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
  3use ui::{Divider, List, ListBulletItem, prelude::*};
  4
  5pub struct ApiKeysWithProviders {
  6    configured_providers: Vec<(IconOrSvg, SharedString)>,
  7}
  8
  9impl ApiKeysWithProviders {
 10    pub fn new(cx: &mut Context<Self>) -> Self {
 11        cx.subscribe(
 12            &LanguageModelRegistry::global(cx),
 13            |this: &mut Self, _registry, event: &language_model::Event, cx| match event {
 14                language_model::Event::ProviderStateChanged(_)
 15                | language_model::Event::AddedProvider(_)
 16                | language_model::Event::RemovedProvider(_)
 17                | language_model::Event::ProvidersChanged => {
 18                    this.configured_providers = Self::compute_configured_providers(cx)
 19                }
 20                _ => {}
 21            },
 22        )
 23        .detach();
 24
 25        Self {
 26            configured_providers: Self::compute_configured_providers(cx),
 27        }
 28    }
 29
 30    fn compute_configured_providers(cx: &App) -> Vec<(IconOrSvg, SharedString)> {
 31        LanguageModelRegistry::read_global(cx)
 32            .visible_providers()
 33            .iter()
 34            .filter(|provider| {
 35                provider.is_authenticated(cx) && provider.id() != ZED_CLOUD_PROVIDER_ID
 36            })
 37            .map(|provider| (provider.icon(), provider.name().0))
 38            .collect()
 39    }
 40}
 41
 42impl Render for ApiKeysWithProviders {
 43    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 44        let configured_providers_list =
 45            self.configured_providers
 46                .iter()
 47                .cloned()
 48                .map(|(icon, name)| {
 49                    h_flex()
 50                        .gap_1p5()
 51                        .child(
 52                            match icon {
 53                                IconOrSvg::Icon(icon_name) => Icon::new(icon_name),
 54                                IconOrSvg::Svg(icon_path) => Icon::from_external_svg(icon_path),
 55                            }
 56                            .size(IconSize::XSmall)
 57                            .color(Color::Muted),
 58                        )
 59                        .child(Label::new(name))
 60                });
 61        div()
 62            .mx_2p5()
 63            .p_1()
 64            .pb_0()
 65            .gap_2()
 66            .rounded_t_lg()
 67            .border_t_1()
 68            .border_x_1()
 69            .border_color(cx.theme().colors().border.opacity(0.5))
 70            .bg(cx.theme().colors().background.alpha(0.5))
 71            .shadow(vec![gpui::BoxShadow {
 72                color: gpui::black().opacity(0.15),
 73                offset: point(px(1.), px(-1.)),
 74                blur_radius: px(3.),
 75                spread_radius: px(0.),
 76            }])
 77            .child(
 78                h_flex()
 79                    .px_2p5()
 80                    .py_1p5()
 81                    .gap_2()
 82                    .flex_wrap()
 83                    .rounded_t(px(5.))
 84                    .overflow_hidden()
 85                    .border_t_1()
 86                    .border_x_1()
 87                    .border_color(cx.theme().colors().border)
 88                    .bg(cx.theme().colors().panel_background)
 89                    .child(
 90                        h_flex()
 91                            .min_w_0()
 92                            .gap_2()
 93                            .child(
 94                                Icon::new(IconName::Info)
 95                                    .size(IconSize::XSmall)
 96                                    .color(Color::Muted)
 97                            )
 98                            .child(
 99                                div()
100                                    .w_full()
101                                    .child(
102                                        Label::new("Start now using API keys from your environment for the following providers:")
103                                            .color(Color::Muted)
104                                    )
105                            )
106                    )
107                    .children(configured_providers_list)
108            )
109    }
110}
111
112#[derive(IntoElement)]
113pub struct ApiKeysWithoutProviders;
114
115impl ApiKeysWithoutProviders {
116    pub fn new() -> Self {
117        Self
118    }
119}
120
121impl RenderOnce for ApiKeysWithoutProviders {
122    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
123        v_flex()
124            .mt_2()
125            .gap_1()
126            .child(
127                h_flex()
128                    .gap_2()
129                    .child(
130                        Label::new("API Keys")
131                            .size(LabelSize::Small)
132                            .color(Color::Muted)
133                            .buffer_font(cx),
134                    )
135                    .child(Divider::horizontal()),
136            )
137            .child(List::new().child(ListBulletItem::new(
138                "Add your own keys to use AI without signing in.",
139            )))
140            .child(
141                Button::new("configure-providers", "Configure Providers")
142                    .full_width()
143                    .style(ButtonStyle::Outlined)
144                    .on_click(move |_, window, cx| {
145                        window.dispatch_action(zed_actions::agent::OpenSettings.boxed_clone(), cx);
146                    }),
147            )
148    }
149}