diff --git a/Cargo.lock b/Cargo.lock index 334815a59a5ce3..1128d5a87ebbea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19210,6 +19210,7 @@ dependencies = [ "chrono", "client", "cloud_api_types", + "command_palette_hooks", "db", "fs", "git_ui", diff --git a/crates/title_bar/Cargo.toml b/crates/title_bar/Cargo.toml index 4a762b18eef384..90f5dc8c40f8d6 100644 --- a/crates/title_bar/Cargo.toml +++ b/crates/title_bar/Cargo.toml @@ -39,6 +39,7 @@ channel.workspace = true chrono.workspace = true client.workspace = true cloud_api_types.workspace = true +command_palette_hooks.workspace = true db.workspace = true git_ui.workspace = true diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 73c183289a2907..20209047435efc 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -25,6 +25,7 @@ use auto_update::AutoUpdateStatus; use call::ActiveCall; use client::{Client, UserStore, zed_urls}; use cloud_api_types::Plan; +use command_palette_hooks::CommandPaletteFilter; use gpui::{ Action, Anchor, Animation, AnimationExt, AnyElement, App, Context, Element, Entity, Focusable, @@ -38,8 +39,9 @@ use project::{ trusted_worktrees::TrustedWorktrees, }; use remote::RemoteConnectionOptions; -use settings::Settings as _; +use settings::{Settings as _, SettingsStore}; +use std::any::TypeId; use std::sync::Arc; use std::time::Duration; use theme::ActiveTheme; @@ -77,9 +79,24 @@ actions!( ] ); +actions!( + workspace, + [ + /// Switches to the classic, editor-focused panel layout. + UseClassicLayout, + /// Switches to the agentic panel layout. + UseAgenticLayout, + ] +); + pub fn init(cx: &mut App) { platform_title_bar::PlatformTitleBar::init(cx); + update_layout_action_filter(cx); + + cx.observe_global::(update_layout_action_filter) + .detach(); + cx.observe_new(|workspace: &mut Workspace, window, cx| { let Some(window) = window else { return; @@ -88,6 +105,14 @@ pub fn init(cx: &mut App) { let item = cx.new(|cx| TitleBar::new("title-bar", workspace, multi_workspace, window, cx)); workspace.set_titlebar_item(item.into(), window, cx); + workspace.register_action(|_workspace, _: &UseClassicLayout, _window, cx| { + set_window_layout(WindowLayout::Editor(None), cx); + }); + + workspace.register_action(|_workspace, _: &UseAgenticLayout, _window, cx| { + set_window_layout(WindowLayout::Agent(None), cx); + }); + workspace.register_action(|workspace, _: &SimulateUpdateAvailable, _window, cx| { if let Some(titlebar) = workspace .titlebar_item() @@ -148,6 +173,28 @@ pub fn init(cx: &mut App) { .detach(); } +/// Hides or shows the panel layout actions in the command palette based on +/// whether AI is currently disabled. +fn update_layout_action_filter(cx: &mut App) { + let disable_ai = project::DisableAiSettings::get_global(cx).disable_ai; + let layout_actions = [ + TypeId::of::(), + TypeId::of::(), + ]; + CommandPaletteFilter::update_global(cx, |filter, _| { + if disable_ai { + filter.hide_action_types(&layout_actions); + } else { + filter.show_action_types(layout_actions.iter()); + } + }); +} + +fn set_window_layout(layout: WindowLayout, cx: &App) { + let fs = ::global(cx); + drop(AgentSettings::set_layout(layout, fs, cx)); +} + pub struct TitleBar { platform_titlebar: Entity, project: Entity, @@ -1234,7 +1281,6 @@ impl TitleBar { let is_editor = matches!(current_layout, WindowLayout::Editor(_)); let is_agent = matches!(current_layout, WindowLayout::Agent(_)); let is_custom = matches!(current_layout, WindowLayout::Custom(_)); - let fs = ::global(cx); ContextMenu::build(window, cx, |menu, _, _cx| { menu.when(is_signed_in, |this| { @@ -1348,36 +1394,26 @@ impl TitleBar { zed_actions::Extensions::default().boxed_clone(), ) .when(ai_enabled, |menu| { - let fs = fs.clone(); menu.separator() .submenu("Panel Layout", move |menu, _window, _cx| { - let fs = fs.clone(); menu.toggleable_entry( "Classic", is_editor, IconPosition::Start, - None, - { - let fs = fs.clone(); - move |_window, cx| { - drop(AgentSettings::set_layout( - WindowLayout::Editor(None), - fs.clone(), - cx, - )); - } + Some(UseClassicLayout.boxed_clone()), + move |window, cx| { + window.dispatch_action(UseClassicLayout.boxed_clone(), cx); + }, + ) + .toggleable_entry( + "Agentic", + is_agent, + IconPosition::Start, + Some(UseAgenticLayout.boxed_clone()), + move |window, cx| { + window.dispatch_action(UseAgenticLayout.boxed_clone(), cx); }, ) - .toggleable_entry("Agentic", is_agent, IconPosition::Start, None, { - let fs = fs.clone(); - move |_window, cx| { - drop(AgentSettings::set_layout( - WindowLayout::Agent(None), - fs.clone(), - cx, - )); - } - }) .when(is_custom, |menu| { menu.item( ContextMenuEntry::new("Custom")