Dismiss agent panel when `disable_ai` is toggled to `true` (#38461)

Finn Evers created

Closes https://github.com/zed-industries/zed/issues/38331

This fixes an issue where we would not dismiss the panel once the user
toggled the setting, leaving them in an awkward state where closing the
panel would become hard.

Also takes care of one more check for the `Fix with assistant` action
and consolidates some of the `AgentSettings` and `DisableAiSetting`
checks into one method to make the code more readable.

Release Notes:

- N/A

Change summary

Cargo.lock                                  |  1 
crates/agent_settings/Cargo.toml            |  1 
crates/agent_settings/src/agent_settings.rs |  5 ++
crates/agent_ui/src/agent_panel.rs          | 44 ++++++++++++++++++++--
crates/agent_ui/src/inline_assistant.rs     |  8 +--
crates/git_ui/src/git_panel.rs              | 22 +++-------
crates/zed/src/zed/quick_action_bar.rs      | 18 ++------
7 files changed, 61 insertions(+), 38 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -337,6 +337,7 @@ dependencies = [
  "gpui",
  "language_model",
  "paths",
+ "project",
  "schemars 1.0.1",
  "serde",
  "serde_json",

crates/agent_settings/Cargo.toml 🔗

@@ -19,6 +19,7 @@ convert_case.workspace = true
 fs.workspace = true
 gpui.workspace = true
 language_model.workspace = true
+project.workspace = true
 schemars.workspace = true
 serde.workspace = true
 settings.workspace = true

crates/agent_settings/src/agent_settings.rs 🔗

@@ -5,6 +5,7 @@ use std::sync::Arc;
 use collections::IndexMap;
 use gpui::{App, Pixels, px};
 use language_model::LanguageModel;
+use project::DisableAiSettings;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{
@@ -53,6 +54,10 @@ pub struct AgentSettings {
 }
 
 impl AgentSettings {
+    pub fn enabled(&self, cx: &App) -> bool {
+        self.enabled && !DisableAiSettings::get_global(cx).disable_ai
+    }
+
     pub fn temperature_for_model(model: &Arc<dyn LanguageModel>, cx: &App) -> Option<f32> {
         let settings = Self::get_global(cx);
         for setting in settings.model_parameters.iter().rev() {

crates/agent_ui/src/agent_panel.rs 🔗

@@ -1,4 +1,4 @@
-use std::ops::{Not, Range};
+use std::ops::Range;
 use std::path::Path;
 use std::rc::Rc;
 use std::sync::Arc;
@@ -662,6 +662,43 @@ impl AgentPanel {
             )
         });
 
+        let mut old_disable_ai = false;
+        cx.observe_global_in::<SettingsStore>(window, move |panel, window, cx| {
+            let disable_ai = DisableAiSettings::get_global(cx).disable_ai;
+            if old_disable_ai != disable_ai {
+                let agent_panel_id = cx.entity_id();
+                let agent_panel_visible = panel
+                    .workspace
+                    .update(cx, |workspace, cx| {
+                        let agent_dock_position = panel.position(window, cx);
+                        let agent_dock = workspace.dock_at_position(agent_dock_position);
+                        let agent_panel_focused = agent_dock
+                            .read(cx)
+                            .active_panel()
+                            .is_some_and(|panel| panel.panel_id() == agent_panel_id);
+
+                        let active_panel_visible = agent_dock
+                            .read(cx)
+                            .visible_panel()
+                            .is_some_and(|panel| panel.panel_id() == agent_panel_id);
+
+                        if agent_panel_focused {
+                            cx.dispatch_action(&ToggleFocus);
+                        }
+
+                        active_panel_visible
+                    })
+                    .unwrap_or_default();
+
+                if agent_panel_visible {
+                    cx.emit(PanelEvent::Close);
+                }
+
+                old_disable_ai = disable_ai;
+            }
+        })
+        .detach();
+
         Self {
             active_view,
             workspace,
@@ -674,11 +711,9 @@ impl AgentPanel {
             prompt_store,
             configuration: None,
             configuration_subscription: None,
-
             inline_assist_context_store,
             previous_view: None,
             history_store: history_store.clone(),
-
             new_thread_menu_handle: PopoverMenuHandle::default(),
             agent_panel_menu_handle: PopoverMenuHandle::default(),
             assistant_navigation_menu_handle: PopoverMenuHandle::default(),
@@ -703,7 +738,6 @@ impl AgentPanel {
         if workspace
             .panel::<Self>(cx)
             .is_some_and(|panel| panel.read(cx).enabled(cx))
-            && !DisableAiSettings::get_global(cx).disable_ai
         {
             workspace.toggle_panel_focus::<Self>(window, cx);
         }
@@ -1499,7 +1533,7 @@ impl Panel for AgentPanel {
     }
 
     fn enabled(&self, cx: &App) -> bool {
-        DisableAiSettings::get_global(cx).disable_ai.not() && AgentSettings::get_global(cx).enabled
+        AgentSettings::get_global(cx).enabled(cx)
     }
 
     fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool {

crates/agent_ui/src/inline_assistant.rs 🔗

@@ -144,8 +144,7 @@ impl InlineAssistant {
             let Some(terminal_panel) = workspace.read(cx).panel::<TerminalPanel>(cx) else {
                 return;
             };
-            let enabled = !DisableAiSettings::get_global(cx).disable_ai
-                && AgentSettings::get_global(cx).enabled;
+            let enabled = AgentSettings::get_global(cx).enabled(cx);
             terminal_panel.update(cx, |terminal_panel, cx| {
                 terminal_panel.set_assistant_enabled(enabled, cx)
             });
@@ -257,8 +256,7 @@ impl InlineAssistant {
         window: &mut Window,
         cx: &mut Context<Workspace>,
     ) {
-        let settings = AgentSettings::get_global(cx);
-        if !settings.enabled || DisableAiSettings::get_global(cx).disable_ai {
+        if !AgentSettings::get_global(cx).enabled(cx) {
             return;
         }
 
@@ -1788,7 +1786,7 @@ impl CodeActionProvider for AssistantCodeActionProvider {
         _: &mut Window,
         cx: &mut App,
     ) -> Task<Result<Vec<CodeAction>>> {
-        if !AgentSettings::get_global(cx).enabled {
+        if !AgentSettings::get_global(cx).enabled(cx) {
             return Task::ready(Ok(Vec::new()));
         }
 

crates/git_ui/src/git_panel.rs 🔗

@@ -46,7 +46,7 @@ use panel::{
     panel_icon_button,
 };
 use project::{
-    DisableAiSettings, Fs, Project, ProjectPath,
+    Fs, Project, ProjectPath,
     git_store::{GitStoreEvent, Repository, RepositoryEvent, RepositoryId},
 };
 use serde::{Deserialize, Serialize};
@@ -405,15 +405,11 @@ impl GitPanel {
 
             let scroll_handle = UniformListScrollHandle::new();
 
-            let mut assistant_enabled = AgentSettings::get_global(cx).enabled;
-            let mut was_ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
+            let mut was_ai_enabled = AgentSettings::get_global(cx).enabled(cx);
             let _settings_subscription = cx.observe_global::<SettingsStore>(move |_, cx| {
-                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;
+                let is_ai_enabled = AgentSettings::get_global(cx).enabled(cx);
+                if was_ai_enabled != is_ai_enabled {
+                    was_ai_enabled = is_ai_enabled;
                     cx.notify();
                 }
             });
@@ -1739,10 +1735,7 @@ impl GitPanel {
 
     /// Generates a commit message using an LLM.
     pub fn generate_commit_message(&mut self, cx: &mut Context<Self>) {
-        if !self.can_commit()
-            || DisableAiSettings::get_global(cx).disable_ai
-            || !agent_settings::AgentSettings::get_global(cx).enabled
-        {
+        if !self.can_commit() || !AgentSettings::get_global(cx).enabled(cx) {
             return;
         }
 
@@ -2996,8 +2989,7 @@ impl GitPanel {
         &self,
         cx: &Context<Self>,
     ) -> Option<AnyElement> {
-        if !agent_settings::AgentSettings::get_global(cx).enabled
-            || DisableAiSettings::get_global(cx).disable_ai
+        if !agent_settings::AgentSettings::get_global(cx).enabled(cx)
             || LanguageModelRegistry::read_global(cx)
                 .commit_message_model()
                 .is_none()

crates/zed/src/zed/quick_action_bar.rs 🔗

@@ -15,7 +15,6 @@ use gpui::{
     FocusHandle, Focusable, InteractiveElement, ParentElement, Render, Styled, Subscription,
     WeakEntity, Window, anchored, deferred, point,
 };
-use project::DisableAiSettings;
 use project::project_settings::DiagnosticSeverity;
 use search::{BufferSearchBar, buffer_search};
 use settings::{Settings, SettingsStore};
@@ -48,20 +47,15 @@ impl QuickActionBar {
         workspace: &Workspace,
         cx: &mut Context<Self>,
     ) -> 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_enabled = AgentSettings::get_global(cx).enabled(cx);
         let mut was_agent_button = AgentSettings::get_global(cx).button;
 
         let ai_settings_subscription = cx.observe_global::<SettingsStore>(move |_, cx| {
-            let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
             let agent_settings = AgentSettings::get_global(cx);
+            let is_agent_enabled = agent_settings.enabled(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;
+            if was_agent_enabled != is_agent_enabled || was_agent_button != agent_settings.button {
+                was_agent_enabled = is_agent_enabled;
                 was_agent_button = agent_settings.button;
                 cx.notify();
             }
@@ -597,9 +591,7 @@ 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
-                    && !DisableAiSettings::get_global(cx).disable_ai,
+                AgentSettings::get_global(cx).enabled(cx) && AgentSettings::get_global(cx).button,
                 |bar| bar.child(assistant_button),
             )
             .children(code_actions_dropdown)