diff --git a/Cargo.lock b/Cargo.lock index c7297e6d59..6237bac204 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,6 +210,7 @@ dependencies = [ "chrono", "client", "collections", + "command_palette_hooks", "component", "context_server", "db", @@ -6360,6 +6361,7 @@ dependencies = [ "buffer_diff", "call", "chrono", + "client", "collections", "command_palette_hooks", "component", diff --git a/assets/settings/default.json b/assets/settings/default.json index 309afaccf5..dab1684aef 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -1076,6 +1076,10 @@ // Send anonymized usage data like what languages you're using Zed with. "metrics": true }, + // Whether to disable all AI features in Zed. + // + // Default: false + "disable_ai": false, // Automatically update Zed. This setting may be ignored on Linux if // installed through a package manager. "auto_update": true, diff --git a/crates/agent_ui/Cargo.toml b/crates/agent_ui/Cargo.toml index 33042c0ebd..7d3b84e42e 100644 --- a/crates/agent_ui/Cargo.toml +++ b/crates/agent_ui/Cargo.toml @@ -32,6 +32,7 @@ buffer_diff.workspace = true chrono.workspace = true client.workspace = true collections.workspace = true +command_palette_hooks.workspace = true component.workspace = true context_server.workspace = true db.workspace = true diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index fc803c730e..7e9360a0cb 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -43,7 +43,7 @@ use anyhow::{Result, anyhow}; use assistant_context::{AssistantContext, ContextEvent, ContextSummary}; use assistant_slash_command::SlashCommandWorkingSet; use assistant_tool::ToolWorkingSet; -use client::{UserStore, zed_urls}; +use client::{DisableAiSettings, UserStore, zed_urls}; use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer}; use feature_flags::{self, FeatureFlagAppExt}; use fs::Fs; @@ -744,6 +744,7 @@ impl AgentPanel { if workspace .panel::(cx) .is_some_and(|panel| panel.read(cx).enabled(cx)) + && !DisableAiSettings::get_global(cx).disable_ai { workspace.toggle_panel_focus::(window, cx); } @@ -1665,7 +1666,10 @@ impl Panel for AgentPanel { } fn icon(&self, _window: &Window, cx: &App) -> Option { - (self.enabled(cx) && AgentSettings::get_global(cx).button).then_some(IconName::ZedAssistant) + (self.enabled(cx) + && AgentSettings::get_global(cx).button + && !DisableAiSettings::get_global(cx).disable_ai) + .then_some(IconName::ZedAssistant) } fn icon_tooltip(&self, _window: &Window, _cx: &App) -> Option<&'static str> { diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index 7f69e8f66e..cac0f1adac 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -31,7 +31,8 @@ use std::sync::Arc; use agent::{Thread, ThreadId}; use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection}; use assistant_slash_command::SlashCommandRegistry; -use client::Client; +use client::{Client, DisableAiSettings}; +use command_palette_hooks::CommandPaletteFilter; use feature_flags::FeatureFlagAppExt as _; use fs::Fs; use gpui::{Action, App, Entity, actions}; @@ -43,6 +44,7 @@ use prompt_store::PromptBuilder; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings as _, SettingsStore}; +use std::any::TypeId; pub use crate::active_thread::ActiveThread; use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal}; @@ -52,6 +54,7 @@ use crate::slash_command_settings::SlashCommandSettings; pub use agent_diff::{AgentDiffPane, AgentDiffToolbar}; pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor}; pub use ui::preview::{all_agent_previews, get_agent_preview}; +use zed_actions; actions!( agent, @@ -241,6 +244,66 @@ pub fn init( }) .detach(); cx.observe_new(ManageProfilesModal::register).detach(); + + // Update command palette filter based on AI settings + update_command_palette_filter(cx); + + // Watch for settings changes + cx.observe_global::(|app_cx| { + // When settings change, update the command palette filter + update_command_palette_filter(app_cx); + }) + .detach(); +} + +fn update_command_palette_filter(cx: &mut App) { + let disable_ai = DisableAiSettings::get_global(cx).disable_ai; + CommandPaletteFilter::update_global(cx, |filter, _| { + if disable_ai { + filter.hide_namespace("agent"); + filter.hide_namespace("assistant"); + filter.hide_namespace("zed_predict_onboarding"); + filter.hide_namespace("edit_prediction"); + + use editor::actions::{ + AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction, + PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction, + }; + let edit_prediction_actions = [ + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + ]; + filter.hide_action_types(&edit_prediction_actions); + filter.hide_action_types(&[TypeId::of::()]); + } else { + filter.show_namespace("agent"); + filter.show_namespace("assistant"); + filter.show_namespace("zed_predict_onboarding"); + + filter.show_namespace("edit_prediction"); + + use editor::actions::{ + AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction, + PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction, + }; + let edit_prediction_actions = [ + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + ]; + filter.show_action_types(edit_prediction_actions.iter()); + + filter + .show_action_types([TypeId::of::()].iter()); + } + }); } fn init_language_model_settings(cx: &mut App) { diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index 65b72cbba5..44ec050ae2 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -16,7 +16,7 @@ use agent::{ }; use agent_settings::AgentSettings; use anyhow::{Context as _, Result}; -use client::telemetry::Telemetry; +use client::{DisableAiSettings, telemetry::Telemetry}; use collections::{HashMap, HashSet, VecDeque, hash_map}; use editor::SelectionEffects; use editor::{ @@ -57,6 +57,17 @@ pub fn init( cx: &mut App, ) { cx.set_global(InlineAssistant::new(fs, prompt_builder, telemetry)); + + cx.observe_global::(|cx| { + if DisableAiSettings::get_global(cx).disable_ai { + // Hide any active inline assist UI when AI is disabled + InlineAssistant::update_global(cx, |assistant, cx| { + assistant.cancel_all_active_completions(cx); + }); + } + }) + .detach(); + cx.observe_new(|_workspace: &mut Workspace, window, cx| { let Some(window) = window else { return; @@ -141,6 +152,26 @@ impl InlineAssistant { .detach(); } + /// Hides all active inline assists when AI is disabled + pub fn cancel_all_active_completions(&mut self, cx: &mut App) { + // Cancel all active completions in editors + for (editor_handle, _) in self.assists_by_editor.iter() { + if let Some(editor) = editor_handle.upgrade() { + let windows = cx.windows(); + if !windows.is_empty() { + let window = windows[0]; + let _ = window.update(cx, |_, window, cx| { + editor.update(cx, |editor, cx| { + if editor.has_active_inline_completion() { + editor.cancel(&Default::default(), window, cx); + } + }); + }); + } + } + } + } + fn handle_workspace_event( &mut self, workspace: Entity, @@ -176,7 +207,7 @@ impl InlineAssistant { window: &mut Window, cx: &mut App, ) { - let is_assistant2_enabled = true; + let is_assistant2_enabled = !DisableAiSettings::get_global(cx).disable_ai; if let Some(editor) = item.act_as::(cx) { editor.update(cx, |editor, cx| { @@ -199,6 +230,13 @@ impl InlineAssistant { cx, ); + if DisableAiSettings::get_global(cx).disable_ai { + // Cancel any active completions + if editor.has_active_inline_completion() { + editor.cancel(&Default::default(), window, cx); + } + } + // Remove the Assistant1 code action provider, as it still might be registered. editor.remove_code_action_provider("assistant".into(), window, cx); } else { @@ -219,7 +257,7 @@ impl InlineAssistant { cx: &mut Context, ) { let settings = AgentSettings::get_global(cx); - if !settings.enabled { + if !settings.enabled || DisableAiSettings::get_global(cx).disable_ai { return; } diff --git a/crates/assistant_tools/Cargo.toml b/crates/assistant_tools/Cargo.toml index e234b62b14..146800e094 100644 --- a/crates/assistant_tools/Cargo.toml +++ b/crates/assistant_tools/Cargo.toml @@ -20,6 +20,7 @@ anyhow.workspace = true assistant_tool.workspace = true buffer_diff.workspace = true chrono.workspace = true +client.workspace = true collections.workspace = true component.workspace = true derive_more.workspace = true diff --git a/crates/assistant_tools/src/assistant_tools.rs b/crates/assistant_tools/src/assistant_tools.rs index eef792f526..57fdc51336 100644 --- a/crates/assistant_tools/src/assistant_tools.rs +++ b/crates/assistant_tools/src/assistant_tools.rs @@ -20,14 +20,13 @@ mod thinking_tool; mod ui; mod web_search_tool; -use std::sync::Arc; - use assistant_tool::ToolRegistry; use copy_path_tool::CopyPathTool; use gpui::{App, Entity}; use http_client::HttpClientWithUrl; use language_model::LanguageModelRegistry; use move_path_tool::MovePathTool; +use std::sync::Arc; use web_search_tool::WebSearchTool; pub(crate) use templates::*; diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 1be8ffdb55..81bb95b514 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -151,6 +151,7 @@ impl Settings for ProxySettings { pub fn init_settings(cx: &mut App) { TelemetrySettings::register(cx); + DisableAiSettings::register(cx); ClientSettings::register(cx); ProxySettings::register(cx); } @@ -548,6 +549,33 @@ impl settings::Settings for TelemetrySettings { } } +/// Whether to disable all AI features in Zed. +/// +/// Default: false +#[derive(Copy, Clone, Debug)] +pub struct DisableAiSettings { + pub disable_ai: bool, +} + +impl settings::Settings for DisableAiSettings { + const KEY: Option<&'static str> = Some("disable_ai"); + + type FileContent = Option; + + fn load(sources: SettingsSources, _: &mut App) -> Result { + Ok(Self { + disable_ai: sources + .user + .or(sources.server) + .copied() + .flatten() + .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), + }) + } + + fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {} +} + impl Client { pub fn new( clock: Arc, diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 1966d1a389..e11242cb15 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -6,6 +6,7 @@ mod sign_in; use crate::sign_in::initiate_sign_in_within_workspace; use ::fs::Fs; use anyhow::{Context as _, Result, anyhow}; +use client::DisableAiSettings; use collections::{HashMap, HashSet}; use command_palette_hooks::CommandPaletteFilter; use futures::{Future, FutureExt, TryFutureExt, channel::oneshot, future::Shared}; @@ -25,6 +26,7 @@ use node_runtime::NodeRuntime; use parking_lot::Mutex; use request::StatusNotification; use serde_json::json; +use settings::Settings; use settings::SettingsStore; use sign_in::{reinstall_and_sign_in_within_workspace, sign_out_within_workspace}; use std::collections::hash_map::Entry; @@ -93,26 +95,34 @@ pub fn init( let copilot_auth_action_types = [TypeId::of::()]; let copilot_no_auth_action_types = [TypeId::of::()]; let status = handle.read(cx).status(); + + let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai; let filter = CommandPaletteFilter::global_mut(cx); - match status { - Status::Disabled => { - filter.hide_action_types(&copilot_action_types); - filter.hide_action_types(&copilot_auth_action_types); - filter.hide_action_types(&copilot_no_auth_action_types); - } - Status::Authorized => { - filter.hide_action_types(&copilot_no_auth_action_types); - filter.show_action_types( - copilot_action_types - .iter() - .chain(&copilot_auth_action_types), - ); - } - _ => { - filter.hide_action_types(&copilot_action_types); - filter.hide_action_types(&copilot_auth_action_types); - filter.show_action_types(copilot_no_auth_action_types.iter()); + if is_ai_disabled { + filter.hide_action_types(&copilot_action_types); + filter.hide_action_types(&copilot_auth_action_types); + filter.hide_action_types(&copilot_no_auth_action_types); + } else { + match status { + Status::Disabled => { + filter.hide_action_types(&copilot_action_types); + filter.hide_action_types(&copilot_auth_action_types); + filter.hide_action_types(&copilot_no_auth_action_types); + } + Status::Authorized => { + filter.hide_action_types(&copilot_no_auth_action_types); + filter.show_action_types( + copilot_action_types + .iter() + .chain(&copilot_auth_action_types), + ); + } + _ => { + filter.hide_action_types(&copilot_action_types); + filter.hide_action_types(&copilot_auth_action_types); + filter.show_action_types(copilot_no_auth_action_types.iter()); + } } } }) diff --git a/crates/git_ui/Cargo.toml b/crates/git_ui/Cargo.toml index 6e04dcb656..2fb80b7e73 100644 --- a/crates/git_ui/Cargo.toml +++ b/crates/git_ui/Cargo.toml @@ -23,6 +23,7 @@ askpass.workspace = true buffer_diff.workspace = true call.workspace = true chrono.workspace = true +client.workspace = true collections.workspace = true command_palette_hooks.workspace = true component.workspace = true diff --git a/crates/git_ui/src/commit_modal.rs b/crates/git_ui/src/commit_modal.rs index ac3d24e3eb..b99f628806 100644 --- a/crates/git_ui/src/commit_modal.rs +++ b/crates/git_ui/src/commit_modal.rs @@ -1,8 +1,10 @@ use crate::branch_picker::{self, BranchList}; use crate::git_panel::{GitPanel, commit_message_editor}; +use client::DisableAiSettings; use git::repository::CommitOptions; use git::{Amend, Commit, GenerateCommitMessage, Signoff}; use panel::{panel_button, panel_editor_style}; +use settings::Settings; use ui::{ ContextMenu, KeybindingHint, PopoverMenu, PopoverMenuHandle, SplitButton, Tooltip, prelude::*, }; @@ -569,11 +571,13 @@ impl Render for CommitModal { .on_action(cx.listener(Self::dismiss)) .on_action(cx.listener(Self::commit)) .on_action(cx.listener(Self::amend)) - .on_action(cx.listener(|this, _: &GenerateCommitMessage, _, cx| { - this.git_panel.update(cx, |panel, cx| { - panel.generate_commit_message(cx); - }) - })) + .when(!DisableAiSettings::get_global(cx).disable_ai, |this| { + this.on_action(cx.listener(|this, _: &GenerateCommitMessage, _, cx| { + this.git_panel.update(cx, |panel, cx| { + panel.generate_commit_message(cx); + }) + })) + }) .on_action( cx.listener(|this, _: &zed_actions::git::Branch, window, cx| { this.toggle_branch_selector(window, cx); diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index e998586af4..061833a6c7 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -12,6 +12,7 @@ use crate::{ use agent_settings::AgentSettings; use anyhow::Context as _; use askpass::AskPassDelegate; +use client::DisableAiSettings; use db::kvp::KEY_VALUE_STORE; use editor::{ Editor, EditorElement, EditorMode, EditorSettings, MultiBuffer, ShowScrollbar, @@ -53,7 +54,7 @@ use project::{ git_store::{GitStoreEvent, Repository}, }; use serde::{Deserialize, Serialize}; -use settings::{Settings as _, SettingsStore}; +use settings::{Settings, SettingsStore}; use std::future::Future; use std::ops::Range; use std::path::{Path, PathBuf}; @@ -464,9 +465,14 @@ impl GitPanel { }; let mut assistant_enabled = AgentSettings::get_global(cx).enabled; + let mut was_ai_disabled = DisableAiSettings::get_global(cx).disable_ai; let _settings_subscription = cx.observe_global::(move |_, cx| { - if assistant_enabled != AgentSettings::get_global(cx).enabled { + let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai; + if assistant_enabled != AgentSettings::get_global(cx).enabled + || was_ai_disabled != is_ai_disabled + { assistant_enabled = AgentSettings::get_global(cx).enabled; + was_ai_disabled = is_ai_disabled; cx.notify(); } }); @@ -1806,7 +1812,7 @@ impl GitPanel { /// Generates a commit message using an LLM. pub fn generate_commit_message(&mut self, cx: &mut Context) { - if !self.can_commit() { + if !self.can_commit() || DisableAiSettings::get_global(cx).disable_ai { return; } @@ -4305,8 +4311,10 @@ impl GitPanel { } fn current_language_model(cx: &Context<'_, GitPanel>) -> Option> { - agent_settings::AgentSettings::get_global(cx) - .enabled + let is_enabled = agent_settings::AgentSettings::get_global(cx).enabled + && !DisableAiSettings::get_global(cx).disable_ai; + + is_enabled .then(|| { let ConfiguredModel { provider, model } = LanguageModelRegistry::read_global(cx).commit_message_model()?; @@ -5037,6 +5045,7 @@ mod tests { language::init(cx); editor::init(cx); Project::init_settings(cx); + client::DisableAiSettings::register(cx); crate::init(cx); }); } diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs index 8a8eacdc6a..2615a8beef 100644 --- a/crates/inline_completion_button/src/inline_completion_button.rs +++ b/crates/inline_completion_button/src/inline_completion_button.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use client::{UserStore, zed_urls}; +use client::{DisableAiSettings, UserStore, zed_urls}; use copilot::{Copilot, Status}; use editor::{ Editor, SelectionEffects, @@ -72,6 +72,11 @@ enum SupermavenButtonStatus { impl Render for InlineCompletionButton { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + // Return empty div if AI is disabled + if DisableAiSettings::get_global(cx).disable_ai { + return div(); + } + let all_language_settings = all_language_settings(None, cx); match all_language_settings.edit_predictions.provider { diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index ea4ac13de7..49bf2031ab 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,4 +1,4 @@ -use client::{TelemetrySettings, telemetry::Telemetry}; +use client::{DisableAiSettings, TelemetrySettings, telemetry::Telemetry}; use db::kvp::KEY_VALUE_STORE; use gpui::{ Action, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, @@ -174,23 +174,25 @@ impl Render for WelcomePage { .ok(); })), ) - .child( - Button::new( - "try-zed-edit-prediction", - edit_prediction_label, + .when(!DisableAiSettings::get_global(cx).disable_ai, |parent| { + parent.child( + Button::new( + "edit_prediction_onboarding", + edit_prediction_label, + ) + .disabled(edit_prediction_provider_is_zed) + .icon(IconName::ZedPredict) + .icon_size(IconSize::XSmall) + .icon_color(Color::Muted) + .icon_position(IconPosition::Start) + .on_click( + cx.listener(|_, _, window, cx| { + telemetry::event!("Welcome Screen Try Edit Prediction clicked"); + window.dispatch_action(zed_actions::OpenZedPredictOnboarding.boxed_clone(), cx); + }), + ), ) - .disabled(edit_prediction_provider_is_zed) - .icon(IconName::ZedPredict) - .icon_size(IconSize::XSmall) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .on_click( - cx.listener(|_, _, window, cx| { - telemetry::event!("Welcome Screen Try Edit Prediction clicked"); - window.dispatch_action(zed_actions::OpenZedPredictOnboarding.boxed_clone(), cx); - }), - ), - ) + }) .child( Button::new("edit settings", "Edit Settings") .icon(IconName::Settings) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 4e39c2d182..3f047e2f11 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -242,6 +242,7 @@ struct PanelEntry { pub struct PanelButtons { dock: Entity, + _settings_subscription: Subscription, } impl Dock { @@ -373,6 +374,12 @@ impl Dock { }) } + pub fn first_enabled_panel_idx_excluding(&self, exclude_name: &str, cx: &App) -> Option { + self.panel_entries.iter().position(|entry| { + entry.panel.persistent_name() != exclude_name && entry.panel.enabled(cx) + }) + } + fn active_panel_entry(&self) -> Option<&PanelEntry> { self.active_panel_index .and_then(|index| self.panel_entries.get(index)) @@ -833,7 +840,11 @@ impl Render for Dock { impl PanelButtons { pub fn new(dock: Entity, cx: &mut Context) -> Self { cx.observe(&dock, |_, _, cx| cx.notify()).detach(); - Self { dock } + let settings_subscription = cx.observe_global::(|_, cx| cx.notify()); + Self { + dock, + _settings_subscription: settings_subscription, + } } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index c9b8eebff6..d0b9c53397 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -554,6 +554,7 @@ pub fn main() { supermaven::init(app_state.client.clone(), cx); language_model::init(app_state.client.clone(), cx); language_models::init(app_state.user_store.clone(), app_state.client.clone(), cx); + agent_settings::init(cx); agent_servers::init(cx); web_search::init(cx); web_search_providers::init(app_state.client.clone(), cx); diff --git a/crates/zed/src/zed/quick_action_bar.rs b/crates/zed/src/zed/quick_action_bar.rs index c95d86c84f..aff124a0bc 100644 --- a/crates/zed/src/zed/quick_action_bar.rs +++ b/crates/zed/src/zed/quick_action_bar.rs @@ -2,6 +2,7 @@ mod preview; mod repl_menu; use agent_settings::AgentSettings; +use client::DisableAiSettings; use editor::actions::{ AddSelectionAbove, AddSelectionBelow, CodeActionSource, DuplicateLineDown, GoToDiagnostic, GoToHunk, GoToPreviousDiagnostic, GoToPreviousHunk, MoveLineDown, MoveLineUp, SelectAll, @@ -32,6 +33,7 @@ const MAX_CODE_ACTION_MENU_LINES: u32 = 16; pub struct QuickActionBar { _inlay_hints_enabled_subscription: Option, + _ai_settings_subscription: Subscription, active_item: Option>, buffer_search_bar: Entity, show: bool, @@ -46,8 +48,28 @@ impl QuickActionBar { workspace: &Workspace, cx: &mut Context, ) -> Self { + let mut was_ai_disabled = DisableAiSettings::get_global(cx).disable_ai; + let mut was_agent_enabled = AgentSettings::get_global(cx).enabled; + let mut was_agent_button = AgentSettings::get_global(cx).button; + + let ai_settings_subscription = cx.observe_global::(move |_, cx| { + let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai; + let agent_settings = AgentSettings::get_global(cx); + + if was_ai_disabled != is_ai_disabled + || was_agent_enabled != agent_settings.enabled + || was_agent_button != agent_settings.button + { + was_ai_disabled = is_ai_disabled; + was_agent_enabled = agent_settings.enabled; + was_agent_button = agent_settings.button; + cx.notify(); + } + }); + let mut this = Self { _inlay_hints_enabled_subscription: None, + _ai_settings_subscription: ai_settings_subscription, active_item: None, buffer_search_bar, show: true, @@ -575,7 +597,9 @@ impl Render for QuickActionBar { .children(self.render_preview_button(self.workspace.clone(), cx)) .children(search_button) .when( - AgentSettings::get_global(cx).enabled && AgentSettings::get_global(cx).button, + AgentSettings::get_global(cx).enabled + && AgentSettings::get_global(cx).button + && !DisableAiSettings::get_global(cx).disable_ai, |bar| bar.child(assistant_button), ) .children(code_actions_dropdown) diff --git a/crates/zeta/src/init.rs b/crates/zeta/src/init.rs index 4bcd50df88..4a65771223 100644 --- a/crates/zeta/src/init.rs +++ b/crates/zeta/src/init.rs @@ -1,10 +1,11 @@ use std::any::{Any, TypeId}; +use client::DisableAiSettings; use command_palette_hooks::CommandPaletteFilter; use feature_flags::{FeatureFlagAppExt as _, PredictEditsRateCompletionsFeatureFlag}; use gpui::actions; use language::language_settings::{AllLanguageSettings, EditPredictionProvider}; -use settings::update_settings_file; +use settings::{Settings, SettingsStore, update_settings_file}; use ui::App; use workspace::Workspace; @@ -21,6 +22,8 @@ actions!( ); pub fn init(cx: &mut App) { + feature_gate_predict_edits_actions(cx); + cx.observe_new(move |workspace: &mut Workspace, _, _cx| { workspace.register_action(|workspace, _: &RateCompletions, window, cx| { if cx.has_flag::() { @@ -53,27 +56,57 @@ pub fn init(cx: &mut App) { }); }) .detach(); - - feature_gate_predict_edits_rating_actions(cx); } -fn feature_gate_predict_edits_rating_actions(cx: &mut App) { +fn feature_gate_predict_edits_actions(cx: &mut App) { let rate_completion_action_types = [TypeId::of::()]; + let reset_onboarding_action_types = [TypeId::of::()]; + let zeta_all_action_types = [ + TypeId::of::(), + TypeId::of::(), + zed_actions::OpenZedPredictOnboarding.type_id(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + ]; CommandPaletteFilter::update_global(cx, |filter, _cx| { filter.hide_action_types(&rate_completion_action_types); + filter.hide_action_types(&reset_onboarding_action_types); filter.hide_action_types(&[zed_actions::OpenZedPredictOnboarding.type_id()]); }); + cx.observe_global::(move |cx| { + let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai; + let has_feature_flag = cx.has_flag::(); + + CommandPaletteFilter::update_global(cx, |filter, _cx| { + if is_ai_disabled { + filter.hide_action_types(&zeta_all_action_types); + } else { + if has_feature_flag { + filter.show_action_types(rate_completion_action_types.iter()); + } else { + filter.hide_action_types(&rate_completion_action_types); + } + } + }); + }) + .detach(); + cx.observe_flag::(move |is_enabled, cx| { - if is_enabled { - CommandPaletteFilter::update_global(cx, |filter, _cx| { - filter.show_action_types(rate_completion_action_types.iter()); - }); - } else { - CommandPaletteFilter::update_global(cx, |filter, _cx| { - filter.hide_action_types(&rate_completion_action_types); - }); + if !DisableAiSettings::get_global(cx).disable_ai { + if is_enabled { + CommandPaletteFilter::update_global(cx, |filter, _cx| { + filter.show_action_types(rate_completion_action_types.iter()); + }); + } else { + CommandPaletteFilter::update_global(cx, |filter, _cx| { + filter.hide_action_types(&rate_completion_action_types); + }); + } } }) .detach();