Fix "Try Now" button opening git panel instead of agent panel

Richard Feldman created

When a user on editor layout clicks "Try Now" on the parallel agents
announcement, the callback dispatches ToggleFocus in the same flush
cycle as set_layout(Agent). Because set_layout writes to the settings
file asynchronously, ToggleFocus opens the agent panel in its old dock
(right dock in editor layout). When the settings later reload, per-panel
migration observers fire in registration order. If the git panel
migrates to the right dock before the agent panel migrates out, the git
panel takes the active slot, and the agent panel's was_visible flag
becomes false when it subsequently moves to the left dockโ€”leaving the
git panel visible and the agent panel hidden.

Fix: when switching layouts, defer the ToggleFocus dispatch until after
the settings change propagates by using a one-shot observe_global
observer on SettingsStore. Global observers fire in registration order,
so by the time this observer runs, all panel-migration observers
(registered at workspace creation) have already moved every panel to its
correct position.

Change summary

Cargo.lock                                  |  1 
crates/auto_update_ui/Cargo.toml            |  1 
crates/auto_update_ui/src/auto_update_ui.rs | 45 ++++++++++++++++++++--
3 files changed, 42 insertions(+), 5 deletions(-)

Detailed changes

Cargo.lock ๐Ÿ”—

@@ -1252,6 +1252,7 @@ dependencies = [
  "semver",
  "serde",
  "serde_json",
+ "settings",
  "smol",
  "telemetry",
  "ui",

crates/auto_update_ui/Cargo.toml ๐Ÿ”—

@@ -24,6 +24,7 @@ markdown_preview.workspace = true
 release_channel.workspace = true
 semver.workspace = true
 serde.workspace = true
+settings.workspace = true
 serde_json.workspace = true
 smol.workspace = true
 telemetry.workspace = true

crates/auto_update_ui/src/auto_update_ui.rs ๐Ÿ”—

@@ -1,3 +1,5 @@
+use std::cell::RefCell;
+use std::rc::Rc;
 use std::sync::Arc;
 
 use agent_settings::{AgentSettings, WindowLayout};
@@ -6,12 +8,14 @@ use db::kvp::Dismissable;
 use editor::{Editor, MultiBuffer};
 use fs::Fs;
 use gpui::{
-    App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Window, actions, prelude::*,
+    App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Window, actions,
+    prelude::*,
 };
 use markdown_preview::markdown_preview_view::{MarkdownPreviewMode, MarkdownPreviewView};
 use release_channel::{AppVersion, ReleaseChannel};
 use semver::Version;
 use serde::Deserialize;
+use settings::SettingsStore;
 use smol::io::AsyncReadExt;
 use ui::{AnnouncementToast, ListBulletItem, ParallelAgentsIllustration, prelude::*};
 use util::{ResultExt as _, maybe};
@@ -207,12 +211,43 @@ fn announcement_for_version(version: &Version, cx: &App) -> Option<AnnouncementC
                         let already_agent_layout =
                             matches!(AgentSettings::get_layout(cx), WindowLayout::Agent(_));
 
-                        if !already_agent_layout {
+                        window.dispatch_action(Box::new(FocusWorkspaceSidebar), cx);
+
+                        if already_agent_layout {
+                            window
+                                .dispatch_action(Box::new(zed_actions::assistant::ToggleFocus), cx);
+                        } else {
                             AgentSettings::set_layout(WindowLayout::Agent(None), fs.clone(), cx);
-                        }
 
-                        window.dispatch_action(Box::new(FocusWorkspaceSidebar), cx);
-                        window.dispatch_action(Box::new(zed_actions::assistant::ToggleFocus), cx);
+                            // Focus the agent panel after the layout change takes
+                            // effect.  `set_layout` writes to the settings file
+                            // asynchronously; dispatching ToggleFocus before the
+                            // file is reloaded would open the agent panel in its
+                            // old dock position and the subsequent panel migration
+                            // can leave the wrong panel visible.  Global observers
+                            // fire in registration order, so by the time this
+                            // observer runs the panel-migration observers (registered
+                            // at workspace creation) have already moved every panel.
+                            let window_handle = window.window_handle();
+                            let subscription = Rc::new(RefCell::new(None::<Subscription>));
+                            let subscription_ref = subscription.clone();
+                            *subscription.borrow_mut() =
+                                Some(cx.observe_global::<SettingsStore>(move |cx| {
+                                    if matches!(
+                                        AgentSettings::get_layout(cx),
+                                        WindowLayout::Agent(_)
+                                    ) {
+                                        cx.update_window(window_handle, |_, window, cx| {
+                                            window.dispatch_action(
+                                                Box::new(zed_actions::assistant::ToggleFocus),
+                                                cx,
+                                            );
+                                        })
+                                        .ok();
+                                        subscription_ref.borrow_mut().take();
+                                    }
+                                }));
+                        }
                     })),
                     on_dismiss: Some(Arc::new(|cx| {
                         ParallelAgentAnnouncement::set_dismissed(true, cx)