From 46ea164c04e66685c6b1764acef2ad830f20f21b Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Thu, 21 May 2026 12:04:35 +0000 Subject: [PATCH 1/2] Make camera bubble follow user across Mission Control spaces --- .../clips/desktop/src-tauri/src/clips/mod.rs | 3 ++ templates/clips/desktop/src-tauri/src/lib.rs | 8 ++++- templates/clips/desktop/src-tauri/src/util.rs | 36 +++++++++++++++++++ templates/clips/desktop/src/styles.css | 5 --- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/templates/clips/desktop/src-tauri/src/clips/mod.rs b/templates/clips/desktop/src-tauri/src/clips/mod.rs index 0feeab442..f0e6e0990 100644 --- a/templates/clips/desktop/src-tauri/src/clips/mod.rs +++ b/templates/clips/desktop/src-tauri/src/clips/mod.rs @@ -517,6 +517,9 @@ pub async fn show_bubble(app: AppHandle) -> Result<(), String> { // `getDisplayMedia`, which matches the other Clips chrome (popover, // toolbar, countdown) but NOT what users want for the camera bubble. let _ = win.show(); + // canJoinAllSpaces: bubble follows the user across every Mission Control + // space and every monitor — not just the one where the session started. + crate::util::set_window_can_join_all_spaces(&win); dlog!("[clips-tray] bubble shown at ({},{}) size {}", x, y, size); Ok(()) } diff --git a/templates/clips/desktop/src-tauri/src/lib.rs b/templates/clips/desktop/src-tauri/src/lib.rs index cbd26e4e0..835babf70 100644 --- a/templates/clips/desktop/src-tauri/src/lib.rs +++ b/templates/clips/desktop/src-tauri/src/lib.rs @@ -29,7 +29,7 @@ use state::{ DictationActive, DictationEnabled, LastTranscript, MeetingActive, PopoverShownAt, RecordingActive, TrayAnchor, TrayMeetings, VoiceWakePopover, }; -use util::{is_recording_active, set_capture_included}; +use util::{is_recording_active, set_capture_included, set_window_can_join_all_spaces}; // Embedded fallback icon — a tiny 16x16 solid purple PNG so the binary always // has *something* to display even if `icons/tray.png` is missing on disk. The @@ -185,6 +185,12 @@ pub fn run() { // popover boots. meetings_watcher::spawn_watcher(app.handle().clone()); + // Ensure the popover appears above every app on every Mission + // Control space so it's reachable before the user starts recording. + if let Some(window) = app.get_webview_window("popover") { + set_window_can_join_all_spaces(&window); + } + // Hide the popover on blur so it feels like a real menu-bar popover. // The 250ms guard is the important bit — during the tray-click // itself macOS briefly steals focus from the popover, which would diff --git a/templates/clips/desktop/src-tauri/src/util.rs b/templates/clips/desktop/src-tauri/src/util.rs index bcc3cf8d5..93532888a 100644 --- a/templates/clips/desktop/src-tauri/src/util.rs +++ b/templates/clips/desktop/src-tauri/src/util.rs @@ -190,6 +190,42 @@ pub fn show_without_activation(window: &WebviewWindow) { } } +/// Set `NSWindowCollectionBehaviorCanJoinAllSpaces` so the window follows the +/// user across every Mission Control space and appears on any monitor — not +/// just the one where it was first shown. +#[cfg(target_os = "macos")] +pub fn set_window_can_join_all_spaces(window: &WebviewWindow) { + let win = window.clone(); + if let Err(err) = win.clone().run_on_main_thread(move || { + let label = win.label().to_string(); + let ns_window_ptr = match win.ns_window() { + Ok(p) => p, + Err(err) => { + eprintln!( + "[clips-tray] set_window_can_join_all_spaces({label}): ns_window() failed: {err}" + ); + return; + } + }; + if ns_window_ptr.is_null() { + return; + } + unsafe { + let obj = ns_window_ptr as *mut objc2::runtime::AnyObject; + // NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0 = 1 + let _: () = objc2::msg_send![&*obj, setCollectionBehavior: 1usize]; + } + dlog!("[clips-tray] set_window_can_join_all_spaces({label}): applied"); + }) { + eprintln!( + "[clips-tray] set_window_can_join_all_spaces: run_on_main_thread failed: {err}" + ); + } +} + +#[cfg(not(target_os = "macos"))] +pub fn set_window_can_join_all_spaces(_window: &WebviewWindow) {} + #[cfg(not(target_os = "macos"))] pub fn show_without_activation(window: &WebviewWindow) { // On non-macOS we just fall back to the standard show. Focus stealing diff --git a/templates/clips/desktop/src/styles.css b/templates/clips/desktop/src/styles.css index c3809af65..ba86820a7 100644 --- a/templates/clips/desktop/src/styles.css +++ b/templates/clips/desktop/src/styles.css @@ -2312,9 +2312,6 @@ body[data-clips-route]:not([data-clips-route="popover"]) #root { border-radius: 50%; overflow: hidden; background: #000; - /* Drop shadow only — no brand ring. The bubble is the user's face; - a bright purple halo competes with what matters visually. */ - box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45); /* Position context for absolute children (close X, canvas). */ position: relative; /* Cursor hint — the drag region is here. */ @@ -2407,7 +2404,6 @@ body[data-clips-route]:not([data-clips-route="popover"]) #root { z-index: 3; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35); } .bubble-close:hover { @@ -2433,7 +2429,6 @@ body[data-clips-route]:not([data-clips-route="popover"]) #root { backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35); z-index: 3; /* Cursor over the pill should not be a grab cursor (we're not dragging the bubble when clicking its controls). */ From 7f870fabef6d79222890076049efa13fc81dd034 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Thu, 21 May 2026 12:16:42 +0000 Subject: [PATCH 2/2] Fix overlay visibility and window behavior across screens --- templates/clips/desktop/src-tauri/src/util.rs | 7 +++++-- templates/clips/desktop/src/styles.css | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/templates/clips/desktop/src-tauri/src/util.rs b/templates/clips/desktop/src-tauri/src/util.rs index 93532888a..938ffdd8e 100644 --- a/templates/clips/desktop/src-tauri/src/util.rs +++ b/templates/clips/desktop/src-tauri/src/util.rs @@ -212,8 +212,11 @@ pub fn set_window_can_join_all_spaces(window: &WebviewWindow) { } unsafe { let obj = ns_window_ptr as *mut objc2::runtime::AnyObject; - // NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0 = 1 - let _: () = objc2::msg_send![&*obj, setCollectionBehavior: 1usize]; + // Read the current behavior so we don't discard flags Tauri + // already set (e.g. NSWindowCollectionBehaviorManaged = 4). + // NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0 = 1. + let current: usize = objc2::msg_send![&*obj, collectionBehavior]; + let _: () = objc2::msg_send![&*obj, setCollectionBehavior: current | 1usize]; } dlog!("[clips-tray] set_window_can_join_all_spaces({label}): applied"); }) { diff --git a/templates/clips/desktop/src/styles.css b/templates/clips/desktop/src/styles.css index ba86820a7..82ebdefe3 100644 --- a/templates/clips/desktop/src/styles.css +++ b/templates/clips/desktop/src/styles.css @@ -2049,9 +2049,6 @@ body[data-clips-route]:not([data-clips-route="popover"]) #root { backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: - 0 10px 30px rgba(0, 0, 0, 0.35), - 0 2px 6px rgba(0, 0, 0, 0.3); color: white; cursor: grab; } @@ -2176,7 +2173,6 @@ body[data-clips-route]:not([data-clips-route="popover"]) #root { backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.06); - box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45); user-select: none; -webkit-user-select: none; cursor: grab;