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