From 9437e84465a313bb3203ac55b5dc860e07df1f29 Mon Sep 17 00:00:00 2001 From: Neel Date: Wed, 28 Jan 2026 21:34:10 +0000 Subject: [PATCH] Add student plan (#47866) Release Notes: - N/A --- crates/agent_ui/src/agent_configuration.rs | 1 + crates/ai_onboarding/src/ai_onboarding.rs | 15 +++++++++++++++ crates/ai_onboarding/src/ai_upsell_card.rs | 9 +++++++++ crates/ai_onboarding/src/plan_definitions.rs | 9 +++++++++ crates/cloud_api_types/src/plan.rs | 4 ++++ crates/title_bar/src/title_bar.rs | 1 + 6 files changed, 39 insertions(+) diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 7359834d545e11fd6c9bd80f1783b76960beb074..9e101b9e8d8de5993eaaedbe43bd3a04e4217d37 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -501,6 +501,7 @@ impl AgentConfiguration { Plan::ZedFree => ("Free", Color::Default, free_chip_bg), Plan::ZedProTrial => ("Pro Trial", Color::Accent, pro_chip_bg), Plan::ZedPro => ("Pro", Color::Accent, pro_chip_bg), + Plan::ZedStudent => ("Student", Color::Accent, pro_chip_bg), }; Chip::new(plan_name.to_string()) diff --git a/crates/ai_onboarding/src/ai_onboarding.rs b/crates/ai_onboarding/src/ai_onboarding.rs index 5cfbbb4acb480a40cc73e6f093d9b0122a30c29a..0b1ccb4088e67de332c2bd2940ca5bdf77f1d3df 100644 --- a/crates/ai_onboarding/src/ai_onboarding.rs +++ b/crates/ai_onboarding/src/ai_onboarding.rs @@ -265,6 +265,20 @@ impl ZedAiOnboarding { .children(self.render_dismiss_button()) .into_any_element() } + + fn render_student_plan_state(&self, _cx: &mut App) -> AnyElement { + v_flex() + .gap_1() + .child(Headline::new("Welcome to Zed Student")) + .child( + Label::new("Here's what you get:") + .color(Color::Muted) + .mb_2(), + ) + .child(PlanDefinitions.student_plan()) + .children(self.render_dismiss_button()) + .into_any_element() + } } impl RenderOnce for ZedAiOnboarding { @@ -275,6 +289,7 @@ impl RenderOnce for ZedAiOnboarding { Some(Plan::ZedFree) => self.render_free_plan_state(cx), Some(Plan::ZedProTrial) => self.render_trial_state(cx), Some(Plan::ZedPro) => self.render_pro_plan_state(cx), + Some(Plan::ZedStudent) => self.render_student_plan_state(cx), } } else { self.render_sign_in_disclaimer(cx) diff --git a/crates/ai_onboarding/src/ai_upsell_card.rs b/crates/ai_onboarding/src/ai_upsell_card.rs index dc368c8fee0f45ad41b951b6f3eabf294214f13a..f1a1c4310def0b9b4dbabbc6a59eae940396fbb9 100644 --- a/crates/ai_onboarding/src/ai_upsell_card.rs +++ b/crates/ai_onboarding/src/ai_upsell_card.rs @@ -250,6 +250,15 @@ impl RenderOnce for AiUpsellCard { .mb_2(), ) .child(PlanDefinitions.pro_plan()), + Some(Plan::ZedStudent) => card + .child(certified_user_stamp) + .child(Label::new("You're in the Zed Student plan").size(LabelSize::Large)) + .child( + Label::new("Here's what you get:") + .color(Color::Muted) + .mb_2(), + ) + .child(PlanDefinitions.student_plan()), }, // Signed Out State _ => card diff --git a/crates/ai_onboarding/src/plan_definitions.rs b/crates/ai_onboarding/src/plan_definitions.rs index 7e1c6bbd19ddd9558ed44ee1bfa3dc61931c5e0c..cc29b9314b391af8573e17f9e474b47ea05d31c3 100644 --- a/crates/ai_onboarding/src/plan_definitions.rs +++ b/crates/ai_onboarding/src/plan_definitions.rs @@ -35,4 +35,13 @@ impl PlanDefinitions { .child(ListBulletItem::new("$5 of tokens")) .child(ListBulletItem::new("Usage-based billing beyond $5")) } + + pub fn student_plan(&self) -> impl IntoElement { + List::new() + .child(ListBulletItem::new("Unlimited edit predictions")) + .child(ListBulletItem::new("$10 of tokens")) + .child(ListBulletItem::new( + "Optional credit packs for additional usage", + )) + } } diff --git a/crates/cloud_api_types/src/plan.rs b/crates/cloud_api_types/src/plan.rs index ea2ffb665899b5696b90a77cd240758bb6ee8c1a..e4a33e3c1933717f642848acc13dcf19b173e902 100644 --- a/crates/cloud_api_types/src/plan.rs +++ b/crates/cloud_api_types/src/plan.rs @@ -9,6 +9,7 @@ pub enum Plan { ZedFree, ZedPro, ZedProTrial, + ZedStudent, } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -58,5 +59,8 @@ mod tests { let plan = serde_json::from_value::(json!("zed_pro_trial")).unwrap(); assert_eq!(plan, Plan::ZedProTrial); + + let plan = serde_json::from_value::(json!("zed_student")).unwrap(); + assert_eq!(plan, Plan::ZedStudent); } } diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 7cddfbd02994117a50ce448cfc664e91db4f4c05..38fc531a76ee1bbdac2b821e7f0d270919219ac6 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -965,6 +965,7 @@ impl TitleBar { None | Some(Plan::ZedFree) => ("Free", Color::Default, free_chip_bg), Some(Plan::ZedProTrial) => ("Pro Trial", Color::Accent, pro_chip_bg), Some(Plan::ZedPro) => ("Pro", Color::Accent, pro_chip_bg), + Some(Plan::ZedStudent) => ("Student", Color::Accent, pro_chip_bg), }; menu.when(is_signed_in, |this| {