Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2522,13 +2522,22 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent

func application(_ application: NSApplication, open urls: [URL]) {
let authCallbacks = urls.filter(AuthCallbackRouter.isAuthCallbackURL)
if !authCallbacks.isEmpty {
// macOS 14+ only grants activate() in response to a user event
// when the call fires synchronously inside the event handler.
// Once we hop to a Task {} the "user event context" is gone
// and cooperative activation refuses. Activate here first,
// then re-order windows after handleCallbackURL finishes.
focusAppForAuthCallback()
}
for url in authCallbacks {
Task { @MainActor in
do {
try await AuthManager.shared.handleCallbackURL(url)
} catch {
NSLog("auth.callback failed: %@", "\(error)")
}
self.raiseWindowsAfterAuthCallback()
}
}

Expand Down Expand Up @@ -6779,6 +6788,35 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
}
}

/// Synchronous, runs inside `application(_:open:)` so macOS 14+
/// cooperative activation still sees the user-event context.
private func focusAppForAuthCallback() {
NSApp.activate()
NSRunningApplication.current.activate(options: [.activateAllWindows])
}

/// Runs after `handleCallbackURL` completes. Re-orders windows so the
/// user's workspace windows keep their relative z-order and the
/// Settings window lands on top.
private func raiseWindowsAfterAuthCallback() {
NSApp.activate()
NSRunningApplication.current.activate(options: [.activateAllWindows])

let settingsWindow = SettingsWindowController.shared.window
let visible = NSApp.orderedWindows.filter { $0.isVisible }
let workspaceWindows = visible.reversed().filter { $0 !== settingsWindow }
for (idx, window) in workspaceWindows.enumerated() {
if idx == 0 {
window.makeKeyAndOrderFront(nil)
} else {
window.orderFrontRegardless()

@cubic-dev-ai cubic-dev-ai Bot Apr 20, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Using orderFrontRegardless() here can leave the top workspace window non-key when Settings is not visible, causing keyboard focus to remain on a different window.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/AppDelegate.swift, line 6812:

<comment>Using `orderFrontRegardless()` here can leave the top workspace window non-key when Settings is not visible, causing keyboard focus to remain on a different window.</comment>

<file context>
@@ -6780,18 +6788,29 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
+            if idx == 0 {
+                window.makeKeyAndOrderFront(nil)
+            } else {
+                window.orderFrontRegardless()
+            }
         }
</file context>
Suggested change
window.orderFrontRegardless()
window.makeKeyAndOrderFront(nil)
Fix with Cubic

}
}
if let settingsWindow, settingsWindow.isVisible {
settingsWindow.makeKeyAndOrderFront(nil)
Comment on lines +6807 to +6816

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd Sources && sed -n '6800,6835p' AppDelegate.swift

Repository: manaflow-ai/cmux

Length of output: 1726


🏁 Script executed:

rg "didBecomeKeyNotification" --context 5 -A 5

Repository: manaflow-ai/cmux

Length of output: 3840


🏁 Script executed:

rg "let visible" Sources/AppDelegate.swift -B 3 -A 3

Repository: manaflow-ai/cmux

Length of output: 2980


Make the originally frontmost workspace key, not the backmost one.

NSApp.orderedWindows returns windows front-to-back. After visible.reversed(), the loop's first iteration (idx == 0) makes workspaceWindows[0]—the originally backmost window—the key window via makeKeyAndOrderFront(). The didBecomeKeyNotification observer then calls setActiveMainWindow(), leaving the app context pointing to the wrong workspace. Later orderFrontRegardless() calls restore visual z-order but don't fix the active context state.

🐛 Proposed fix
         let workspaceWindows = visible.reversed().filter { $0 !== settingsWindow }
-        for (idx, window) in workspaceWindows.enumerated() {
-            if idx == 0 {
-                window.makeKeyAndOrderFront(nil)
-            } else {
-                window.orderFrontRegardless()
-            }
+        for window in workspaceWindows {
+            window.orderFrontRegardless()
+        }
+        if let frontmostWorkspaceWindow = workspaceWindows.last {
+            frontmostWorkspaceWindow.makeKeyAndOrderFront(nil)
         }
         if let settingsWindow, settingsWindow.isVisible {
             settingsWindow.makeKeyAndOrderFront(nil)
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let workspaceWindows = visible.reversed().filter { $0 !== settingsWindow }
for (idx, window) in workspaceWindows.enumerated() {
if idx == 0 {
window.makeKeyAndOrderFront(nil)
} else {
window.orderFrontRegardless()
}
}
if let settingsWindow, settingsWindow.isVisible {
settingsWindow.makeKeyAndOrderFront(nil)
let workspaceWindows = visible.reversed().filter { $0 !== settingsWindow }
for window in workspaceWindows {
window.orderFrontRegardless()
}
if let frontmostWorkspaceWindow = workspaceWindows.last {
frontmostWorkspaceWindow.makeKeyAndOrderFront(nil)
}
if let settingsWindow, settingsWindow.isVisible {
settingsWindow.makeKeyAndOrderFront(nil)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/AppDelegate.swift` around lines 6813 - 6822, The current logic
reverses NSApp.orderedWindows then treats the first element as the frontmost,
making the originally backmost window key; change the selection/ordering so the
original frontmost becomes key. Specifically, adjust how workspaceWindows is
built (stop using visible.reversed() or instead pick the last element as the
key) so that in the loop over workspaceWindows the element handled when idx == 0
is the original frontmost window; keep the rest using orderFrontRegardless(),
and continue to skip settingsWindow (refer to visible, workspaceWindows,
settingsWindow, makeKeyAndOrderFront, orderFrontRegardless).

}
}

private func claimAuthCallbackURLSchemes() {
// Pin the current build as the default for cmux:// and cmux-dev://
// so the auth-callback deeplink routes back to this app instead of an
Expand Down
Loading