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
43impl Render for ApiKeysWithProviders {
44 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
45 let configured_providers_list =
46 self.configured_providers
47 .iter()
48 .cloned()
49 .map(|(icon, name)| {
50 h_flex()
51 .gap_1p5()
52 .child(Icon::new(icon).size(IconSize::XSmall).color(Color::Muted))
53 .child(Label::new(name))
54 });
55 div()
56 .mx_2p5()
57 .p_1()
58 .pb_0()
59 .gap_2()
60 .rounded_t_lg()
61 .border_t_1()
62 .border_x_1()
63 .border_color(cx.theme().colors().border.opacity(0.5))
64 .bg(cx.theme().colors().background.alpha(0.5))
65 .shadow(vec![gpui::BoxShadow {
66 color: gpui::black().opacity(0.15),
67 offset: point(px(1.), px(-1.)),
68 blur_radius: px(3.),
69 spread_radius: px(0.),
70 }])
71 .child(
72 h_flex()
73 .px_2p5()
74 .py_1p5()
75 .gap_2()
76 .flex_wrap()
77 .rounded_t(px(5.))
78 .overflow_hidden()
79 .border_t_1()
80 .border_x_1()
81 .border_color(cx.theme().colors().border)
82 .bg(cx.theme().colors().panel_background)
83 .child(
84 h_flex()
85 .min_w_0()
86 .gap_2()
87 .child(
88 Icon::new(IconName::Info)
89 .size(IconSize::XSmall)
90 .color(Color::Muted)
91 )
92 .child(
93 div()
94 .w_full()
95 .child(
96 Label::new("Start now using API keys from your environment for the following providers:")
97 .color(Color::Muted)
98 )
99 )
100 )
101 .children(configured_providers_list)
102 )
103 }
104}
105
106#[derive(IntoElement)]
107pub struct ApiKeysWithoutProviders;
108
109impl ApiKeysWithoutProviders {
110 pub fn new() -> Self {
111 Self
112 }
113}
114
115impl RenderOnce for ApiKeysWithoutProviders {
116 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
117 v_flex()
118 .mt_2()
119 .gap_1()
120 .child(
121 h_flex()
122 .gap_2()
123 .child(
124 Label::new("API Keys")
125 .size(LabelSize::Small)
126 .color(Color::Muted)
127 .buffer_font(cx),
128 )
129 .child(Divider::horizontal()),
130 )
131 .child(List::new().child(BulletItem::new(
132 "Add your own keys to use AI without signing in.",
133 )))
134 .child(
135 Button::new("configure-providers", "Configure Providers")
136 .full_width()
137 .style(ButtonStyle::Outlined)
138 .on_click(move |_, window, cx| {
139 window.dispatch_action(
140 zed_actions::agent::OpenConfiguration.boxed_clone(),
141 cx,
142 );
143 }),
144 )
145 }
146}