Skip to content

Fix Cmd+Tab activation ordering (#744) - #766

Merged
austinywang merged 1 commit into
mainfrom
issue-744-cmd-tab-activation
Mar 3, 2026
Merged

austinywang merged 1 commit into
mainfrom
issue-744-cmd-tab-activation

Conversation

@austinywang

@austinywang austinywang commented Mar 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Fixes activation/window-ordering behavior so cmux reliably comes to front when switching back via Cmd+Tab.
  • Enforces regular app activation policy during launch.
  • On , restores the best-known main terminal window and reorders it to key/front.
  • Tracks last active main window for deterministic restore in multi-window scenarios.\n\n## Issue
    Closes Cmd+tab doesn't always work #744

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved window focus management to restore the most recently active window when the app is activated.
    • Fixed notification interaction to properly restore window focus when marking notifications as read.

@vercel

vercel Bot commented Mar 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Building Building Preview, Comment Mar 3, 2026 0:31am

@coderabbitai

coderabbitai Bot commented Mar 3, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added tracking for the last active main window and implemented helper methods to determine and bring the appropriate main window to the front when the application is activated, integrating this logic with app activation events and window focus notifications.

Changes

Cohort / File(s) Summary
Window Activation Management
Sources/AppDelegate.swift
Added lastActiveMainWindow property to track the most recently active main window. Implemented bringMainWindowToFrontOnActivationIfNeeded() to intelligently select and bring the appropriate main window to the foreground on app activation. Integrated window tracking via NSWindow.didBecomeKeyNotification handler and triggered foregrounding logic in applicationDidBecomeActive() and after marking notifications as read. Ensures app activation policy is set to regular on startup.

Sequence Diagram

sequenceDiagram
    participant User
    participant System as macOS System
    participant AppDelegate
    participant WindowManager as Window Manager
    participant MainWindow as Main Window
    
    User->>System: Press Cmd+Tab to switch to app
    System->>AppDelegate: applicationDidBecomeActive()
    AppDelegate->>WindowManager: bringMainWindowToFrontOnActivationIfNeeded()
    WindowManager->>WindowManager: frontmostKnownMainWindow()
    WindowManager->>WindowManager: Check lastActiveMainWindow, then keyWindow
    WindowManager-->>MainWindow: Identify appropriate main window
    MainWindow->>MainWindow: orderFrontRegardless()
    MainWindow->>User: Window brought to foreground
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 When Cmd+Tab was pressed with care,
The window would hide, lost in air,
But now with tracking, bold and bright,
The main window springs into sight!
App activation—logic divine,
The perfect view's now yours to find! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix Cmd+Tab activation ordering' directly addresses the main change, which is fixing window activation behavior when switching back to the app via Cmd+Tab as described in issue #744.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #744: tracks last active window, enforces regular activation policy, restores the best-known main window on activation, and ensures deterministic window reordering.
Out of Scope Changes check ✅ Passed All changes in AppDelegate.swift are directly related to fixing Cmd+Tab activation ordering. The addition of window tracking, activation policy enforcement, and window foregrounding logic are all necessary to resolve the reported issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-744-cmd-tab-activation

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/AppDelegate.swift (1)

1707-1728: ⚠️ Potential issue | 🔴 Critical

Move activation fronting out of unread-notification guards.

At Line 1727, bringMainWindowToFrontOnActivationIfNeeded() only runs when unread notifications exist. In normal activations (no unread), the method exits early and never restores front/key ordering, which can leave the Cmd+Tab issue unresolved.

💡 Proposed fix
 func applicationDidBecomeActive(_ notification: Notification) {
     sentryBreadcrumb("app.didBecomeActive", category: "lifecycle", data: [
         "tabCount": tabManager?.tabs.count ?? 0
     ])
@@
     if TelemetrySettings.enabledForCurrentLaunch && !isRunningUnderXCTest(env) {
         PostHogAnalytics.shared.trackDailyActive(reason: "didBecomeActive")
         PostHogAnalytics.shared.trackHourlyActive(reason: "didBecomeActive")
     }
 
+    bringMainWindowToFrontOnActivationIfNeeded()
+
     guard let tabManager, let notificationStore else { return }
     guard let tabId = tabManager.selectedTabId else { return }
     let surfaceId = tabManager.focusedSurfaceId(for: tabId)
     guard notificationStore.hasUnreadNotification(forTabId: tabId, surfaceId: surfaceId) else { return }
@@
     if let surfaceId,
        let tab = tabManager.tabs.first(where: { $0.id == tabId }) {
         tab.triggerNotificationFocusFlash(panelId: surfaceId, requiresSplit: false, shouldFocus: false)
     }
     notificationStore.markRead(forTabId: tabId, surfaceId: surfaceId)
-    bringMainWindowToFrontOnActivationIfNeeded()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/AppDelegate.swift` around lines 1707 - 1728, In
applicationDidBecomeActive(_:), bringMainWindowToFrontOnActivationIfNeeded() is
currently invoked after the unread-notification guards so it only runs when
notifications exist; move the call so it runs for every activation (e.g., place
a call to bringMainWindowToFrontOnActivationIfNeeded() immediately before the
guard sequence that checks tabManager/notificationStore/tabId or at the top of
the notification-handling block) so that
bringMainWindowToFrontOnActivationIfNeeded() is executed regardless of
notificationStore.hasUnreadNotification(forTabId:surfaceId:); keep the existing
notification handling (tab.triggerNotificationFocusFlash and
notificationStore.markRead) unchanged.
🧹 Nitpick comments (1)
Sources/AppDelegate.swift (1)

6790-6792: Track only main-terminal windows in lastActiveMainWindow (optional cleanup).

At Line 6791, every key window updates lastActiveMainWindow. Consider gating this to main terminal windows so the field remains semantically precise and deterministic.

♻️ Suggested tweak
         ) { [weak self] note in
             guard let self, let window = note.object as? NSWindow else { return }
-            self.lastActiveMainWindow = window
+            if self.isMainTerminalWindow(window) {
+                self.lastActiveMainWindow = window
+            }
             self.setActiveMainWindow(window)
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/AppDelegate.swift` around lines 6790 - 6792, The handler currently
sets lastActiveMainWindow for every key window; restrict it to only main
terminal windows by adding a predicate check (e.g. create a helper
isMainTerminalWindow(_ window: NSWindow) that verifies whatever makes a window a
"main terminal" — class/type of windowController, window.identifier, or its root
view controller) and only call self.lastActiveMainWindow = window and
self.setActiveMainWindow(window) when that predicate returns true; update the
notification handling code around lastActiveMainWindow and setActiveMainWindow
to use this helper so the field remains semantically precise.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@Sources/AppDelegate.swift`:
- Around line 1707-1728: In applicationDidBecomeActive(_:),
bringMainWindowToFrontOnActivationIfNeeded() is currently invoked after the
unread-notification guards so it only runs when notifications exist; move the
call so it runs for every activation (e.g., place a call to
bringMainWindowToFrontOnActivationIfNeeded() immediately before the guard
sequence that checks tabManager/notificationStore/tabId or at the top of the
notification-handling block) so that
bringMainWindowToFrontOnActivationIfNeeded() is executed regardless of
notificationStore.hasUnreadNotification(forTabId:surfaceId:); keep the existing
notification handling (tab.triggerNotificationFocusFlash and
notificationStore.markRead) unchanged.

---

Nitpick comments:
In `@Sources/AppDelegate.swift`:
- Around line 6790-6792: The handler currently sets lastActiveMainWindow for
every key window; restrict it to only main terminal windows by adding a
predicate check (e.g. create a helper isMainTerminalWindow(_ window: NSWindow)
that verifies whatever makes a window a "main terminal" — class/type of
windowController, window.identifier, or its root view controller) and only call
self.lastActiveMainWindow = window and self.setActiveMainWindow(window) when
that predicate returns true; update the notification handling code around
lastActiveMainWindow and setActiveMainWindow to use this helper so the field
remains semantically precise.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d9587c and 8b389e5.

📒 Files selected for processing (1)
  • Sources/AppDelegate.swift

@greptile-apps

greptile-apps Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Improves Cmd+Tab window activation by setting regular activation policy on launch and tracking the last active main terminal window. When the app is activated (e.g., via Cmd+Tab or clicking a notification), the implementation now restores the most recently active main window using a fallback hierarchy: last active window → key window → main window → active tab manager's window → first visible window → any window.

  • Added lastActiveMainWindow property to track the most recently active main terminal window
  • Enforces .regular activation policy during applicationDidFinishLaunching
  • Calls bringMainWindowToFrontOnActivationIfNeeded() after marking notifications as read
  • Implemented frontmostKnownMainWindow() with a robust fallback strategy for multi-window scenarios
  • One logic issue: lastActiveMainWindow is currently set for any window that becomes key, not just main terminal windows

Confidence Score: 4/5

  • Safe to merge with one minor logic issue that should be addressed
  • The implementation correctly addresses the Cmd+Tab activation ordering issue with a well-structured fallback hierarchy. One logic bug exists where lastActiveMainWindow tracks all windows instead of only main terminal windows, though frontmostKnownMainWindow() filters this out. The fix is straightforward and won't cause runtime errors.
  • Sources/AppDelegate.swift at line 6791 needs the lastActiveMainWindow assignment to be conditional

Important Files Changed

Filename Overview
Sources/AppDelegate.swift Fixes Cmd+Tab activation by tracking last active window and enforcing regular activation policy; one issue with lastActiveMainWindow tracking non-main windows

Last reviewed commit: 8b389e5

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread Sources/AppDelegate.swift
queue: .main
) { [weak self] note in
guard let self, let window = note.object as? NSWindow else { return }
self.lastActiveMainWindow = window

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lastActiveMainWindow is set for any window that becomes key, not just main terminal windows. If a non-main window (settings, about, etc.) becomes key, lastActiveMainWindow will point to it, though frontmostKnownMainWindow() filters it out later.

Suggested change
self.lastActiveMainWindow = window
if isMainTerminalWindow(window) {
self.lastActiveMainWindow = window
}

@austinywang
austinywang merged commit a6f6485 into main Mar 3, 2026
9 checks passed
austinywang added a commit that referenced this pull request Mar 4, 2026
austinywang added a commit that referenced this pull request Mar 4, 2026
austinywang added a commit that referenced this pull request Mar 4, 2026
* Revert "Fix Cmd+Tab activation ordering for cmux windows (#744) (#766)"

This reverts commit a6f6485.

* Fix debug Ghostty theme loading fallback
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
* Revert "Fix Cmd+Tab activation ordering for cmux windows (manaflow-ai#744) (manaflow-ai#766)"

This reverts commit 30671a5.

* Fix debug Ghostty theme loading fallback
israeligal pushed a commit to israeligal/cmux that referenced this pull request May 2, 2026
rodchristiansen pushed a commit to rodchristiansen/cmux that referenced this pull request Sep 2, 2026

This branch was successfully deployed

1 active deployment
Preview — 8b389e59 Deployed Mar 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cmd+tab doesn't always work

1 participant