Remove Claude upsell (#52831)

Conrad Irwin created

Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Release Notes:

- Removed the (broken) Claude ACP upsell dialogue

Change summary

crates/agent_ui/src/agent_panel.rs                      |  11 
crates/agent_ui/src/ui.rs                               |   2 
crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs | 261 -----------
crates/title_bar/src/onboarding_banner.rs               |  26 
crates/title_bar/src/title_bar.rs                       |  21 
crates/zed_actions/src/lib.rs                           |   2 
6 files changed, 23 insertions(+), 300 deletions(-)

Detailed changes

crates/agent_ui/src/agent_panel.rs 🔗

@@ -21,8 +21,8 @@ use settings::{LanguageModelProviderSetting, LanguageModelSelection};
 
 use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt as _};
 use zed_actions::agent::{
-    AddSelectionToThread, ConflictContent, OpenClaudeAgentOnboardingModal, ReauthenticateAgent,
-    ResolveConflictedFilesWithAgent, ResolveConflictsWithAgent, ReviewBranchDiff,
+    AddSelectionToThread, ConflictContent, ReauthenticateAgent, ResolveConflictedFilesWithAgent,
+    ResolveConflictsWithAgent, ReviewBranchDiff,
 };
 
 use crate::{
@@ -40,7 +40,7 @@ use crate::{
 };
 use crate::{
     DEFAULT_THREAD_TITLE,
-    ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal, HoldForDefault},
+    ui::{AcpOnboardingModal, HoldForDefault},
 };
 use crate::{ExpandMessageEditor, ThreadHistoryView};
 use crate::{ManageProfiles, ThreadHistoryViewEvent};
@@ -245,11 +245,6 @@ pub fn init(cx: &mut App) {
                 .register_action(|workspace, _: &OpenAcpOnboardingModal, window, cx| {
                     AcpOnboardingModal::toggle(workspace, window, cx)
                 })
-                .register_action(
-                    |workspace, _: &OpenClaudeAgentOnboardingModal, window, cx| {
-                        ClaudeCodeOnboardingModal::toggle(workspace, window, cx)
-                    },
-                )
                 .register_action(|_workspace, _: &ResetOnboarding, window, cx| {
                     window.dispatch_action(workspace::RestoreBanner.boxed_clone(), cx);
                     window.refresh();

crates/agent_ui/src/ui.rs 🔗

@@ -1,6 +1,5 @@
 mod acp_onboarding_modal;
 mod agent_notification;
-mod claude_agent_onboarding_modal;
 mod end_trial_upsell;
 mod hold_for_default;
 mod mention_crease;
@@ -9,7 +8,6 @@ mod undo_reject_toast;
 
 pub use acp_onboarding_modal::*;
 pub use agent_notification::*;
-pub use claude_agent_onboarding_modal::*;
 pub use end_trial_upsell::*;
 pub use hold_for_default::*;
 pub use mention_crease::*;

crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs 🔗

@@ -1,261 +0,0 @@
-use agent_servers::CLAUDE_AGENT_ID;
-use gpui::{
-    ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent, Render,
-    linear_color_stop, linear_gradient,
-};
-use ui::{TintColor, Vector, VectorName, prelude::*};
-use workspace::{ModalView, Workspace};
-
-use crate::{Agent, agent_panel::AgentPanel};
-
-macro_rules! claude_agent_onboarding_event {
-    ($name:expr) => {
-        telemetry::event!($name, source = "ACP Claude Code Onboarding");
-    };
-    ($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => {
-        telemetry::event!($name, source = "ACP Claude Code Onboarding", $($key $(= $value)?),+);
-    };
-}
-
-pub struct ClaudeCodeOnboardingModal {
-    focus_handle: FocusHandle,
-    workspace: Entity<Workspace>,
-}
-
-impl ClaudeCodeOnboardingModal {
-    pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
-        let workspace_entity = cx.entity();
-        workspace.toggle_modal(window, cx, |_window, cx| Self {
-            workspace: workspace_entity,
-            focus_handle: cx.focus_handle(),
-        });
-    }
-
-    fn open_panel(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
-        self.workspace.update(cx, |workspace, cx| {
-            workspace.focus_panel::<AgentPanel>(window, cx);
-
-            if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
-                panel.update(cx, |panel, cx| {
-                    panel.new_agent_thread(
-                        Agent::Custom {
-                            id: CLAUDE_AGENT_ID.into(),
-                        },
-                        window,
-                        cx,
-                    );
-                });
-            }
-        });
-
-        cx.emit(DismissEvent);
-
-        claude_agent_onboarding_event!("Open Panel Clicked");
-    }
-
-    fn view_docs(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
-        window.dispatch_action(Box::new(zed_actions::AcpRegistry), cx);
-        cx.notify();
-
-        claude_agent_onboarding_event!("Documentation Link Clicked");
-    }
-
-    fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
-        cx.emit(DismissEvent);
-    }
-}
-
-impl EventEmitter<DismissEvent> for ClaudeCodeOnboardingModal {}
-
-impl Focusable for ClaudeCodeOnboardingModal {
-    fn focus_handle(&self, _cx: &App) -> FocusHandle {
-        self.focus_handle.clone()
-    }
-}
-
-impl ModalView for ClaudeCodeOnboardingModal {}
-
-impl Render for ClaudeCodeOnboardingModal {
-    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        let illustration_element = |icon: IconName, label: Option<SharedString>, opacity: f32| {
-            h_flex()
-                .px_1()
-                .py_0p5()
-                .gap_1()
-                .rounded_sm()
-                .bg(cx.theme().colors().element_active.opacity(0.05))
-                .border_1()
-                .border_color(cx.theme().colors().border)
-                .border_dashed()
-                .child(
-                    Icon::new(icon)
-                        .size(IconSize::Small)
-                        .color(Color::Custom(cx.theme().colors().text_muted.opacity(0.15))),
-                )
-                .map(|this| {
-                    if let Some(label_text) = label {
-                        this.child(
-                            Label::new(label_text)
-                                .size(LabelSize::Small)
-                                .color(Color::Muted),
-                        )
-                    } else {
-                        this.child(
-                            div().w_16().h_1().rounded_full().bg(cx
-                                .theme()
-                                .colors()
-                                .element_active
-                                .opacity(0.6)),
-                        )
-                    }
-                })
-                .opacity(opacity)
-        };
-
-        let illustration = h_flex()
-            .relative()
-            .h(rems_from_px(126.))
-            .bg(cx.theme().colors().editor_background)
-            .border_b_1()
-            .border_color(cx.theme().colors().border_variant)
-            .justify_center()
-            .gap_8()
-            .rounded_t_md()
-            .overflow_hidden()
-            .child(
-                div().absolute().inset_0().w(px(515.)).h(px(126.)).child(
-                    Vector::new(VectorName::AcpGrid, rems_from_px(515.), rems_from_px(126.))
-                        .color(ui::Color::Custom(cx.theme().colors().text.opacity(0.02))),
-                ),
-            )
-            .child(div().absolute().inset_0().size_full().bg(linear_gradient(
-                0.,
-                linear_color_stop(
-                    cx.theme().colors().elevated_surface_background.opacity(0.1),
-                    0.9,
-                ),
-                linear_color_stop(
-                    cx.theme().colors().elevated_surface_background.opacity(0.),
-                    0.,
-                ),
-            )))
-            .child(
-                div()
-                    .absolute()
-                    .inset_0()
-                    .size_full()
-                    .bg(gpui::black().opacity(0.15)),
-            )
-            .child(
-                Vector::new(
-                    VectorName::AcpLogoSerif,
-                    rems_from_px(257.),
-                    rems_from_px(47.),
-                )
-                .color(ui::Color::Custom(cx.theme().colors().text.opacity(0.8))),
-            )
-            .child(
-                v_flex()
-                    .gap_1p5()
-                    .child(illustration_element(IconName::Stop, None, 0.15))
-                    .child(illustration_element(
-                        IconName::AiGemini,
-                        Some("New Gemini CLI Thread".into()),
-                        0.3,
-                    ))
-                    .child(
-                        h_flex()
-                            .pl_1()
-                            .pr_2()
-                            .py_0p5()
-                            .gap_1()
-                            .rounded_sm()
-                            .bg(cx.theme().colors().element_active.opacity(0.2))
-                            .border_1()
-                            .border_color(cx.theme().colors().border)
-                            .child(
-                                Icon::new(IconName::AiClaude)
-                                    .size(IconSize::Small)
-                                    .color(Color::Muted),
-                            )
-                            .child(Label::new("New Claude Agent Thread").size(LabelSize::Small)),
-                    )
-                    .child(illustration_element(
-                        IconName::Stop,
-                        Some("Your Agent Here".into()),
-                        0.3,
-                    ))
-                    .child(illustration_element(IconName::Stop, None, 0.15)),
-            );
-
-        let heading = v_flex()
-            .w_full()
-            .gap_1()
-            .child(
-                Label::new("Beta Release")
-                    .size(LabelSize::Small)
-                    .color(Color::Muted),
-            )
-            .child(Headline::new("Claude Agent: Natively in Zed").size(HeadlineSize::Large));
-
-        let copy = "Powered by the Agent Client Protocol, you can now run Claude Agent as\na first-class citizen in Zed's agent panel.";
-
-        let open_panel_button = Button::new("open-panel", "Start with Claude Agent")
-            .style(ButtonStyle::Tinted(TintColor::Accent))
-            .full_width()
-            .on_click(cx.listener(Self::open_panel));
-
-        let docs_button = Button::new("add-other-agents", "Add Other Agents")
-            .end_icon(
-                Icon::new(IconName::ArrowUpRight)
-                    .size(IconSize::Indicator)
-                    .color(Color::Muted),
-            )
-            .full_width()
-            .on_click(cx.listener(Self::view_docs));
-
-        let close_button = h_flex().absolute().top_2().right_2().child(
-            IconButton::new("cancel", IconName::Close).on_click(cx.listener(
-                |_, _: &ClickEvent, _window, cx| {
-                    claude_agent_onboarding_event!("Canceled", trigger = "X click");
-                    cx.emit(DismissEvent);
-                },
-            )),
-        );
-
-        v_flex()
-            .id("acp-onboarding")
-            .key_context("AcpOnboardingModal")
-            .relative()
-            .w(rems(34.))
-            .h_full()
-            .elevation_3(cx)
-            .track_focus(&self.focus_handle(cx))
-            .overflow_hidden()
-            .on_action(cx.listener(Self::cancel))
-            .on_action(cx.listener(|_, _: &menu::Cancel, _window, cx| {
-                claude_agent_onboarding_event!("Canceled", trigger = "Action");
-                cx.emit(DismissEvent);
-            }))
-            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, cx| {
-                this.focus_handle.focus(window, cx);
-            }))
-            .child(illustration)
-            .child(
-                v_flex()
-                    .p_4()
-                    .gap_2()
-                    .child(heading)
-                    .child(Label::new(copy).color(Color::Muted))
-                    .child(
-                        v_flex()
-                            .w_full()
-                            .mt_2()
-                            .gap_1()
-                            .child(open_panel_button)
-                            .child(docs_button),
-                    ),
-            )
-            .child(close_button)
-    }
-}

crates/title_bar/src/onboarding_banner.rs 🔗

@@ -1,3 +1,7 @@
+// This module provides infrastructure for showing onboarding banners in the title bar.
+// It's currently not in use but is kept for future feature announcements.
+#![allow(dead_code)]
+
 use gpui::{Action, Entity, Global, Render, SharedString};
 use ui::{ButtonLike, Tooltip, prelude::*};
 use util::ResultExt;
@@ -94,21 +98,21 @@ fn persist_dismissed(source: &str, cx: &mut App) {
 }
 
 pub fn restore_banner(cx: &mut App) {
-    cx.defer(|cx| {
-        cx.global::<BannerGlobal>()
-            .entity
-            .clone()
-            .update(cx, |this, cx| {
+    if let Some(banner_global) = cx.try_global::<BannerGlobal>() {
+        let entity = banner_global.entity.clone();
+        cx.defer(move |cx| {
+            entity.update(cx, |this, cx| {
                 this.dismissed = false;
                 cx.notify();
             });
-    });
+        });
 
-    let source = &cx.global::<BannerGlobal>().entity.read(cx).source;
-    let dismissed_at = dismissed_at_key(source);
-    let kvp = db::kvp::KeyValueStore::global(cx);
-    cx.spawn(async move |_| kvp.delete_kvp(dismissed_at).await)
-        .detach_and_log_err(cx);
+        let source = &cx.global::<BannerGlobal>().entity.read(cx).source;
+        let dismissed_at = dismissed_at_key(source);
+        let kvp = db::kvp::KeyValueStore::global(cx);
+        cx.spawn(async move |_| kvp.delete_kvp(dismissed_at).await)
+            .detach_and_log_err(cx);
+    }
 }
 
 impl Render for OnboardingBanner {

crates/title_bar/src/title_bar.rs 🔗

@@ -155,7 +155,7 @@ pub struct TitleBar {
     multi_workspace: Option<WeakEntity<MultiWorkspace>>,
     application_menu: Option<Entity<ApplicationMenu>>,
     _subscriptions: Vec<Subscription>,
-    banner: Entity<OnboardingBanner>,
+    banner: Option<Entity<OnboardingBanner>>,
     update_version: Entity<UpdateVersion>,
     screen_share_popover_handle: PopoverMenuHandle<ContextMenu>,
     _diagnostics_subscription: Option<gpui::Subscription>,
@@ -246,7 +246,9 @@ impl Render for TitleBar {
         children.push(self.render_collaborator_list(window, cx).into_any_element());
 
         if title_bar_settings.show_onboarding_banner {
-            children.push(self.banner.clone().into_any_element())
+            if let Some(banner) = &self.banner {
+                children.push(banner.clone().into_any_element())
+            }
         }
 
         let status = self.client.status();
@@ -385,19 +387,6 @@ impl TitleBar {
             }));
         }
 
-        let banner = cx.new(|cx| {
-            OnboardingBanner::new(
-                "ACP Claude Code Onboarding",
-                IconName::AiClaude,
-                "Claude Agent",
-                Some("Introducing:".into()),
-                zed_actions::agent::OpenClaudeAgentOnboardingModal.boxed_clone(),
-                cx,
-            )
-            // When updating this to a non-AI feature release, remove this line.
-            .visible_when(|cx| !project::DisableAiSettings::get_global(cx).disable_ai)
-        });
-
         let update_version = cx.new(|cx| UpdateVersion::new(cx));
         let platform_titlebar = cx.new(|cx| {
             let mut titlebar = PlatformTitleBar::new(id, cx);
@@ -416,7 +405,7 @@ impl TitleBar {
             user_store,
             client,
             _subscriptions: subscriptions,
-            banner,
+            banner: None,
             update_version,
             screen_share_popover_handle: PopoverMenuHandle::default(),
             _diagnostics_subscription: None,

crates/zed_actions/src/lib.rs 🔗

@@ -450,8 +450,6 @@ pub mod agent {
             OpenOnboardingModal,
             /// Opens the ACP onboarding modal.
             OpenAcpOnboardingModal,
-            /// Opens the Claude Agent onboarding modal.
-            OpenClaudeAgentOnboardingModal,
             /// Resets the agent onboarding state.
             ResetOnboarding,
             /// Starts a chat conversation with the agent.