agent_panel_onboarding_content.rs

  1use std::sync::Arc;
  2
  3use client::{Client, UserStore};
  4use gpui::{Action, ClickEvent, Entity, IntoElement, ParentElement};
  5use language_model::{LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
  6use ui::{Divider, List, prelude::*};
  7use zed_actions::agent::{OpenConfiguration, ToggleModelSelector};
  8
  9use crate::{AgentPanelOnboardingCard, BulletItem, ZedAiOnboarding};
 10
 11pub struct AgentPanelOnboarding {
 12    user_store: Entity<UserStore>,
 13    client: Arc<Client>,
 14    configured_providers: Vec<(IconName, SharedString)>,
 15    continue_with_zed_ai: Arc<dyn Fn(&mut Window, &mut App)>,
 16}
 17
 18impl AgentPanelOnboarding {
 19    pub fn new(
 20        user_store: Entity<UserStore>,
 21        client: Arc<Client>,
 22        continue_with_zed_ai: impl Fn(&mut Window, &mut App) + 'static,
 23        cx: &mut Context<Self>,
 24    ) -> Self {
 25        cx.subscribe(
 26            &LanguageModelRegistry::global(cx),
 27            |this: &mut Self, _registry, event: &language_model::Event, cx| match event {
 28                language_model::Event::ProviderStateChanged
 29                | language_model::Event::AddedProvider(_)
 30                | language_model::Event::RemovedProvider(_) => {
 31                    this.configured_providers = Self::compute_available_providers(cx)
 32                }
 33                _ => {}
 34            },
 35        )
 36        .detach();
 37
 38        Self {
 39            user_store,
 40            client,
 41            configured_providers: Self::compute_available_providers(cx),
 42            continue_with_zed_ai: Arc::new(continue_with_zed_ai),
 43        }
 44    }
 45
 46    fn compute_available_providers(cx: &App) -> Vec<(IconName, SharedString)> {
 47        LanguageModelRegistry::read_global(cx)
 48            .providers()
 49            .iter()
 50            .filter(|provider| {
 51                provider.is_authenticated(cx) && provider.id() != ZED_CLOUD_PROVIDER_ID
 52            })
 53            .map(|provider| (provider.icon(), provider.name().0.clone()))
 54            .collect()
 55    }
 56
 57    fn configure_providers(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
 58        window.dispatch_action(OpenConfiguration.boxed_clone(), cx);
 59        cx.notify();
 60    }
 61
 62    fn render_api_keys_section(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
 63        let has_existing_providers = self.configured_providers.len() > 0;
 64        let configure_provider_label = if has_existing_providers {
 65            "Configure Other Provider"
 66        } else {
 67            "Configure Providers"
 68        };
 69
 70        let content = if has_existing_providers {
 71            List::new()
 72                    .child(BulletItem::new(
 73                        "Or start now using API keys from your environment for the following providers:"
 74                    ))
 75                    .child(
 76                        h_flex()
 77                            .px_5()
 78                            .gap_2()
 79                            .flex_wrap()
 80                            .children(self.configured_providers.iter().cloned().map(|(icon, name)|
 81                                h_flex()
 82                                    .gap_1p5()
 83                                    .child(Icon::new(icon).size(IconSize::Small).color(Color::Muted))
 84                                    .child(Label::new(name))
 85                            ))
 86                    )
 87                    .child(BulletItem::new(
 88                        "No need for any of the plans or even to sign in",
 89                    ))
 90        } else {
 91            List::new()
 92                .child(BulletItem::new(
 93                    "You can also use AI in Zed by bringing your own API keys",
 94                ))
 95                .child(BulletItem::new(
 96                    "No need for any of the plans or even to sign in",
 97                ))
 98        };
 99
100        v_flex()
101            .mt_2()
102            .gap_1()
103            .child(
104                h_flex()
105                    .gap_2()
106                    .child(
107                        Label::new("API Keys")
108                            .size(LabelSize::Small)
109                            .color(Color::Muted)
110                            .buffer_font(cx),
111                    )
112                    .child(Divider::horizontal()),
113            )
114            .child(content)
115            .when(has_existing_providers, |this| {
116                this.child(
117                    Button::new("pick-model", "Choose Model")
118                        .full_width()
119                        .style(ButtonStyle::Outlined)
120                        .on_click(|_event, window, cx| {
121                            window.dispatch_action(ToggleModelSelector.boxed_clone(), cx)
122                        }),
123                )
124            })
125            .child(
126                Button::new("configure-providers", configure_provider_label)
127                    .full_width()
128                    .style(ButtonStyle::Outlined)
129                    .on_click(cx.listener(Self::configure_providers)),
130            )
131    }
132}
133
134impl Render for AgentPanelOnboarding {
135    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
136        AgentPanelOnboardingCard::new()
137            .child(ZedAiOnboarding::new(
138                self.client.clone(),
139                &self.user_store,
140                self.continue_with_zed_ai.clone(),
141                cx,
142            ))
143            .child(self.render_api_keys_section(cx))
144    }
145}