From db99d7131e7aaa06778ded34949413e8d9c6606d Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 18 Jun 2025 20:44:02 +0200 Subject: [PATCH] debugger: Add onboarding modal (#32961) - **debugger: Add debugger onboarding modal (wip)** - **woops** Release Notes: - debugger: Added the onboarding modal. --------- Co-authored-by: Danilo Leal Co-authored-by: Julia Ryan --- Cargo.lock | 1 + assets/images/debugger_grid.svg | 890 +++++++++++++++++++++ crates/debugger_ui/Cargo.toml | 1 + crates/debugger_ui/src/debugger_panel.rs | 5 +- crates/debugger_ui/src/debugger_ui.rs | 9 +- crates/debugger_ui/src/onboarding_modal.rs | 166 ++++ crates/title_bar/src/title_bar.rs | 8 +- crates/ui/src/components/image.rs | 1 + crates/zed_actions/src/lib.rs | 7 + 9 files changed, 1080 insertions(+), 8 deletions(-) create mode 100644 assets/images/debugger_grid.svg create mode 100644 crates/debugger_ui/src/onboarding_modal.rs diff --git a/Cargo.lock b/Cargo.lock index 7322436f51..e69eb264ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4300,6 +4300,7 @@ dependencies = [ "sysinfo", "task", "tasks_ui", + "telemetry", "terminal_view", "theme", "tree-sitter", diff --git a/assets/images/debugger_grid.svg b/assets/images/debugger_grid.svg new file mode 100644 index 0000000000..8b40dbd707 --- /dev/null +++ b/assets/images/debugger_grid.svg @@ -0,0 +1,890 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index 17570bd3d9..e259b8a4b3 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -57,6 +57,7 @@ shlex.workspace = true sysinfo.workspace = true task.workspace = true tasks_ui.workspace = true +telemetry.workspace = true terminal_view.workspace = true theme.workspace = true tree-sitter.workspace = true diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 159f97a3cd..12f0c78b43 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -19,7 +19,7 @@ use dap::{DapRegistry, StartDebuggingRequestArguments}; use gpui::{ Action, App, AsyncWindowContext, ClipboardItem, Context, DismissEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, - WeakEntity, actions, anchored, deferred, + WeakEntity, anchored, deferred, }; use itertools::Itertools as _; @@ -39,6 +39,7 @@ use workspace::{ Pane, Workspace, dock::{DockPosition, Panel, PanelEvent}, }; +use zed_actions::ToggleFocus; pub enum DebugPanelEvent { Exited(SessionId), @@ -57,8 +58,6 @@ pub enum DebugPanelEvent { CapabilitiesChanged(SessionId), } -actions!(debug_panel, [ToggleFocus]); - pub struct DebugPanel { size: Pixels, sessions: Vec>, diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 69ef35e7d7..ade1308f06 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -1,10 +1,11 @@ use std::any::TypeId; use dap::debugger_settings::DebuggerSettings; -use debugger_panel::{DebugPanel, ToggleFocus}; +use debugger_panel::DebugPanel; use editor::Editor; use gpui::{App, DispatchPhase, EntityInputHandler, actions}; use new_process_modal::{NewProcessModal, NewProcessMode}; +use onboarding_modal::DebuggerOnboardingModal; use project::debugger::{self, breakpoint_store::SourceBreakpoint, session::ThreadStatus}; use session::DebugSession; use settings::Settings; @@ -13,11 +14,14 @@ use tasks_ui::{Spawn, TaskOverrides}; use ui::{FluentBuilder, InteractiveElement}; use util::maybe; use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace}; +use zed_actions::ToggleFocus; +use zed_actions::debugger::OpenOnboardingModal; pub mod attach_modal; pub mod debugger_panel; mod dropdown_menus; mod new_process_modal; +mod onboarding_modal; mod persistence; pub(crate) mod session; mod stack_trace_view; @@ -90,6 +94,9 @@ pub fn init(cx: &mut App) { }) }, ) + .register_action(|workspace, _: &OpenOnboardingModal, window, cx| { + DebuggerOnboardingModal::toggle(workspace, window, cx) + }) .register_action_renderer(|div, workspace, _, cx| { let Some(debug_panel) = workspace.panel::(cx) else { return div; diff --git a/crates/debugger_ui/src/onboarding_modal.rs b/crates/debugger_ui/src/onboarding_modal.rs new file mode 100644 index 0000000000..c9fa009940 --- /dev/null +++ b/crates/debugger_ui/src/onboarding_modal.rs @@ -0,0 +1,166 @@ +use gpui::{ + ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent, Render, +}; +use ui::{TintColor, Vector, VectorName, prelude::*}; +use workspace::{ModalView, Workspace}; + +use crate::DebugPanel; + +macro_rules! debugger_onboarding_event { + ($name:expr) => { + telemetry::event!($name, source = "Debugger Onboarding"); + }; + ($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => { + telemetry::event!($name, source = "Debugger Onboarding", $($key $(= $value)?),+); + }; +} + +pub struct DebuggerOnboardingModal { + focus_handle: FocusHandle, + workspace: Entity, +} + +impl DebuggerOnboardingModal { + 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); + + debugger_onboarding_event!("Open Panel Clicked"); + } + + fn view_blog(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { + cx.open_url("http://zed.dev/blog/debugger"); + cx.notify(); + + debugger_onboarding_event!("Blog Link Clicked"); + } + + fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { + cx.emit(DismissEvent); + } +} + +impl EventEmitter for DebuggerOnboardingModal {} + +impl Focusable for DebuggerOnboardingModal { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl ModalView for DebuggerOnboardingModal {} + +impl Render for DebuggerOnboardingModal { + 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("debugger-onboarding") + .key_context("DebuggerOnboardingModal") + .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| { + debugger_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(px(-8.0)) + .right_0() + .w(px(400.)) + .h(px(92.)) + .child( + Vector::new( + VectorName::DebuggerGrid, + rems_from_px(400.), + rems_from_px(92.), + ) + .color(ui::Color::Custom(cx.theme().colors().text.alpha(0.32))), + ), + ) + .child( + div() + .absolute() + .inset_0() + .size_full() + .bg(gpui::linear_gradient( + 175., + gpui::linear_color_stop( + cx.theme().colors().elevated_surface_background, + 0., + ), + gpui::linear_color_stop( + cx.theme().colors().elevated_surface_background.opacity(0.), + 0.8, + ), + )), + ) + .child( + v_flex() + .w_full() + .gap_1() + .child( + Label::new("Introducing") + .size(LabelSize::Small) + .color(Color::Muted), + ) + .child(Headline::new("Zed's Debugger").size(HeadlineSize::Large)), + ) + .child(h_flex().absolute().top_2().right_2().child( + IconButton::new("cancel", IconName::X).on_click(cx.listener( + |_, _: &ClickEvent, _window, cx| { + debugger_onboarding_event!("Cancelled", trigger = "X click"); + cx.emit(DismissEvent); + }, + )), + )); + + let open_panel_button = Button::new("open-panel", "Get Started with the Debugger") + .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 = "It's finally here: Native support for debugging across multiple programming languages."; + + 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 d5a4d50580..418bb70f8d 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -336,11 +336,11 @@ impl TitleBar { let banner = cx.new(|cx| { OnboardingBanner::new( - "Agentic Onboarding", - IconName::ZedAssistant, - "Agentic Editing", + "Debugger Onboarding", + IconName::Debug, + "The Debugger", None, - zed_actions::agent::OpenOnboardingModal.boxed_clone(), + zed_actions::debugger::OpenOnboardingModal.boxed_clone(), cx, ) }); diff --git a/crates/ui/src/components/image.rs b/crates/ui/src/components/image.rs index 38b7a9ae29..2deba68d88 100644 --- a/crates/ui/src/components/image.rs +++ b/crates/ui/src/components/image.rs @@ -16,6 +16,7 @@ pub enum VectorName { ZedXCopilot, Grid, AiGrid, + DebuggerGrid, } impl VectorName { diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index 5c28140b11..7dd29d72fc 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -249,6 +249,12 @@ pub mod assistant { impl_actions!(assistant, [InlineAssist]); } +pub mod debugger { + use gpui::actions; + + actions!(debugger, [OpenOnboardingModal, ResetOnboarding]); +} + #[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)] #[serde(deny_unknown_fields)] pub struct OpenRecent { @@ -349,6 +355,7 @@ pub mod outline { actions!(zed_predict_onboarding, [OpenZedPredictOnboarding]); actions!(git_onboarding, [OpenGitIntegrationOnboarding]); +actions!(debug_panel, [ToggleFocus]); actions!( debugger, [