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 has_configured_providers: bool,
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.has_configured_providers = Self::has_configured_providers(cx)
32 }
33 _ => {}
34 },
35 )
36 .detach();
37
38 Self {
39 user_store,
40 client,
41 has_configured_providers: Self::has_configured_providers(cx),
42 continue_with_zed_ai: Arc::new(continue_with_zed_ai),
43 }
44 }
45
46 fn has_configured_providers(cx: &App) -> bool {
47 LanguageModelRegistry::read_global(cx)
48 .providers()
49 .iter()
50 .any(|provider| provider.is_authenticated(cx) && provider.id() != ZED_CLOUD_PROVIDER_ID)
51 }
52}
53
54impl Render for AgentPanelOnboarding {
55 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
56 let enrolled_in_trial = self.user_store.read(cx).plan().is_some_and(|plan| {
57 matches!(
58 plan,
59 Plan::V1(PlanV1::ZedProTrial) | Plan::V2(PlanV2::ZedProTrial)
60 )
61 });
62 let is_pro_user = self.user_store.read(cx).plan().is_some_and(|plan| {
63 matches!(plan, Plan::V1(PlanV1::ZedPro) | Plan::V2(PlanV2::ZedPro))
64 });
65
66 AgentPanelOnboardingCard::new()
67 .child(
68 ZedAiOnboarding::new(
69 self.client.clone(),
70 &self.user_store,
71 self.continue_with_zed_ai.clone(),
72 cx,
73 )
74 .with_dismiss({
75 let callback = self.continue_with_zed_ai.clone();
76 move |window, cx| callback(window, cx)
77 }),
78 )
79 .map(|this| {
80 if enrolled_in_trial || is_pro_user || self.has_configured_providers {
81 this
82 } else {
83 this.child(ApiKeysWithoutProviders::new())
84 }
85 })
86 }
87}