Fix native fullscreen unreachable on the main window (#5933)

Merged
AU
5mo ago
+135-0

Fixes #5933.

Problem

On macOS 26 (Tahoe), cmux's main window cannot enter native fullscreen by any route:

  • Command Palette Toggle Full Screen → no-op
  • ⌃⌘F → no-op
  • Green traffic-light button → only zooms/maximizes; never moves to its own fullscreen Space

All three entrypoints resolve the right CmuxMainWindow and call toggleFullScreen(_:), so window targeting is fine. The symptom set (toggle is a no-op and the green button zooms instead of fullscreening) is the textbook signature of a window that is not fullscreen-capable.

Root cause

CmuxMainWindow is created programmatically (never from a nib), so it cannot inherit fullscreen capability from Interface Builder. It never declared .fullScreenPrimary and relied on AppKit implicitly granting fullscreen to a resizable, titled window.

That implicit grant is not reliable across macOS versions. Verified empirically on macOS 26.5 (Tahoe): a window created with cmux's exact style mask ([.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView]) reports collectionBehavior.rawValue == 0 — i.e. it carries none of .fullScreenPrimary / .fullScreenAuxiliary / .fullScreenNone, and AppKit does not treat it as fullscreen-capable. This matches CLAUDE.md's standing warning that AppKit semantics change silently between macOS majors, so implicit defaults must not be assumed stable.

Fix

Declare .fullScreenPrimary explicitly in CmuxMainWindow's initializer, via a pure, unit-testable canonicalCollectionBehavior(_:) helper. The helper:

  • inserts .fullScreenPrimary so native fullscreen is always reachable,
  • strips any stale .fullScreenNone (mutually exclusive with primary),
  • preserves unrelated bits, so it composes with the temporary .fullScreenDisallowsTiling opt-out the window factory layers on when spawning a window out of an existing fullscreen Space.

It is idempotent and a no-op where AppKit would have granted fullscreen anyway, so it cannot regress setups that already worked.

Tests

Two-commit red/green:

  1. Commit 1 adds CmuxMainWindowFullScreenCapabilityTests (wired into project.pbxproj). The window-instantiation test fails on current code because the default collection behavior lacks .fullScreenPrimary.
  2. Commit 2 adds the fix, turning the test green, plus pure-function tests for the canonicalCollectionBehavior(_:) contract (adds primary, drops stale none, preserves unrelated bits, idempotent).

Scope note: sleep/wake size shrink

The issue also mentions the window shrinking after display sleep/wake. The reporter is on v0.64.14; the sleep/wake reposition fix (#6305, CmuxMainWindow.constrainFrameRect) landed in v0.64.17 / nightly and is already on main, which lines up with the reporter's note that the bug "does not reproduce on NIGHTLY." This PR targets the fullscreen-capability defect, which is the part still present on main and is not addressed anywhere else. The fullscreen change is orthogonal to and composes with the existing constrain-frame logic.

Localization

No user-facing strings added or changed — the "Toggle Full Screen" command and its command.toggleFullScreen.* strings already exist in Localizable.xcstrings. Change is window-capability code + tests + pbxproj wiring only.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Make the main window explicitly fullscreen-capable so native fullscreen works again via Toggle Full Screen, ⌃⌘F, and the green button on macOS 26 and multi-monitor setups. Fixes #5933.

  • Bug Fixes
    • Add canonicalCollectionBehavior(_:) and apply it in CmuxMainWindow init to insert .fullScreenPrimary, remove .fullScreenNone, and preserve other behavior bits.
    • Add tests for fullscreen capability and the helper’s contract (adds primary, drops none, preserves bits, idempotent), now using Swift Testing (@Suite, @Test).