From 11fd84018584403438447a7cacc8e6886b29b30c Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Fri, 22 Aug 2025 20:12:16 -0300 Subject: [PATCH 01/16] Add ACP grid --- assets/images/acp_grid.svg | 321 ++++++++++++++++++++++++++++++ crates/ui/src/components/image.rs | 1 + 2 files changed, 322 insertions(+) create mode 100644 assets/images/acp_grid.svg diff --git a/assets/images/acp_grid.svg b/assets/images/acp_grid.svg new file mode 100644 index 00000000000000..b9952ee259a377 --- /dev/null +++ b/assets/images/acp_grid.svg @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/ui/src/components/image.rs b/crates/ui/src/components/image.rs index 09c3bbeb943ca1..04ab57d9d9d299 100644 --- a/crates/ui/src/components/image.rs +++ b/crates/ui/src/components/image.rs @@ -13,6 +13,7 @@ use crate::prelude::*; )] #[strum(serialize_all = "snake_case")] pub enum VectorName { + AcpGrid, AiGrid, DebuggerGrid, Grid, From c68cc4016602bc075a367f1619006a5794c480e2 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Fri, 22 Aug 2025 20:12:26 -0300 Subject: [PATCH 02/16] Add onboarding modal --- crates/agent_ui/src/agent_panel.rs | 9 +- crates/agent_ui/src/ui.rs | 2 + .../agent_ui/src/ui/acp_onboarding_modal.rs | 165 ++++++++++++++++++ crates/zed_actions/src/lib.rs | 2 + 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 crates/agent_ui/src/ui/acp_onboarding_modal.rs diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 0e611d0db95a0d..574af90d3ca038 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; use crate::acp::{AcpThreadHistory, ThreadHistoryEvent}; use crate::agent_diff::AgentDiffThread; +use crate::ui::AcpOnboardingModal; use crate::{ AddContextServer, AgentDiffPane, ContinueThread, ContinueWithBurnMode, DeleteRecentlyOpenThread, ExpandMessageEditor, Follow, InlineAssistant, NewTextThread, @@ -74,7 +75,10 @@ use workspace::{ }; use zed_actions::{ DecreaseBufferFontSize, IncreaseBufferFontSize, ResetBufferFontSize, - agent::{OpenOnboardingModal, OpenSettings, ResetOnboarding, ToggleModelSelector}, + agent::{ + OpenAcpOnboardingModal, OpenOnboardingModal, OpenSettings, ResetOnboarding, + ToggleModelSelector, + }, assistant::{OpenRulesLibrary, ToggleFocus}, }; @@ -198,6 +202,9 @@ pub fn init(cx: &mut App) { .register_action(|workspace, _: &OpenOnboardingModal, window, cx| { AgentOnboardingModal::toggle(workspace, window, cx) }) + .register_action(|workspace, _: &OpenAcpOnboardingModal, window, cx| { + AcpOnboardingModal::toggle(workspace, window, cx) + }) .register_action(|_workspace, _: &ResetOnboarding, window, cx| { window.dispatch_action(workspace::RestoreBanner.boxed_clone(), cx); window.refresh(); diff --git a/crates/agent_ui/src/ui.rs b/crates/agent_ui/src/ui.rs index ada973cddfc847..600698b07e1e2b 100644 --- a/crates/agent_ui/src/ui.rs +++ b/crates/agent_ui/src/ui.rs @@ -1,3 +1,4 @@ +mod acp_onboarding_modal; mod agent_notification; mod burn_mode_tooltip; mod context_pill; @@ -6,6 +7,7 @@ mod onboarding_modal; pub mod preview; mod unavailable_editing_tooltip; +pub use acp_onboarding_modal::*; pub use agent_notification::*; pub use burn_mode_tooltip::*; pub use context_pill::*; diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs new file mode 100644 index 00000000000000..5ce4758cb156cf --- /dev/null +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -0,0 +1,165 @@ +use gpui::{ + ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent, Render, +}; +use ui::{TintColor, Vector, VectorName, prelude::*}; +use workspace::{ModalView, Workspace}; + +use crate::agent_panel::{AgentPanel, AgentType}; + +macro_rules! acp_onboarding_event { + ($name:expr) => { + telemetry::event!($name, source = "ACP Onboarding"); + }; + ($name:expr, $($key:ident $(= $value:expr)?),+ $(,)?) => { + telemetry::event!($name, source = "ACP Onboarding", $($key $(= $value)?),+); + }; +} + +pub struct AcpOnboardingModal { + focus_handle: FocusHandle, + workspace: Entity, +} + +impl AcpOnboardingModal { + 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); + + if let Some(panel) = workspace.panel::(cx) { + panel.update(cx, |panel, cx| { + panel.new_agent_thread(AgentType::Gemini, window, cx); + }); + } + }); + + cx.emit(DismissEvent); + + acp_onboarding_event!("Open Panel Clicked"); + } + + fn view_docs(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { + cx.open_url("https://zed.dev/blog/"); // TODO: Add link + cx.notify(); + + acp_onboarding_event!("Docs Link Clicked"); + } + + fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { + cx.emit(DismissEvent); + } +} + +impl EventEmitter for AcpOnboardingModal {} + +impl Focusable for AcpOnboardingModal { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl ModalView for AcpOnboardingModal {} + +impl Render for AcpOnboardingModal { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let base = v_flex() + .id("acp-onboarding") + .key_context("AcpOnboardingModal") + .relative() + .w(rems(34.)) + .h_full() + .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| { + acp_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::AcpGrid, 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("Bring Your Own Agent to Zed").size(HeadlineSize::Large)) + .child(Headline::new("featuring Gemini CLI").size(HeadlineSize::Large)), + ) + .child(h_flex().absolute().top_2().right_2().child( + IconButton::new("cancel", IconName::Close).on_click(cx.listener( + |_, _: &ClickEvent, _window, cx| { + acp_onboarding_event!("Cancelled", trigger = "X click"); + cx.emit(DismissEvent); + }, + )), + )); + + let open_panel_button = Button::new("open-panel", "Start with Gemini CLI") + .icon_size(IconSize::Indicator) + .style(ButtonStyle::Tinted(TintColor::Accent)) + .full_width() + .on_click(cx.listener(Self::open_panel)); + + let docs_button = Button::new("view-post", "Read Blog Post") + .icon(IconName::ArrowUpRight) + .icon_size(IconSize::Indicator) + .icon_color(Color::Muted) + .full_width() + .on_click(cx.listener(Self::view_docs)); + + let copy = "Zed now lets you bring the agent of your choice through the new Agent Client Protocol, starting with Google's Gemini CLI integration."; + + base.child(Label::new(copy).color(Color::Muted)).child( + v_flex() + .w_full() + .mt_2() + .gap_2() + .child(open_panel_button) + .child(docs_button), + ) + } +} diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index 069abc0a127365..9f3100d0e2fc6d 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -284,6 +284,8 @@ pub mod agent { OpenSettings, /// Opens the agent onboarding modal. OpenOnboardingModal, + /// Opens the ACP onboarding modal. + OpenAcpOnboardingModal, /// Resets the agent onboarding state. ResetOnboarding, /// Starts a chat conversation with the agent. From d1516b8dcd6267fce1bc63a03095f9b5a7a39c2d Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Fri, 22 Aug 2025 20:12:32 -0300 Subject: [PATCH 03/16] Add it to the title bar --- crates/title_bar/src/title_bar.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index b84a2800b65f5a..3858727ba58112 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -275,11 +275,11 @@ impl TitleBar { let banner = cx.new(|cx| { OnboardingBanner::new( - "Debugger Onboarding", - IconName::Debug, - "The Debugger", + "Gemini CLI Onboarding", + IconName::AiGemini, + "Gemini CLI in Zed", None, - zed_actions::debugger::OpenOnboardingModal.boxed_clone(), + zed_actions::agent::OpenAcpOnboardingModal.boxed_clone(), cx, ) }); From 5100b40b7c618d0a564556406ae24cff1aef47ec Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Fri, 22 Aug 2025 20:15:36 -0300 Subject: [PATCH 04/16] Improve the ACP grid --- assets/images/acp_grid.svg | 388 ++++++++++--------------------------- 1 file changed, 100 insertions(+), 288 deletions(-) diff --git a/assets/images/acp_grid.svg b/assets/images/acp_grid.svg index b9952ee259a377..07e0b95e4cc962 100644 --- a/assets/images/acp_grid.svg +++ b/assets/images/acp_grid.svg @@ -1,321 +1,133 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + + + + + - + - + + + + + - + + + + + - - + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + From 4355e4190f93ccc9e3c7151940b3433d4c15210e Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Sat, 23 Aug 2025 10:10:16 -0300 Subject: [PATCH 05/16] Iterate --- crates/agent_ui/src/ui/acp_onboarding_modal.rs | 6 +++--- crates/title_bar/src/title_bar.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index 5ce4758cb156cf..1bc3ad61b4bd11 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -73,7 +73,7 @@ impl Render for AcpOnboardingModal { .id("acp-onboarding") .key_context("AcpOnboardingModal") .relative() - .w(rems(34.)) + .w(rems(32.)) .h_full() .p_4() .gap_2() @@ -122,7 +122,7 @@ impl Render for AcpOnboardingModal { .w_full() .gap_1() .child( - Label::new("Introducing") + Label::new("Now Available") .size(LabelSize::Small) .color(Color::Muted), ) @@ -151,7 +151,7 @@ impl Render for AcpOnboardingModal { .full_width() .on_click(cx.listener(Self::view_docs)); - let copy = "Zed now lets you bring the agent of your choice through the new Agent Client Protocol, starting with Google's Gemini CLI integration."; + let copy = "Zed now lets you bring the agent of your choice through the new\nAgent Client Protocol, starting with Google's Gemini CLI integration."; base.child(Label::new(copy).color(Color::Muted)).child( v_flex() diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 3858727ba58112..feab0ac305faa1 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -278,7 +278,7 @@ impl TitleBar { "Gemini CLI Onboarding", IconName::AiGemini, "Gemini CLI in Zed", - None, + Some("News:".into()), zed_actions::agent::OpenAcpOnboardingModal.boxed_clone(), cx, ) From 14526e6803bf0ae4ba1afc89b8c50405aeb12e71 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Sun, 24 Aug 2025 13:14:23 -0300 Subject: [PATCH 06/16] Iterate on design --- assets/images/acp_grid.svg | 1380 +++++++++++++++-- .../agent_ui/src/ui/acp_onboarding_modal.rs | 165 +- 2 files changed, 1347 insertions(+), 198 deletions(-) diff --git a/assets/images/acp_grid.svg b/assets/images/acp_grid.svg index 07e0b95e4cc962..8ebff8e1bc87b1 100644 --- a/assets/images/acp_grid.svg +++ b/assets/images/acp_grid.svg @@ -1,133 +1,1257 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index 1bc3ad61b4bd11..d5e352f2200d08 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -1,5 +1,6 @@ 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}; @@ -46,10 +47,10 @@ impl AcpOnboardingModal { } fn view_docs(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { - cx.open_url("https://zed.dev/blog/"); // TODO: Add link + cx.open_url("https://zed.dev/blog/extensible-agent-support-in-zed"); // TODO: Add final link cx.notify(); - acp_onboarding_event!("Docs Link Clicked"); + acp_onboarding_event!("Blog Post Link Clicked"); } fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { @@ -69,74 +70,65 @@ impl ModalView for AcpOnboardingModal {} impl Render for AcpOnboardingModal { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { - let base = v_flex() - .id("acp-onboarding") - .key_context("AcpOnboardingModal") + let illustration = h_flex() .relative() - .w(rems(32.)) - .h_full() - .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| { - acp_onboarding_event!("Canceled", trigger = "Action"); - cx.emit(DismissEvent); - })) - .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| { - this.focus_handle.focus(window); - })) + .h(rems_from_px(126.)) + .bg(cx.theme().colors().editor_background) + .border_b_1() + .border_color(cx.theme().colors().border_variant) + .justify_center() .child( - div() - .absolute() - .top(px(-8.0)) - .right_0() - .w(px(400.)) - .h(px(92.)) - .child( - Vector::new(VectorName::AcpGrid, 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, - ), - )), + 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.4), + 0.9, + ), + linear_color_stop( + cx.theme().colors().elevated_surface_background.opacity(0.), + 0., + ), + ))) .child( v_flex() - .w_full() - .gap_1() + .gap_px() .child( - Label::new("Now Available") - .size(LabelSize::Small) + Label::new("External Agents") + .ml_1() + .size(LabelSize::XSmall) .color(Color::Muted), ) - .child(Headline::new("Bring Your Own Agent to Zed").size(HeadlineSize::Large)) - .child(Headline::new("featuring Gemini CLI").size(HeadlineSize::Large)), + .child( + h_flex() + .px_1() + .py_0p5() + .gap_1() + .rounded_sm() + .bg(cx.theme().colors().element_active.opacity(0.2)) + .child( + Icon::new(IconName::AiGemini) + .size(IconSize::Small) + .color(Color::Muted), + ) + .child(Label::new("New Gemini CLI Thread").size(LabelSize::Small)), + ), + ); + + let heading = v_flex() + .w_full() + .gap_1() + .child( + Label::new("Now Available") + .size(LabelSize::Small) + .color(Color::Muted), ) - .child(h_flex().absolute().top_2().right_2().child( - IconButton::new("cancel", IconName::Close).on_click(cx.listener( - |_, _: &ClickEvent, _window, cx| { - acp_onboarding_event!("Cancelled", trigger = "X click"); - cx.emit(DismissEvent); - }, - )), - )); + .child(Headline::new("Bring Your Own Agent to Zed").size(HeadlineSize::Large)); + let copy = "Zed now lets you bring the agent of your choice through the new\nAgent Client Protocol, starting with Google's Gemini CLI integration."; let open_panel_button = Button::new("open-panel", "Start with Gemini CLI") .icon_size(IconSize::Indicator) @@ -151,15 +143,48 @@ impl Render for AcpOnboardingModal { .full_width() .on_click(cx.listener(Self::view_docs)); - let copy = "Zed now lets you bring the agent of your choice through the new\nAgent Client Protocol, starting with Google's Gemini CLI integration."; + let close_button = h_flex().absolute().top_2().right_2().child( + IconButton::new("cancel", IconName::Close).on_click(cx.listener( + |_, _: &ClickEvent, _window, cx| { + acp_onboarding_event!("Canceled", trigger = "X click"); + cx.emit(DismissEvent); + }, + )), + ); - base.child(Label::new(copy).color(Color::Muted)).child( - v_flex() - .w_full() - .mt_2() - .gap_2() - .child(open_panel_button) - .child(docs_button), - ) + v_flex() + .id("acp-onboarding") + .key_context("AcpOnboardingModal") + .relative() + .w(rems(32.)) + .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| { + acp_onboarding_event!("Canceled", trigger = "Action"); + cx.emit(DismissEvent); + })) + .on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _cx| { + this.focus_handle.focus(window); + })) + .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_2() + .child(open_panel_button) + .child(docs_button), + ), + ) + .child(close_button) } } From 046458ff095e248ef981fd4c092ceb2e460a8f2c Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Sun, 24 Aug 2025 13:16:22 -0300 Subject: [PATCH 07/16] Fix background overflow --- crates/agent_ui/src/ui/acp_onboarding_modal.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index d5e352f2200d08..d6a43199184b70 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -77,6 +77,7 @@ impl Render for AcpOnboardingModal { .border_b_1() .border_color(cx.theme().colors().border_variant) .justify_center() + .rounded_t_md() .child( div().absolute().inset_0().w(px(515.)).h(px(126.)).child( Vector::new(VectorName::AcpGrid, rems_from_px(515.), rems_from_px(126.)) From 110970695a10c47c7399858f811b7c23be2b6f36 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Mon, 25 Aug 2025 08:42:37 -0300 Subject: [PATCH 08/16] Iterate on design --- .../agent_ui/src/ui/acp_onboarding_modal.rs | 65 +++++++++++++++---- crates/title_bar/src/title_bar.rs | 2 +- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index d6a43199184b70..09f0b26732fddb 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -47,10 +47,10 @@ impl AcpOnboardingModal { } fn view_docs(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { - cx.open_url("https://zed.dev/blog/extensible-agent-support-in-zed"); // TODO: Add final link + cx.open_url("https://zed.dev/docs"); // TODO: Add final link cx.notify(); - acp_onboarding_event!("Blog Post Link Clicked"); + acp_onboarding_event!("Documentation Link Clicked"); } fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { @@ -70,6 +70,41 @@ impl ModalView for AcpOnboardingModal {} impl Render for AcpOnboardingModal { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let illustration_element = |label: bool, 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(IconName::Stop) + .size(IconSize::Small) + .color(Color::Custom(cx.theme().colors().text_muted.opacity(0.15))), + ) + .map(|this| { + if label { + this.child( + Label::new("Your Agent Here") + .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.)) @@ -78,10 +113,11 @@ impl Render for AcpOnboardingModal { .border_color(cx.theme().colors().border_variant) .justify_center() .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))), + .color(ui::Color::Custom(cx.theme().colors().text.opacity(0.012))), ), ) .child(div().absolute().inset_0().size_full().bg(linear_gradient( @@ -97,27 +133,28 @@ impl Render for AcpOnboardingModal { ))) .child( v_flex() - .gap_px() - .child( - Label::new("External Agents") - .ml_1() - .size(LabelSize::XSmall) - .color(Color::Muted), - ) + .gap_1p5() + .child(illustration_element(false, 0.15)) + .child(illustration_element(true, 0.3)) .child( h_flex() - .px_1() + .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::AiGemini) .size(IconSize::Small) .color(Color::Muted), ) .child(Label::new("New Gemini CLI Thread").size(LabelSize::Small)), - ), + ) + .child(illustration_element(true, 0.3)) + .child(illustration_element(false, 0.15)), ); let heading = v_flex() @@ -137,7 +174,7 @@ impl Render for AcpOnboardingModal { .full_width() .on_click(cx.listener(Self::open_panel)); - let docs_button = Button::new("view-post", "Read Blog Post") + let docs_button = Button::new("add-agent", "Add Your Own Agent") .icon(IconName::ArrowUpRight) .icon_size(IconSize::Indicator) .icon_color(Color::Muted) @@ -181,7 +218,7 @@ impl Render for AcpOnboardingModal { v_flex() .w_full() .mt_2() - .gap_2() + .gap_1() .child(open_panel_button) .child(docs_button), ), diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index feab0ac305faa1..a318fa8e55abad 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -275,7 +275,7 @@ impl TitleBar { let banner = cx.new(|cx| { OnboardingBanner::new( - "Gemini CLI Onboarding", + "ACP Onboarding", IconName::AiGemini, "Gemini CLI in Zed", Some("News:".into()), From ef490328500a330e74452e7b63aa78dad4fe198a Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Mon, 25 Aug 2025 17:27:52 -0300 Subject: [PATCH 09/16] Add docs link --- crates/agent_ui/src/ui/acp_onboarding_modal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index 09f0b26732fddb..ccdbbc05383984 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -47,7 +47,7 @@ impl AcpOnboardingModal { } fn view_docs(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { - cx.open_url("https://zed.dev/docs"); // TODO: Add final link + cx.open_url("https://agentclientprotocol.com/"); cx.notify(); acp_onboarding_event!("Documentation Link Clicked"); From 9ffad84a4d1956d3c8127b36458ab1eebf0552e5 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Mon, 25 Aug 2025 18:24:24 -0300 Subject: [PATCH 10/16] Update description copy --- crates/agent_ui/src/ui/acp_onboarding_modal.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index ccdbbc05383984..f6d30697e38c4a 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -166,7 +166,8 @@ impl Render for AcpOnboardingModal { .color(Color::Muted), ) .child(Headline::new("Bring Your Own Agent to Zed").size(HeadlineSize::Large)); - let copy = "Zed now lets you bring the agent of your choice through the new\nAgent Client Protocol, starting with Google's Gemini CLI integration."; + + let copy = "Bring the agent of your choice to Zed via our new Agent Client Protocol, starting with Google's Gemini CLI integration."; let open_panel_button = Button::new("open-panel", "Start with Gemini CLI") .icon_size(IconSize::Indicator) From fcf1d2ada61729d7cc3f4243cdfe1d14f0d53e1c Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Mon, 25 Aug 2025 20:39:02 -0300 Subject: [PATCH 11/16] Update modal illustration --- assets/images/acp_logo.svg | 1 + assets/images/acp_logo_serif.svg | 2 ++ .../agent_ui/src/ui/acp_onboarding_modal.rs | 28 +++++++++++++++++-- crates/ui/src/components/image.rs | 2 ++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 assets/images/acp_logo.svg create mode 100644 assets/images/acp_logo_serif.svg diff --git a/assets/images/acp_logo.svg b/assets/images/acp_logo.svg new file mode 100644 index 00000000000000..efaa46707be0a8 --- /dev/null +++ b/assets/images/acp_logo.svg @@ -0,0 +1 @@ + diff --git a/assets/images/acp_logo_serif.svg b/assets/images/acp_logo_serif.svg new file mode 100644 index 00000000000000..6bc359cf82dde8 --- /dev/null +++ b/assets/images/acp_logo_serif.svg @@ -0,0 +1,2 @@ + + diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index f6d30697e38c4a..7f157b291163d5 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -112,18 +112,19 @@ impl Render for AcpOnboardingModal { .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.012))), + .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.4), + cx.theme().colors().elevated_surface_background.opacity(0.1), 0.9, ), linear_color_stop( @@ -131,6 +132,29 @@ impl Render for AcpOnboardingModal { 0., ), ))) + .child( + div() + .absolute() + .inset_0() + .size_full() + .bg(gpui::black().opacity(0.15)), + ) + .child( + h_flex() + .gap_4() + .child( + Vector::new(VectorName::AcpLogo, rems_from_px(106.), rems_from_px(40.)) + .color(ui::Color::Custom(cx.theme().colors().text.opacity(0.8))), + ) + .child( + Vector::new( + VectorName::AcpLogoSerif, + rems_from_px(111.), + rems_from_px(41.), + ) + .color(ui::Color::Custom(cx.theme().colors().text.opacity(0.8))), + ), + ) .child( v_flex() .gap_1p5() diff --git a/crates/ui/src/components/image.rs b/crates/ui/src/components/image.rs index 04ab57d9d9d299..6e552ddcee83e2 100644 --- a/crates/ui/src/components/image.rs +++ b/crates/ui/src/components/image.rs @@ -14,6 +14,8 @@ use crate::prelude::*; #[strum(serialize_all = "snake_case")] pub enum VectorName { AcpGrid, + AcpLogo, + AcpLogoSerif, AiGrid, DebuggerGrid, Grid, From 6f53b9da1e4e9e599f952aa8cc712de18b0e1837 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Mon, 25 Aug 2025 20:44:20 -0300 Subject: [PATCH 12/16] Adjust title bar --- crates/title_bar/src/title_bar.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index a318fa8e55abad..ad64dac9c69863 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -276,9 +276,9 @@ impl TitleBar { let banner = cx.new(|cx| { OnboardingBanner::new( "ACP Onboarding", - IconName::AiGemini, - "Gemini CLI in Zed", - Some("News:".into()), + IconName::Sparkle, + "Bring Your Own Agent", + Some("Introducing:".into()), zed_actions::agent::OpenAcpOnboardingModal.boxed_clone(), cx, ) From 0f5750cfc0458b499874958ee14f52e846e8b47e Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Tue, 26 Aug 2025 12:44:27 -0300 Subject: [PATCH 13/16] Fix the selected agent logic Co-authored-by: Bennet Bo Fenner --- crates/agent_ui/src/agent_panel.rs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index cb4ee81740d8d9..5f6027b58c3287 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -1859,19 +1859,6 @@ impl AgentPanel { menu } - pub fn set_selected_agent( - &mut self, - agent: AgentType, - window: &mut Window, - cx: &mut Context, - ) { - if self.selected_agent != agent { - self.selected_agent = agent.clone(); - self.serialize(cx); - } - self.new_agent_thread(agent, window, cx); - } - pub fn selected_agent(&self) -> AgentType { self.selected_agent.clone() } @@ -1882,6 +1869,11 @@ impl AgentPanel { window: &mut Window, cx: &mut Context, ) { + if self.selected_agent != agent { + self.selected_agent = agent.clone(); + self.serialize(cx); + } + match agent { AgentType::Zed => { window.dispatch_action( @@ -2562,7 +2554,7 @@ impl AgentPanel { workspace.panel::(cx) { panel.update(cx, |panel, cx| { - panel.set_selected_agent( + panel.new_agent_thread( AgentType::NativeAgent, window, cx, @@ -2588,7 +2580,7 @@ impl AgentPanel { workspace.panel::(cx) { panel.update(cx, |panel, cx| { - panel.set_selected_agent( + panel.new_agent_thread( AgentType::TextThread, window, cx, @@ -2616,7 +2608,7 @@ impl AgentPanel { workspace.panel::(cx) { panel.update(cx, |panel, cx| { - panel.set_selected_agent( + panel.new_agent_thread( AgentType::Gemini, window, cx, @@ -2643,7 +2635,7 @@ impl AgentPanel { workspace.panel::(cx) { panel.update(cx, |panel, cx| { - panel.set_selected_agent( + panel.new_agent_thread( AgentType::ClaudeCode, window, cx, @@ -2676,7 +2668,7 @@ impl AgentPanel { workspace.panel::(cx) { panel.update(cx, |panel, cx| { - panel.set_selected_agent( + panel.new_agent_thread( AgentType::Custom { name: agent_name .clone(), From ff8af4f7961750ca75a76fab72fd8f58ddf4c554 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Tue, 26 Aug 2025 12:44:35 -0300 Subject: [PATCH 14/16] Improve copy in the settings view Co-authored-by: Bennet Bo Fenner --- crates/agent_ui/src/agent_configuration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 52fb7eed4b96e2..b1fa924c8425de 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -1061,7 +1061,7 @@ impl AgentConfiguration { .child(Headline::new("External Agents")) .child( Label::new( - "Use the full power of Zed's UI with your favorite agent, connected via the Agent Client Protocol.", + "Bring the agent of your choice to Zed via our new Agent Client Protocol", ) .color(Color::Muted), ), From 90aa529478cf3fc54733245e4f4df1503591aec6 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Tue, 26 Aug 2025 12:46:18 -0300 Subject: [PATCH 15/16] Make title bar icon smaller Co-authored-by: Bennet Bo Fenner --- crates/title_bar/src/onboarding_banner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/title_bar/src/onboarding_banner.rs b/crates/title_bar/src/onboarding_banner.rs index ed43c5277a51d6..1c289424900086 100644 --- a/crates/title_bar/src/onboarding_banner.rs +++ b/crates/title_bar/src/onboarding_banner.rs @@ -119,7 +119,7 @@ impl Render for OnboardingBanner { h_flex() .h_full() .gap_1() - .child(Icon::new(self.details.icon_name).size(IconSize::Small)) + .child(Icon::new(self.details.icon_name).size(IconSize::XSmall)) .child( h_flex() .gap_0p5() From 1024265b45f96a188fc2a5065a27255215b00a89 Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Tue, 26 Aug 2025 14:10:46 -0300 Subject: [PATCH 16/16] Add latest riffing --- crates/agent_ui/src/agent_configuration.rs | 2 +- crates/agent_ui/src/agent_panel.rs | 4 ++-- crates/agent_ui/src/ui/acp_onboarding_modal.rs | 9 +++++---- crates/client/src/zed_urls.rs | 8 ++++++++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 7a8a4797668e77..224f49cc3e11f7 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -1093,7 +1093,7 @@ impl AgentConfiguration { ) .child( Label::new( - "Bring the agent of your choice to Zed via our new Agent Client Protocol", + "Bring the agent of your choice to Zed via our new Agent Client Protocol.", ) .color(Color::Muted), ), diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 07420505ad8b90..d1cf748733127b 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -2681,9 +2681,9 @@ impl AgentPanel { }) .when(cx.has_flag::(), |menu| { menu.separator().link( - "Add Your Own Agent", + "Add Other Agents", OpenBrowser { - url: "https://agentclientprotocol.com/".into(), + url: zed_urls::external_agents_docs(cx), } .boxed_clone(), ) diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index 7f157b291163d5..0ed9de72210144 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -1,3 +1,4 @@ +use client::zed_urls; use gpui::{ ClickEvent, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, MouseDownEvent, Render, linear_color_stop, linear_gradient, @@ -47,7 +48,7 @@ impl AcpOnboardingModal { } fn view_docs(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context) { - cx.open_url("https://agentclientprotocol.com/"); + cx.open_url(&zed_urls::external_agents_docs(cx)); cx.notify(); acp_onboarding_event!("Documentation Link Clicked"); @@ -191,7 +192,7 @@ impl Render for AcpOnboardingModal { ) .child(Headline::new("Bring Your Own Agent to Zed").size(HeadlineSize::Large)); - let copy = "Bring the agent of your choice to Zed via our new Agent Client Protocol, starting with Google's Gemini CLI integration."; + let copy = "Bring the agent of your choice to Zed via our new Agent Client Protocol (ACP), starting with Google's Gemini CLI integration."; let open_panel_button = Button::new("open-panel", "Start with Gemini CLI") .icon_size(IconSize::Indicator) @@ -199,7 +200,7 @@ impl Render for AcpOnboardingModal { .full_width() .on_click(cx.listener(Self::open_panel)); - let docs_button = Button::new("add-agent", "Add Your Own Agent") + let docs_button = Button::new("add-other-agents", "Add Other Agents") .icon(IconName::ArrowUpRight) .icon_size(IconSize::Indicator) .icon_color(Color::Muted) @@ -219,7 +220,7 @@ impl Render for AcpOnboardingModal { .id("acp-onboarding") .key_context("AcpOnboardingModal") .relative() - .w(rems(32.)) + .w(rems(34.)) .h_full() .elevation_3(cx) .track_focus(&self.focus_handle(cx)) diff --git a/crates/client/src/zed_urls.rs b/crates/client/src/zed_urls.rs index 9df41906d79b4d..7193c099473c95 100644 --- a/crates/client/src/zed_urls.rs +++ b/crates/client/src/zed_urls.rs @@ -43,3 +43,11 @@ pub fn ai_privacy_and_security(cx: &App) -> String { server_url = server_url(cx) ) } + +/// Returns the URL to Zed AI's external agents documentation. +pub fn external_agents_docs(cx: &App) -> String { + format!( + "{server_url}/docs/ai/external-agents", + server_url = server_url(cx) + ) +}