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}