1use std::sync::Arc;
2
3use client::{Client, UserStore};
4use cloud_llm_client::Plan;
5use gpui::{Entity, IntoElement, ParentElement};
6use language_model::{LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
7use ui::prelude::*;
8
9use crate::{AgentPanelOnboardingCard, ApiKeysWithoutProviders, 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))
54 .collect()
55 }
56}
57
58impl Render for AgentPanelOnboarding {
59 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
60 let enrolled_in_trial = self.user_store.read(cx).plan() == Some(Plan::ZedProTrial);
61 let is_pro_user = self.user_store.read(cx).plan() == Some(Plan::ZedPro);
62
63 AgentPanelOnboardingCard::new()
64 .child(
65 ZedAiOnboarding::new(
66 self.client.clone(),
67 &self.user_store,
68 self.continue_with_zed_ai.clone(),
69 cx,
70 )
71 .with_dismiss({
72 let callback = self.continue_with_zed_ai.clone();
73 move |window, cx| callback(window, cx)
74 }),
75 )
76 .map(|this| {
77 if enrolled_in_trial || is_pro_user || !self.configured_providers.is_empty() {
78 this
79 } else {
80 this.child(ApiKeysWithoutProviders::new())
81 }
82 })
83 }
84}