onboarding_modal.rs

  1use gpui::{
  2    ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent, Render,
  3};
  4use ui::{TintColor, Vector, VectorName, prelude::*};
  5use workspace::{ModalView, Workspace};
  6
  7use crate::agent_panel::AgentPanel;
  8
  9macro_rules! agent_onboarding_event {
 10    ($name:expr) => {
 11        telemetry::event!($name, source = "Agent Onboarding");
 12    };
 13    ($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => {
 14        telemetry::event!($name, source = "Agent Onboarding", $($key $(= $value)?),+);
 15    };
 16}
 17
 18pub struct AgentOnboardingModal {
 19    focus_handle: FocusHandle,
 20    workspace: Entity<Workspace>,
 21}
 22
 23impl AgentOnboardingModal {
 24    pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
 25        let workspace_entity = cx.entity();
 26        workspace.toggle_modal(window, cx, |_window, cx| Self {
 27            workspace: workspace_entity,
 28            focus_handle: cx.focus_handle(),
 29        });
 30    }
 31
 32    fn open_panel(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
 33        self.workspace.update(cx, |workspace, cx| {
 34            workspace.focus_panel::<AgentPanel>(window, cx);
 35        });
 36
 37        cx.emit(DismissEvent);
 38
 39        agent_onboarding_event!("Open Panel Clicked");
 40    }
 41
 42    fn view_blog(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context<Self>) {
 43        cx.open_url("https://zed.dev/blog/fastest-ai-code-editor");
 44        cx.notify();
 45
 46        agent_onboarding_event!("Blog Link Clicked");
 47    }
 48
 49    fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
 50        cx.emit(DismissEvent);
 51    }
 52}
 53
 54impl EventEmitter<DismissEvent> for AgentOnboardingModal {}
 55
 56impl Focusable for AgentOnboardingModal {
 57    fn focus_handle(&self, _cx: &App) -> FocusHandle {
 58        self.focus_handle.clone()
 59    }
 60}
 61
 62impl ModalView for AgentOnboardingModal {}
 63
 64impl Render for AgentOnboardingModal {
 65    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 66        let window_height = window.viewport_size().height;
 67        let max_height = window_height - px(200.);
 68
 69        let base = v_flex()
 70            .id("agent-onboarding")
 71            .key_context("AgentOnboardingModal")
 72            .relative()
 73            .w(px(450.))
 74            .h_full()
 75            .max_h(max_height)
 76            .p_4()
 77            .gap_2()
 78            .elevation_3(cx)
 79            .track_focus(&self.focus_handle(cx))
 80            .overflow_hidden()
 81            .on_action(cx.listener(Self::cancel))
 82            .on_action(cx.listener(|_, _: &menu::Cancel, _window, cx| {
 83                agent_onboarding_event!("Canceled", trigger = "Action");
 84                cx.emit(DismissEvent);
 85            }))
 86            .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| {
 87                this.focus_handle.focus(window);
 88            }))
 89            .child(
 90                div()
 91                    .absolute()
 92                    .top_0()
 93                    .right(px(-1.0))
 94                    .w(px(441.))
 95                    .h(px(240.))
 96                    .child(
 97                        Vector::new(VectorName::Grid, rems_from_px(441.), rems_from_px(240.))
 98                            .color(ui::Color::Custom(cx.theme().colors().text.alpha(0.05))),
 99                    ),
100            )
101            .child(
102                div()
103                    .absolute()
104                    .top(px(-8.0))
105                    .right_0()
106                    .w(px(400.))
107                    .h(px(92.))
108                    .child(
109                        Vector::new(VectorName::AiGrid, rems_from_px(400.), rems_from_px(92.))
110                            .color(ui::Color::Custom(cx.theme().colors().text.alpha(0.32))),
111                    ),
112            )
113            .child(
114                div()
115                    .absolute()
116                    .inset_0()
117                    .size_full()
118                    .bg(gpui::linear_gradient(
119                        175.,
120                        gpui::linear_color_stop(
121                            cx.theme().colors().elevated_surface_background,
122                            0.,
123                        ),
124                        gpui::linear_color_stop(
125                            cx.theme().colors().elevated_surface_background.opacity(0.),
126                            0.8,
127                        ),
128                    )),
129            )
130            .child(
131                v_flex()
132                    .w_full()
133                    .gap_1()
134                    .child(
135                        Label::new("Introducing")
136                            .size(LabelSize::Small)
137                            .color(Color::Muted),
138                    )
139                    .child(Headline::new("Agentic Editing in Zed").size(HeadlineSize::Large)),
140            )
141            .child(h_flex().absolute().top_2().right_2().child(
142                IconButton::new("cancel", IconName::Close).on_click(cx.listener(
143                    |_, _: &ClickEvent, _window, cx| {
144                        agent_onboarding_event!("Cancelled", trigger = "X click");
145                        cx.emit(DismissEvent);
146                    },
147                )),
148            ));
149
150        let open_panel_button = Button::new("open-panel", "Get Started with the Agent Panel")
151            .icon_size(IconSize::Indicator)
152            .style(ButtonStyle::Tinted(TintColor::Accent))
153            .full_width()
154            .on_click(cx.listener(Self::open_panel));
155
156        let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
157            .icon(IconName::ArrowUpRight)
158            .icon_size(IconSize::Indicator)
159            .icon_color(Color::Muted)
160            .full_width()
161            .on_click(cx.listener(Self::view_blog));
162
163        let copy = "Zed now natively supports agentic editing, enabling fluid collaboration between humans and AI.";
164
165        base.child(Label::new(copy).color(Color::Muted)).child(
166            v_flex()
167                .w_full()
168                .mt_2()
169                .gap_2()
170                .child(open_panel_button)
171                .child(blog_post_button),
172        )
173    }
174}