From 300da3b7181bbee62671d5aece582a3e212ba779 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 6 May 2025 19:41:04 -0400 Subject: [PATCH] Add an onboarding banner for the Agent panel (#30050) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds an onboarding banner for the Agent panel: Screenshot 2025-05-06 at 6 54 58 PM Release Notes: - N/A --- crates/agent/src/assistant_panel.rs | 10 +- crates/agent/src/ui.rs | 2 + crates/agent/src/ui/onboarding_modal.rs | 157 ++++++++++++++++++++++++ crates/title_bar/src/title_bar.rs | 8 +- crates/zed_actions/src/lib.rs | 5 +- 5 files changed, 176 insertions(+), 6 deletions(-) create mode 100644 crates/agent/src/ui/onboarding_modal.rs diff --git a/crates/agent/src/assistant_panel.rs b/crates/agent/src/assistant_panel.rs index 4cff679aa9..6b46e95bae 100644 --- a/crates/agent/src/assistant_panel.rs +++ b/crates/agent/src/assistant_panel.rs @@ -47,7 +47,7 @@ use ui::{ use util::{ResultExt as _, maybe}; use workspace::dock::{DockPosition, Panel, PanelEvent}; use workspace::{CollaboratorId, DraggedSelection, DraggedTab, ToolbarItemView, Workspace}; -use zed_actions::agent::OpenConfiguration; +use zed_actions::agent::{OpenConfiguration, OpenOnboardingModal, ResetOnboarding}; use zed_actions::assistant::{OpenRulesLibrary, ToggleFocus}; use zed_actions::{DecreaseBufferFontSize, IncreaseBufferFontSize, ResetBufferFontSize}; use zed_llm_client::UsageLimit; @@ -60,6 +60,7 @@ use crate::message_editor::{MessageEditor, MessageEditorEvent}; use crate::thread::{Thread, ThreadError, ThreadId, TokenUsageRatio}; use crate::thread_history::{EntryTimeFormat, PastContext, PastThread, ThreadHistory}; use crate::thread_store::ThreadStore; +use crate::ui::AgentOnboardingModal; use crate::{ AddContextServer, AgentDiffPane, ContextStore, DeleteRecentlyOpenThread, ExpandMessageEditor, Follow, InlineAssistant, NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, @@ -146,6 +147,13 @@ pub fn init(cx: &mut App) { }); } }) + .register_action(|workspace, _: &OpenOnboardingModal, window, cx| { + AgentOnboardingModal::toggle(workspace, window, cx) + }) + .register_action(|_workspace, _: &ResetOnboarding, window, cx| { + window.dispatch_action(workspace::RestoreBanner.boxed_clone(), cx); + window.refresh(); + }) .register_action(|_workspace, _: &ResetTrialUpsell, _window, cx| { set_trial_upsell_dismissed(false, cx); }); diff --git a/crates/agent/src/ui.rs b/crates/agent/src/ui.rs index d494742a1e..c9adc2a631 100644 --- a/crates/agent/src/ui.rs +++ b/crates/agent/src/ui.rs @@ -2,6 +2,7 @@ mod agent_notification; mod animated_label; mod context_pill; mod max_mode_tooltip; +mod onboarding_modal; pub mod preview; mod upsell; @@ -9,3 +10,4 @@ pub use agent_notification::*; pub use animated_label::*; pub use context_pill::*; pub use max_mode_tooltip::*; +pub use onboarding_modal::*; diff --git a/crates/agent/src/ui/onboarding_modal.rs b/crates/agent/src/ui/onboarding_modal.rs new file mode 100644 index 0000000000..c9a014c3a2 --- /dev/null +++ b/crates/agent/src/ui/onboarding_modal.rs @@ -0,0 +1,157 @@ +use gpui::{ + ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent, Render, +}; +use ui::{TintColor, Vector, VectorName, prelude::*}; +use workspace::{ModalView, Workspace}; + +use crate::assistant_panel::AssistantPanel; + +macro_rules! agent_onboarding_event { + ($name:expr) => { + telemetry::event!($name, source = "Agent Onboarding"); + }; + ($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => { + telemetry::event!($name, source = "Agent Onboarding", $($key $(= $value)?),+); + }; +} + +pub struct AgentOnboardingModal { + focus_handle: FocusHandle, + workspace: Entity, +} + +impl AgentOnboardingModal { + pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context) { + 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.workspace.update(cx, |workspace, cx| { + workspace.focus_panel::(window, cx); + }); + + cx.emit(DismissEvent); + + agent_onboarding_event!("Open Panel Clicked"); + } + + fn view_blog(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { + cx.open_url("http://zed.dev/blog/fastest-ai-code-editor"); + cx.notify(); + + agent_onboarding_event!("Blog Link Clicked"); + } + + fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { + cx.emit(DismissEvent); + } +} + +impl EventEmitter for AgentOnboardingModal {} + +impl Focusable for AgentOnboardingModal { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl ModalView for AgentOnboardingModal {} + +impl Render for AgentOnboardingModal { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + let window_height = window.viewport_size().height; + let max_height = window_height - px(200.); + + let base = v_flex() + .id("agent-onboarding") + .key_context("AgentOnboardingModal") + .relative() + .w(px(450.)) + .h_full() + .max_h(max_height) + .p_4() + .gap_2() + .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| { + agent_onboarding_event!("Canceled", trigger = "Action"); + cx.emit(DismissEvent); + })) + .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| { + this.focus_handle.focus(window); + })) + .child( + div() + .absolute() + .top_0() + .right(px(-1.0)) + .w(px(441.)) + .h(px(167.)) + .child( + Vector::new(VectorName::Grid, rems_from_px(441.), rems_from_px(167.)) + .color(ui::Color::Custom(cx.theme().colors().text.alpha(0.1))), + ), + ) + .child( + div() + .absolute() + .top(px(-8.0)) + .right_0() + .w(px(400.)) + .h(px(92.)) + .child( + Vector::new(VectorName::AiGrid, rems_from_px(400.), rems_from_px(92.)) + .color(ui::Color::Custom(cx.theme().colors().text.alpha(0.32))), + ), + ) + .child( + v_flex() + .w_full() + .gap_1() + .child( + Label::new("Introducing") + .size(LabelSize::Small) + .color(Color::Muted), + ) + .child(Headline::new("Agentic Editing in Zed").size(HeadlineSize::Large)), + ) + .child(h_flex().absolute().top_2().right_2().child( + IconButton::new("cancel", IconName::X).on_click(cx.listener( + |_, _: &ClickEvent, _window, cx| { + agent_onboarding_event!("Cancelled", trigger = "X click"); + cx.emit(DismissEvent); + }, + )), + )); + + let open_panel_button = Button::new("open-panel", "Get Started with the Agent Panel") + .icon_size(IconSize::Indicator) + .style(ButtonStyle::Tinted(TintColor::Accent)) + .full_width() + .on_click(cx.listener(Self::open_panel)); + + let blog_post_button = Button::new("view-blog", "Check out the Blog Post") + .icon(IconName::ArrowUpRight) + .icon_size(IconSize::Indicator) + .icon_color(Color::Muted) + .full_width() + .on_click(cx.listener(Self::view_blog)); + + let copy = "Zed natively supports agentic editing, enabling seamless collaboration between humans and AI."; + + base.child(Label::new(copy).color(Color::Muted)).child( + v_flex() + .w_full() + .mt_2() + .gap_2() + .child(open_panel_button) + .child(blog_post_button), + ) + } +} diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 50afd0d359..5b68fa4be0 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -316,11 +316,11 @@ impl TitleBar { let banner = cx.new(|cx| { OnboardingBanner::new( - "Git Onboarding", - IconName::GitBranchSmall, - "Git Support", + "Agentic Onboarding", + IconName::ZedAssistant, + "Agentic Editing", None, - zed_actions::OpenGitIntegrationOnboarding.boxed_clone(), + zed_actions::agent::OpenOnboardingModal.boxed_clone(), cx, ) }); diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index 0092554cd0..3ebce6a87b 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -186,7 +186,10 @@ pub mod icon_theme_selector { pub mod agent { use gpui::actions; - actions!(agent, [OpenConfiguration]); + actions!( + agent, + [OpenConfiguration, OpenOnboardingModal, ResetOnboarding] + ); } pub mod assistant {