cli: Defer app activation until after open-behavior prompt (#53915)

Eric Holk created

On macOS, the CLI uses `LSOpenFromURLSpec` to deliver the `zed-cli://`
URL to the running Zed app. With `kLSLaunchDefaults`, Launch Services
activates (brings to foreground) the Zed app as a side effect of URL
delivery. This happens before the IPC handshake completes and before the
user can answer the terminal prompt asking whether to open in a new
window or existing workspace, forcing them to manually switch back to
the terminal.

## Fix

**CLI side** (`crates/cli/src/main.rs`): Add `kLSLaunchDontSwitch` to
the launch flags so macOS delivers the URL without activating the app.

**App side** (`crates/zed/src/zed/open_listener.rs`): Add
`cx.activate(true)` in `handle_cli_connection` after the prompt resolves
(or immediately for the URL-only path), so the app comes to the
foreground at the right time.

Linux and Windows are unaffected — they use Unix sockets and named pipes
respectively, which don't have activation side effects.

Release Notes:

- Fixed the Zed CLI activating the app window before the user answers
the open-behavior prompt in the terminal.

Change summary

crates/cli/src/main.rs              | 6 ++++--
crates/zed/src/zed/open_listener.rs | 3 +++
2 files changed, 7 insertions(+), 2 deletions(-)

Detailed changes

crates/cli/src/main.rs 🔗

@@ -1217,7 +1217,9 @@ mod mac_os {
         string::kCFStringEncodingUTF8,
         url::{CFURL, CFURLCreateWithBytes},
     };
-    use core_services::{LSLaunchURLSpec, LSOpenFromURLSpec, kLSLaunchDefaults};
+    use core_services::{
+        LSLaunchURLSpec, LSOpenFromURLSpec, kLSLaunchDefaults, kLSLaunchDontSwitch,
+    };
     use serde::Deserialize;
     use std::{
         ffi::OsStr,
@@ -1316,7 +1318,7 @@ mod mac_os {
                                 appURL: app_url.as_concrete_TypeRef(),
                                 itemURLs: urls_to_open.as_concrete_TypeRef(),
                                 passThruParams: ptr::null(),
-                                launchFlags: kLSLaunchDefaults,
+                                launchFlags: kLSLaunchDefaults | kLSLaunchDontSwitch,
                                 asyncRefCon: ptr::null_mut(),
                             },
                             ptr::null_mut(),

crates/zed/src/zed/open_listener.rs 🔗

@@ -482,6 +482,7 @@ pub async fn handle_cli_connection(
                             cx,
                         ) {
                             Ok(open_request) => {
+                                cx.activate(true);
                                 handle_open_request(open_request, app_state.clone(), cx);
                                 responses.send(CliResponse::Exit { status: 0 }).log_err();
                             }
@@ -518,6 +519,8 @@ pub async fn handle_cli_connection(
                     }
                 }
 
+                cx.update(|cx| cx.activate(true));
+
                 let open_workspace_result = open_workspaces(
                     paths,
                     diff_paths,