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