Repository navigation
Raise all cmux windows on auth callback, Settings on top #3044
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2cb77c1
75c83c3
8e2cee4
249c4a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| if let settingsWindow, settingsWindow.isVisible { | ||||||||||||||||||||||||||||||||||||||||||
| settingsWindow.makeKeyAndOrderFront(nil) | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6807
to
+6816
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cd Sources && sed -n '6800,6835p' AppDelegate.swiftRepository: manaflow-ai/cmux Length of output: 1726 🏁 Script executed: rg "didBecomeKeyNotification" --context 5 -A 5Repository: manaflow-ai/cmux Length of output: 3840 🏁 Script executed: rg "let visible" Sources/AppDelegate.swift -B 3 -A 3Repository: manaflow-ai/cmux Length of output: 2980 Make the originally frontmost workspace key, not the backmost one.
🐛 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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