1use std::sync::Arc;
2
3use client::{Client, UserStore};
4use cloud_llm_client::{Plan, PlanV1, PlanV2};
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().is_some_and(|plan| {
61 matches!(
62 plan,
63 Plan::V1(PlanV1::ZedProTrial) | Plan::V2(PlanV2::ZedProTrial)
64 )
65 });
66 let is_pro_user = self.user_store.read(cx).plan().is_some_and(|plan| {
67 matches!(plan, Plan::V1(PlanV1::ZedPro) | Plan::V2(PlanV2::ZedPro))
68 });
69
70 AgentPanelOnboardingCard::new()
71 .child(
72 ZedAiOnboarding::new(
73 self.client.clone(),
74 &self.user_store,
75 self.continue_with_zed_ai.clone(),
76 cx,
77 )
78 .with_dismiss({
79 let callback = self.continue_with_zed_ai.clone();
80 move |window, cx| callback(window, cx)
81 }),
82 )
83 .map(|this| {
84 if enrolled_in_trial || is_pro_user || !self.configured_providers.is_empty() {
85 this
86 } else {
87 this.child(ApiKeysWithoutProviders::new())
88 }
89 })
90 }
91}