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