From 9adc3b4e82f1c3e825c597bedeb0b184876dcb78 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 23 Nov 2024 11:24:52 -0500 Subject: [PATCH] Break ground on `assistant2` (#21109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR breaks ground on a new `assistant2` crate. In order to see this new version of the assistant, both of the following must be true: 1. The `assistant2` feature flag is enabled for your user - It is **not** currently enabled for all staff. 2. You are running a development build of Zed The intent here is to enable the folks working on `assistant2` to incrementally land work onto `main` without breaking use of the current Assistant for anyone. Screenshot 2024-11-23 at 10 46 08 AM Release Notes: - N/A --- Cargo.lock | 14 +++ Cargo.toml | 2 + crates/assistant2/Cargo.toml | 22 ++++ crates/assistant2/LICENSE-GPL | 1 + crates/assistant2/src/assistant.rs | 40 +++++++ crates/assistant2/src/assistant_panel.rs | 123 ++++++++++++++++++++++ crates/feature_flags/src/feature_flags.rs | 10 ++ crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 1 + crates/zed/src/zed.rs | 34 +++++- 10 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 crates/assistant2/Cargo.toml create mode 120000 crates/assistant2/LICENSE-GPL create mode 100644 crates/assistant2/src/assistant.rs create mode 100644 crates/assistant2/src/assistant_panel.rs diff --git a/Cargo.lock b/Cargo.lock index 52841152f0..8138744707 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,6 +449,19 @@ dependencies = [ "zed_actions", ] +[[package]] +name = "assistant2" +version = "0.1.0" +dependencies = [ + "anyhow", + "command_palette_hooks", + "feature_flags", + "gpui", + "proto", + "ui", + "workspace", +] + [[package]] name = "assistant_slash_command" version = "0.1.0" @@ -15549,6 +15562,7 @@ dependencies = [ "ashpd", "assets", "assistant", + "assistant2", "async-watch", "audio", "auto_update", diff --git a/Cargo.toml b/Cargo.toml index c066b942e7..a8537611fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/anthropic", "crates/assets", "crates/assistant", + "crates/assistant2", "crates/assistant_slash_command", "crates/assistant_tool", "crates/audio", @@ -186,6 +187,7 @@ ai = { path = "crates/ai" } anthropic = { path = "crates/anthropic" } assets = { path = "crates/assets" } assistant = { path = "crates/assistant" } +assistant2 = { path = "crates/assistant2" } assistant_slash_command = { path = "crates/assistant_slash_command" } assistant_tool = { path = "crates/assistant_tool" } audio = { path = "crates/audio" } diff --git a/crates/assistant2/Cargo.toml b/crates/assistant2/Cargo.toml new file mode 100644 index 0000000000..320cd015e2 --- /dev/null +++ b/crates/assistant2/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "assistant2" +version = "0.1.0" +edition = "2021" +publish = false +license = "GPL-3.0-or-later" + +[lints] +workspace = true + +[lib] +path = "src/assistant.rs" +doctest = false + +[dependencies] +anyhow.workspace = true +command_palette_hooks.workspace = true +feature_flags.workspace = true +gpui.workspace = true +proto.workspace = true +ui.workspace = true +workspace.workspace = true diff --git a/crates/assistant2/LICENSE-GPL b/crates/assistant2/LICENSE-GPL new file mode 120000 index 0000000000..89e542f750 --- /dev/null +++ b/crates/assistant2/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/assistant2/src/assistant.rs b/crates/assistant2/src/assistant.rs new file mode 100644 index 0000000000..31676198ba --- /dev/null +++ b/crates/assistant2/src/assistant.rs @@ -0,0 +1,40 @@ +mod assistant_panel; + +use command_palette_hooks::CommandPaletteFilter; +use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt}; +use gpui::{actions, AppContext}; + +pub use crate::assistant_panel::AssistantPanel; + +actions!(assistant2, [ToggleFocus, NewChat]); + +const NAMESPACE: &str = "assistant2"; + +/// Initializes the `assistant2` crate. +pub fn init(cx: &mut AppContext) { + assistant_panel::init(cx); + feature_gate_assistant2_actions(cx); +} + +fn feature_gate_assistant2_actions(cx: &mut AppContext) { + const ASSISTANT1_NAMESPACE: &str = "assistant"; + + CommandPaletteFilter::update_global(cx, |filter, _cx| { + filter.hide_namespace(NAMESPACE); + }); + + cx.observe_flag::(move |is_enabled, cx| { + if is_enabled { + CommandPaletteFilter::update_global(cx, |filter, _cx| { + filter.show_namespace(NAMESPACE); + filter.hide_namespace(ASSISTANT1_NAMESPACE); + }); + } else { + CommandPaletteFilter::update_global(cx, |filter, _cx| { + filter.hide_namespace(NAMESPACE); + filter.show_namespace(ASSISTANT1_NAMESPACE); + }); + } + }) + .detach(); +} diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs new file mode 100644 index 0000000000..7c586dd35e --- /dev/null +++ b/crates/assistant2/src/assistant_panel.rs @@ -0,0 +1,123 @@ +use anyhow::Result; +use gpui::{ + prelude::*, px, Action, AppContext, AsyncWindowContext, EventEmitter, FocusHandle, + FocusableView, Pixels, Task, View, ViewContext, WeakView, WindowContext, +}; +use ui::prelude::*; +use workspace::dock::{DockPosition, Panel, PanelEvent}; +use workspace::{Pane, Workspace}; + +use crate::{NewChat, ToggleFocus}; + +pub fn init(cx: &mut AppContext) { + cx.observe_new_views( + |workspace: &mut Workspace, _cx: &mut ViewContext| { + workspace.register_action(|workspace, _: &ToggleFocus, cx| { + workspace.toggle_panel_focus::(cx); + }); + }, + ) + .detach(); +} + +pub struct AssistantPanel { + pane: View, +} + +impl AssistantPanel { + pub fn load( + workspace: WeakView, + cx: AsyncWindowContext, + ) -> Task>> { + cx.spawn(|mut cx| async move { + workspace.update(&mut cx, |workspace, cx| { + cx.new_view(|cx| Self::new(workspace, cx)) + }) + }) + } + + fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { + let pane = cx.new_view(|cx| { + let mut pane = Pane::new( + workspace.weak_handle(), + workspace.project().clone(), + Default::default(), + None, + NewChat.boxed_clone(), + cx, + ); + pane.set_can_split(false, cx); + pane.set_can_navigate(true, cx); + + pane + }); + + Self { pane } + } +} + +impl FocusableView for AssistantPanel { + fn focus_handle(&self, cx: &AppContext) -> FocusHandle { + self.pane.focus_handle(cx) + } +} + +impl EventEmitter for AssistantPanel {} + +impl Panel for AssistantPanel { + fn persistent_name() -> &'static str { + "AssistantPanel2" + } + + fn position(&self, _cx: &WindowContext) -> DockPosition { + DockPosition::Right + } + + fn position_is_valid(&self, _: DockPosition) -> bool { + true + } + + fn set_position(&mut self, _position: DockPosition, _cx: &mut ViewContext) {} + + fn size(&self, _cx: &WindowContext) -> Pixels { + px(640.) + } + + fn set_size(&mut self, _size: Option, _cx: &mut ViewContext) {} + + fn is_zoomed(&self, cx: &WindowContext) -> bool { + self.pane.read(cx).is_zoomed() + } + + fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext) { + self.pane.update(cx, |pane, cx| pane.set_zoomed(zoomed, cx)); + } + + fn set_active(&mut self, _active: bool, _cx: &mut ViewContext) {} + + fn pane(&self) -> Option> { + Some(self.pane.clone()) + } + + fn remote_id() -> Option { + Some(proto::PanelId::AssistantPanel) + } + + fn icon(&self, _cx: &WindowContext) -> Option { + Some(IconName::ZedAssistant) + } + + fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> { + Some("Assistant Panel") + } + + fn toggle_action(&self) -> Box { + Box::new(ToggleFocus) + } +} + +impl Render for AssistantPanel { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + div().child(Label::new("Assistant II")) + } +} diff --git a/crates/feature_flags/src/feature_flags.rs b/crates/feature_flags/src/feature_flags.rs index 286acdfc98..416971b36e 100644 --- a/crates/feature_flags/src/feature_flags.rs +++ b/crates/feature_flags/src/feature_flags.rs @@ -39,6 +39,16 @@ pub trait FeatureFlag { } } +pub struct Assistant2FeatureFlag; + +impl FeatureFlag for Assistant2FeatureFlag { + const NAME: &'static str = "assistant2"; + + fn enabled_for_staff() -> bool { + false + } +} + pub struct Remoting {} impl FeatureFlag for Remoting { const NAME: &'static str = "remoting"; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 755076e360..1959fb0e00 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -19,6 +19,7 @@ activity_indicator.workspace = true anyhow.workspace = true assets.workspace = true assistant.workspace = true +assistant2.workspace = true async-watch.workspace = true audio.workspace = true auto_update.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 73b0e0f199..6febe05d10 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -406,6 +406,7 @@ fn main() { stdout_is_a_pty(), cx, ); + assistant2::init(cx); assistant_hints::init(cx); repl::init( app_state.fs.clone(), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 322ea3610b..086935542c 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -236,10 +236,29 @@ pub fn initialize_workspace( .unwrap_or(true) }); + let release_channel = ReleaseChannel::global(cx); + let assistant2_feature_flag = cx.wait_for_flag::(); + let prompt_builder = prompt_builder.clone(); cx.spawn(|workspace_handle, mut cx| async move { - let assistant_panel = - assistant::AssistantPanel::load(workspace_handle.clone(), prompt_builder, cx.clone()); + let is_assistant2_enabled = if cfg!(test) { + false + } else { + let is_assistant2_feature_flag_enabled = assistant2_feature_flag.await; + release_channel == ReleaseChannel::Dev && is_assistant2_feature_flag_enabled + }; + + let (assistant_panel, assistant2_panel) = if is_assistant2_enabled { + let assistant2_panel = + assistant2::AssistantPanel::load(workspace_handle.clone(), cx.clone()).await?; + + (None, Some(assistant2_panel)) + } else { + let assistant_panel = + assistant::AssistantPanel::load(workspace_handle.clone(), prompt_builder, cx.clone()).await?; + + (Some(assistant_panel), None) + }; let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); let outline_panel = OutlinePanel::load(workspace_handle.clone(), cx.clone()); @@ -257,7 +276,6 @@ pub fn initialize_workspace( project_panel, outline_panel, terminal_panel, - assistant_panel, channels_panel, chat_panel, notification_panel, @@ -265,14 +283,20 @@ pub fn initialize_workspace( project_panel, outline_panel, terminal_panel, - assistant_panel, channels_panel, chat_panel, notification_panel, )?; workspace_handle.update(&mut cx, |workspace, cx| { - workspace.add_panel(assistant_panel, cx); + if let Some(assistant_panel) = assistant_panel { + workspace.add_panel(assistant_panel, cx); + } + + if let Some(assistant2_panel) = assistant2_panel { + workspace.add_panel(assistant2_panel, cx); + } + workspace.add_panel(project_panel, cx); workspace.add_panel(outline_panel, cx); workspace.add_panel(terminal_panel, cx);