Skip to content

Fix minimal mode toggle relayout hang - #7076

Merged
austinywang merged 9 commits into
mainfrom
issue-5732-minimal-mode-toggling-minimal-mode-hangs-the
Jun 30, 2026
Merged

austinywang merged 9 commits into
mainfrom
issue-5732-minimal-mode-toggling-minimal-mode-hangs-the

Conversation

@austinywang

@austinywang austinywang commented Jun 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds a regression test that mounts the real ContentView and toggles the workspace presentation mode through UserDefaults, asserting minimal-mode toggles do not re-evaluate the heavy root/content/sidebar bodies.
  • Moves minimal-mode @AppStorage subscriptions out of ContentView, WorkspaceContentView, and VerticalTabsSidebar into narrow chrome-specific leaf views/modifiers so the toggle updates only the titlebar/safe-area/overlay chrome that changes.

Fixes #5732

Testing

  • git diff --check
  • ./scripts/check-pbxproj.sh
  • ./scripts/lint-pbxproj-test-wiring.sh
  • python3 scripts/swift_file_length_budget.py

Per task instruction, I did not run ./scripts/reload.sh, reload-cloud, or xcodebuild; CI will provide compile/test coverage.

Localization audit: no user-facing strings were added or changed; staged Swift additions were checked for newly-added string literals.

Demo Video

  • Video URL or attachment: not included; this is a relayout/invalidation fix covered by regression test and CI.

Review Trigger (Copy/Paste as PR comment)

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • I tested the change locally
  • I added or updated tests for behavior changes
  • I updated docs/changelog if needed
  • I requested bot reviews after my latest commit (copy/paste block above or equivalent)
  • All code review bot comments are resolved
  • All human review comments are resolved

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


Summary by cubic

Fixes a hang when toggling Minimal Mode by scoping updates to chrome-only layers so heavy views don’t re-render. Adds a regression test that mounts a real window and asserts zero body re-evaluations on toggles. Fixes #5732.

  • Bug Fixes

    • Moved minimal-mode @AppStorage out of ContentView, WorkspaceContentView, and VerticalTabsSidebar into leaf layers/modifiers: WorkspaceTitlebarModeLayer, MinimalModeTitlebarEventSurfaceLayer, WorkspaceContentMinimalModeSafeAreaModifier, WorkspacePresentationModeContentTopPaddingModifier, MinimalModeSidebarTitlebarControlsOverlay.
    • Centralized mode-change handling via WorkspacePresentationModeChangeObserver → handleWorkspacePresentationModeChange to update titlebar controls, window decorations, geometry, resizer band, and traffic-light inset without recomputing content.
    • Fixed notifications popover anchor in Minimal Mode by routing the anchor view through MinimalModeSidebarTitlebarControlsOverlay.
    • Added a regression test using MinimalModeInvalidationProbe (mounts a real window; toggles via isolated UserDefaults with defaultAppStorage) to assert zero body re-evaluations; drains the run loop for updates and switches to swift-testing assertions.
  • Refactors

    • Replaced inline padding/safe-area/event handling with focused layers/modifiers and removed heavy view subscriptions; palette commands now write UserDefaults directly.
    • Introduced WorkspacePresentationModeRuntimeCache to keep mode side effects on observed state (drives traffic-light inset and fullscreen updates) without invalidating ContentView.

Written for commit 0dd40bc. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Improved minimal-mode handling across the workspace, including better titlebar, sidebar, and safe-area behavior.
    • Added support for smoother workspace mode transitions and layout updates.
    • Introduced debug-only visibility probes to help validate UI refresh behavior.
  • Bug Fixes

    • Fixed inconsistent minimal-mode UI updates when toggling modes or entering/exiting fullscreen.
    • Improved command palette behavior so workspace mode changes are reflected more reliably.

@vercel

vercel Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled Jun 30, 2026 3:03am
cmux-staging Building Building Preview, Comment Jun 30, 2026 3:03am

@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Refactors minimal-mode state derivation and change handling out of ContentView into dedicated SwiftUI view and modifier types (WorkspacePresentationModeChangeObserver, WorkspaceTitlebarModeLayer, MinimalModeSidebarTitlebarControlsOverlay, WorkspaceContentMinimalModeSafeAreaModifier, WorkspacePresentationModeContentTopPaddingModifier, MinimalModeTitlebarEventSurfaceLayer). Adds #if DEBUG invalidation probes across ContentView, WorkspaceContentView, and VerticalTabsSidebar. Migrates visibility tests from XCTest to Swift Testing.

Changes

Minimal-Mode Refactor and Invalidation Optimization

Layer / File(s) Summary
New minimal-mode view/modifier components
Sources/WorkspacePresentationModeChangeObserver.swift, Sources/WorkspaceTitlebarModeLayer.swift, Sources/MinimalModeTitlebarEventSurfaceLayer.swift, Sources/MinimalModeSidebarTitlebarControlsOverlay.swift, Sources/WorkspaceContentMinimalModeSafeAreaModifier.swift, Sources/WorkspacePresentationModeContentTopPaddingModifier.swift
Six new SwiftUI types each own a single minimal-mode concern: observing mode changes, gating titlebar rendering, providing the sidebar overlay, managing safe-area edges, and applying top padding.
DEBUG invalidation probe infrastructure
Sources/Debug/MinimalModeInvalidationProbe.swift, Sources/Debug/MinimalModeInvalidationProbeKey.swift, Sources/Debug/EnvironmentValues+MinimalModeInvalidationProbe.swift
Introduces MinimalModeInvalidationProbe struct with three optional body-probe closures, its EnvironmentKey, and a EnvironmentValues computed property, all gated behind #if DEBUG.
ContentView minimal-mode state and change-handler wiring
Sources/ContentView.swift (lines 971–2525, 3011–7715)
Replaces isMinimalMode with currentIsMinimalMode, extracts all side effects into handleWorkspacePresentationModeChange(isMinimalMode:), removes old onChange(of: isMinimalMode), wires WorkspacePresentationModeChangeObserver and WorkspaceTitlebarModeLayer into body, updates fullscreen paths and command handlers to write directly to UserDefaults.
VerticalTabsSidebar overlay centralization
Sources/ContentView.swift (lines 10070–10924)
Removes local isMinimalMode, adds minimalModeSidebarTitlebarControlsOverlay() helper used in both the normal scroll area and extension sidebar timeline, and adds DEBUG probe call.
WorkspaceContentView safe-area and probe wiring
Sources/WorkspaceContentView.swift
Removes workspacePresentationMode/isMinimalMode storage, applies WorkspaceContentMinimalModeSafeAreaModifier, and adds DEBUG probe invocation.
Xcode project registration
cmux.xcodeproj/project.pbxproj
Registers all new Swift source files in PBXBuildFile, PBXFileReference, PBXGroup, and PBXSourcesBuildPhase.
Test suite migration to Swift Testing
cmuxTests/WorkspaceContentViewVisibilityTests.swift
Migrates from XCTestCase to @Suite/@Test/#expect/#require, rewrites minimal-mode invalidation test with probe-count assertions, and converts all XCTAssert* calls.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#3102: Introduced WorkspacePresentationModeSettings helpers (isMinimal, modeKey) that this PR depends on for all mode-derivation logic.
  • manaflow-ai/cmux#6580: Also modifies cmuxTests/WorkspaceContentViewVisibilityTests.swift, adjusting geometry values in the same test suite being migrated here.
  • manaflow-ai/cmux#4309: Touches extension sidebar rendering in ContentView, the same code paths where this PR replaces conditional HiddenTitlebarSidebarControlsView mounting.

Suggested reviewers

  • lawrencecchen

🐇 A rabbit hops through the code tonight,
Extracting mode checks, making bodies light.
No more big relayout on the main thread's dime,
Each modifier now reads @AppStorage just in time.
The beachball is gone — oh, what a delight! 🎉


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (3 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swiftui State Layout ❌ Error ContentView and WorkspaceContentView invoke debug probe closures from body (let _ = { ... }()), which is render-time side-effect/state mutation. Move probes out of body (e.g. lifecycle hook or test-only wrapper) and keep production SwiftUI views side-effect free during render.
Cmux Architecture Rethink ❌ Error WorkspacePresentationModeChangeObserver adds a separate minimal-mode observer/side channel, and its initial onAppear sync can be dropped when observedWindow is nil. Make window registration the single owner of the initial minimal-mode chrome sync (retry once the window exists) and keep the new leaf views pure projections.
Cmux No Test Or Debug Seam In Production Source ❌ Error Sources/ContentView.swift, WorkspaceContentView.swift, and VerticalTabsSidebar.swift add #if DEBUG minimalModeInvalidationProbe seams and body callbacks in production source. Move the probe wiring into Tests/ via @testable import, or remove the production env/member hooks and keep any debug-only facility isolated in Sources/Debug.
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 (21 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: fixing the Minimal Mode toggle relayout hang.
Linked Issues check ✅ Passed The changes address #5732 by reducing Minimal Mode relayout work and adding a regression test for body re-evaluation.
Out of Scope Changes check ✅ Passed No clear unrelated code changes stand out; the new layers, debug probes, and test updates all support the Minimal Mode fix.
Cmux Swift Actor Isolation ✅ Passed New production additions are SwiftUI views/modifiers or pure settings helpers; no new Sendable reference models, service protocols, or background-store access were introduced.
Cmux Swift Blocking Runtime ✅ Passed PASS: The PR adds no new blocking waits/sleeps/locks/syncs in production Swift; the new minimal-mode views are callback/state-driven only.
Cmux Browser Automation Off-Main ✅ Passed Diff only changes minimal-mode SwiftUI views/tests; no browser.* socket commands, router, or policy files were touched, so the off-main browser rule is not implicated.
Cmux Expensive Synchronous Load ✅ Passed No RestorableAgentSessionIndex.load()/agent-history JSONL/transcript parsing was added; touched files only add lightweight minimal-mode chrome views and test probes.
Cmux Cache Substitution Correctness ✅ Passed No stale-cache substitution: minimal mode now reads UserDefaults freshly via WorkspacePresentationModeSettings.isMinimal(), and AppStorage is confined to transient chrome UI.
Cmux No Hacky Sleeps ✅ Passed No TS/JS/shell/build runtime files changed; the only wait-like call is in a Swift test helper, which the rule allows.
Cmux Algorithmic Complexity ✅ Passed The new minimal-mode views only do constant-time AppStorage/default lookups and simple conditionals; no new nested scans, sorts, or batch rescans were added to hot paths.
Cmux Swift Concurrency ✅ Passed New minimal-mode code uses SwiftUI/AppKit callbacks and @AppStorage; no new DispatchQueue/Combine/completion-handler/fire-and-forget Task patterns were introduced.
Cmux Swift @Concurrent ✅ Passed PASS: touched files add only synchronous SwiftUI views/modifiers, and the one new async test helper is @MainActor; no nonisolated async/@Concurrent changes in diff.
Cmux Swift File And Package Boundaries ✅ Passed New files are tiny UI/AppKit glue; the oversized app views were only lightly refactored, with no new oversized or mixed-responsibility boundary violation.
Cmux Swiftpm Lockfiles ✅ Passed The PR only adds source-file entries to cmux.xcodeproj; there are no Package.resolved, .gitignore, or SwiftPM package-reference changes to satisfy this rule.
Cmux Swift Logging ✅ Passed Scanned the changed Swift files and found no print/debugPrint/dump/NSLog, FileHandle/stdout logging, or Logger constants; only debug probe closures and pure SwiftUI state.
Cmux User-Facing Error Privacy ✅ Passed Changed files add only internal SwiftUI plumbing/debug probes; no user-facing alerts, error bodies, or recovery copy were introduced.
Cmux Full Internationalization ✅ Passed No production user-facing text or locale resources were added or changed; the new Swift files are structural/debug-only and reuse existing localized copy.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No new standalone user-visible windows/controllers or close-shortcut wiring were added; only main-window plumbing and a test-only NSWindow fixture appear.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source, test, or project-file updates; no logs, caches, build output, or scratch artifacts were added.
Cmux No Ambient Global State ✅ Passed New code uses scoped views/modifiers; the only new static is a DEBUG EnvironmentKey default, and no new top-level API/singleton state appears.
Description check ✅ Passed The PR description follows the required template and includes summary, testing, review trigger, and checklist, with only the demo video left intentionally absent.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-5732-minimal-mode-toggling-minimal-mode-hangs-the

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@austinywang

Copy link
Copy Markdown
Contributor Author

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@austinywang I can't start this review because your workspace has reached its free monthly review limit. cubic has reviewed 241,260 of the 240,000 allowed lines of code this month. Reviews resume on 1 July 2026 (in 1 day). Paid plans include much higher monthly review limits. Upgrade now to resume reviews.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

@austinywang: I’ll review the changes now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@austinywang

Copy link
Copy Markdown
Contributor Author

Latest head after merging origin/main: @codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Latest head after merging origin/main: @codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@austinywang I can't start this review because your workspace has reached its free monthly review limit. cubic has reviewed 241,260 of the 240,000 allowed lines of code this month. Reviews resume on 1 July 2026 (in 1 day). Paid plans include much higher monthly review limits. Upgrade now to resume reviews.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

1 similar comment
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a relayout hang when toggling Minimal Mode by moving @AppStorage(WorkspacePresentationModeSettings.modeKey) subscriptions out of the three heavy views (ContentView, WorkspaceContentView, VerticalTabsSidebar) and into narrow chrome-only leaf views and modifiers. A non-observable final class runtime cache (WorkspacePresentationModeRuntimeCache) stored as @State lets ContentView read the current mode for AppKit side-effect callbacks without subscribing to UserDefaults changes.

  • WorkspacePresentationModeChangeObserver is embedded as a zero-size .background() in ContentView.body; it owns the single @AppStorage subscription for the heavy view tree, fires on both onAppear and onChange, and delegates all AppKit imperative work to handleWorkspacePresentationModeChange.
  • Six new narrow views/modifiers each carry their own lightweight @AppStorage subscriptions so only the chrome layers that actually change on a mode toggle re-render.
  • A regression test mounts a real ContentView window, injects #if DEBUG environment-closure probes, toggles mode via an isolated UserDefaults suite, and asserts zero body re-evaluations on all three heavy views.

Confidence Score: 5/5

The change is safe to merge: it replaces broad @AppStorage subscriptions on three heavy views with narrow chrome-only leaf subscribers, backed by a deliberately non-observable cache class so ContentView itself never invalidates on a mode toggle.

All AppKit side-effect calls are correctly consolidated in handleWorkspacePresentationModeChange, triggered by both onAppear and onChange in WorkspacePresentationModeChangeObserver. The runtime cache is initialized from UserDefaults.standard at @State creation and re-confirmed on first mount via onAppear. The regression test uses an isolated UserDefaults suite, mounts a real window, and verifies zero heavy-view body re-evaluations after a mode toggle.

No files require special attention.

Important Files Changed

Filename Overview
Sources/ContentView.swift Removes @AppStorage minimal-mode subscription from ContentView and VerticalTabsSidebar; adds WorkspacePresentationModeRuntimeCache @State and routes mode-change side effects through handleWorkspacePresentationModeChange.
Sources/WorkspacePresentationModeRuntimeCache.swift New non-observable final class used as @State; init reads from UserDefaults.standard and is corrected on first mount via WorkspacePresentationModeChangeObserver.onAppear.
Sources/WorkspacePresentationModeChangeObserver.swift New zero-size background view that owns the single @AppStorage subscription for the heavy view tree; fires both onAppear and onChange to keep the runtime cache and AppKit chrome side-effects up-to-date.
Sources/WorkspaceContentView.swift Removes @AppStorage subscription and isMinimalMode computed property; delegates safe-area edge selection to WorkspaceContentMinimalModeSafeAreaModifier.
Sources/MinimalModeSidebarTitlebarControlsOverlay.swift New narrow leaf view with its own @AppStorage subscription; only this view re-renders on mode change, keeping VerticalTabsSidebar body clean.
cmuxTests/WorkspaceContentViewVisibilityTests.swift Migrated from XCTest to Swift Testing; adds regression test that mounts a real ContentView window and asserts zero body re-evaluations after a mode toggle via an isolated UserDefaults suite.
cmux.xcodeproj/project.pbxproj Adds all nine new Swift source files with correct PBXBuildFile and PBXFileReference entries.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    UD[("UserDefaults\n(modeKey)")]
    subgraph HeavyViews["Heavy Views - no AppStorage subscription"]
        CV["ContentView\n(@State cache)"]
        WCV["WorkspaceContentView"]
        VTS["VerticalTabsSidebar"]
    end
    subgraph ChromeLeaves["Chrome Leaf Views - own @AppStorage subscription"]
        OBS["WorkspacePresentationModeChangeObserver"]
        TML["WorkspaceTitlebarModeLayer"]
        ESSL["MinimalModeTitlebarEventSurfaceLayer"]
        SAM["WorkspaceContentMinimalModeSafeAreaModifier"]
        TPM["WorkspacePresentationModeContentTopPaddingModifier"]
        OVL["MinimalModeSidebarTitlebarControlsOverlay"]
    end
    CACHE["WorkspacePresentationModeRuntimeCache\n(non-observable class)"]
    UD -->|"@AppStorage"| OBS
    UD -->|"@AppStorage"| TML
    UD -->|"@AppStorage"| ESSL
    UD -->|"@AppStorage"| SAM
    UD -->|"@AppStorage"| TPM
    UD -->|"@AppStorage"| OVL
    OBS -->|"handleWorkspacePresentationModeChange"| CV
    CV -->|"writes"| CACHE
    CV -->|"reads"| CACHE
    CV -->|"contains"| WCV
    CV -->|"contains"| VTS
    CV -->|"embeds"| OBS
    WCV -->|"modifier"| SAM
    CV -->|"modifier"| TPM
    CV -->|"wraps"| TML
    CV -->|"embeds"| ESSL
    VTS -->|"overlay"| OVL
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    UD[("UserDefaults\n(modeKey)")]
    subgraph HeavyViews["Heavy Views - no AppStorage subscription"]
        CV["ContentView\n(@State cache)"]
        WCV["WorkspaceContentView"]
        VTS["VerticalTabsSidebar"]
    end
    subgraph ChromeLeaves["Chrome Leaf Views - own @AppStorage subscription"]
        OBS["WorkspacePresentationModeChangeObserver"]
        TML["WorkspaceTitlebarModeLayer"]
        ESSL["MinimalModeTitlebarEventSurfaceLayer"]
        SAM["WorkspaceContentMinimalModeSafeAreaModifier"]
        TPM["WorkspacePresentationModeContentTopPaddingModifier"]
        OVL["MinimalModeSidebarTitlebarControlsOverlay"]
    end
    CACHE["WorkspacePresentationModeRuntimeCache\n(non-observable class)"]
    UD -->|"@AppStorage"| OBS
    UD -->|"@AppStorage"| TML
    UD -->|"@AppStorage"| ESSL
    UD -->|"@AppStorage"| SAM
    UD -->|"@AppStorage"| TPM
    UD -->|"@AppStorage"| OVL
    OBS -->|"handleWorkspacePresentationModeChange"| CV
    CV -->|"writes"| CACHE
    CV -->|"reads"| CACHE
    CV -->|"contains"| WCV
    CV -->|"contains"| VTS
    CV -->|"embeds"| OBS
    WCV -->|"modifier"| SAM
    CV -->|"modifier"| TPM
    CV -->|"wraps"| TML
    CV -->|"embeds"| ESSL
    VTS -->|"overlay"| OVL
Loading

Reviews (8): Last reviewed commit: "Keep minimal mode side effects on observ..." | Re-trigger Greptile

Comment thread Sources/Debug/MinimalModeInvalidationProbe.swift
Comment thread Sources/WorkspacePresentationModeChangeObserver.swift
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a UI hang on minimal-mode toggle by moving @AppStorage(WorkspacePresentationModeSettings.modeKey) subscriptions out of the heavy root views (ContentView, WorkspaceContentView, VerticalTabsSidebar) into six narrow chrome-specific leaf views/modifiers, so only the titlebar and safe-area chrome re-evaluates when the mode changes.

  • Six new leaf files (WorkspaceTitlebarModeLayer, MinimalModeSidebarTitlebarControlsOverlay, MinimalModeTitlebarEventSurfaceLayer, WorkspaceContentMinimalModeSafeAreaModifier, WorkspacePresentationModeChangeObserver, WorkspacePresentationModeContentTopPaddingModifier) each own their own @AppStorage subscription, isolating SwiftUI invalidation to the smallest possible subtree.
  • A regression test mounts the real ContentView in an NSWindow, toggles the mode via UserDefaults, pumps the run loop, and asserts the three heavy body computed properties are not re-entered — but the test observability hook (MinimalModeInvalidationProbe) is placed in Sources/ under #if DEBUG, which violates the repo's no-test-debug-seam-in-production-source rule.

Confidence Score: 3/5

The core invalidation fix is correct and the leaf-view decomposition is clean, but MinimalModeInvalidationProbe lands in production Sources/ as a #if DEBUG-only test hook with no shipped behaviour.

The narrowing of @AppStorage subscriptions to leaf views is architecturally sound and the regression test validates the contract. The problem is Sources/MinimalModeInvalidationProbe.swift: it is entirely compiled away in release builds but adds a debug-only EnvironmentKey and three matching #if DEBUG body hooks to ContentView, WorkspaceContentView, and VerticalTabsSidebar with no production caller. Per the repo's explicit rule this class of observability seam belongs in a debug folder or test-support module, not in Sources/.

Sources/MinimalModeInvalidationProbe.swift and the three #if DEBUG probe blocks inside ContentView.body, WorkspaceContentView.body, and VerticalTabsSidebar.body should be relocated before merge.

Important Files Changed

Filename Overview
Sources/MinimalModeInvalidationProbe.swift New file entirely under #if DEBUG with no production caller; adds a test-only EnvironmentKey seam to production Sources in violation of the no-test-debug-seam rule.
Sources/ContentView.swift Removes @AppStorage(modeKey) from ContentView and VerticalTabsSidebar bodies; adds #if DEBUG probe calls in body (mirrors the test-seam issue), replaces onChange handler with WorkspacePresentationModeChangeObserver leaf, and delegates mode-gated chrome to narrow leaf views.
Sources/WorkspaceContentView.swift Removes @AppStorage(modeKey) subscription; delegates safe-area ignoring to WorkspaceContentMinimalModeSafeAreaModifier; adds #if DEBUG probe call in body (test-seam, flagged separately).
Sources/WorkspacePresentationModeChangeObserver.swift New zero-size leaf view that owns the @AppStorage subscription and fires an onChange callback to ContentView; correctly isolates invalidation to a narrow view.
Sources/WorkspaceTitlebarModeLayer.swift New leaf view wrapping titlebar chrome with its own @AppStorage subscription so only the titlebar re-evaluates on mode toggle.
Sources/MinimalModeSidebarTitlebarControlsOverlay.swift New leaf view owning @AppStorage for the sidebar titlebar overlay; correctly narrows invalidation scope away from VerticalTabsSidebar.
Sources/MinimalModeTitlebarEventSurfaceLayer.swift New leaf view narrowing @AppStorage subscription for the event surface layer; clean.
Sources/WorkspaceContentMinimalModeSafeAreaModifier.swift New ViewModifier owning its own @AppStorage subscription to handle safe-area edge selection; correctly isolated.
Sources/WorkspacePresentationModeContentTopPaddingModifier.swift New ViewModifier owning @AppStorage for top-padding computation; clean narrow-scope subscriber.
cmuxTests/WorkspaceContentViewVisibilityTests.swift New regression test that mounts the real ContentView and verifies minimal-mode toggle doesn't re-evaluate heavy bodies; uses drainMainRunLoop with fixed 1 ms iterations (deterministic test scaffolding, rule-exempt).
cmux.xcodeproj/project.pbxproj Wires all seven new Swift files into the app target and test target; indentation diverges in some list sections but content is correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    UD["UserDefaults modeKey change"]
    OBS["WorkspacePresentationModeChangeObserver @AppStorage subscriber"]
    TML["WorkspaceTitlebarModeLayer @AppStorage subscriber"]
    STCO["MinimalModeSidebarTitlebarControlsOverlay @AppStorage subscriber"]
    TESL["MinimalModeTitlebarEventSurfaceLayer @AppStorage subscriber"]
    MSAM["WorkspaceContentMinimalModeSafeAreaModifier @AppStorage subscriber"]
    CTPM["WorkspacePresentationModeContentTopPaddingModifier @AppStorage subscriber"]
    CV["ContentView.handleWorkspacePresentationModeChange imperative NSWindow chrome updates"]
    TB["workspaceTitlebarBand re-evaluates"]
    SB["HiddenTitlebarSidebarControlsView re-evaluates"]
    ES["MinimalModeTitlebarEventSurfaceView re-evaluates"]
    WCV["WorkspaceContentView safe-area edge"]
    PAD["ContentView top padding"]
    UD --> OBS
    UD --> TML
    UD --> STCO
    UD --> TESL
    UD --> MSAM
    UD --> CTPM
    OBS -->|onChange callback| CV
    TML -->|titlebar visibility only| TB
    STCO -->|overlay only| SB
    TESL -->|event surface only| ES
    MSAM -->|safe-area edges only| WCV
    CTPM -->|top padding only| PAD
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    UD["UserDefaults modeKey change"]
    OBS["WorkspacePresentationModeChangeObserver @AppStorage subscriber"]
    TML["WorkspaceTitlebarModeLayer @AppStorage subscriber"]
    STCO["MinimalModeSidebarTitlebarControlsOverlay @AppStorage subscriber"]
    TESL["MinimalModeTitlebarEventSurfaceLayer @AppStorage subscriber"]
    MSAM["WorkspaceContentMinimalModeSafeAreaModifier @AppStorage subscriber"]
    CTPM["WorkspacePresentationModeContentTopPaddingModifier @AppStorage subscriber"]
    CV["ContentView.handleWorkspacePresentationModeChange imperative NSWindow chrome updates"]
    TB["workspaceTitlebarBand re-evaluates"]
    SB["HiddenTitlebarSidebarControlsView re-evaluates"]
    ES["MinimalModeTitlebarEventSurfaceView re-evaluates"]
    WCV["WorkspaceContentView safe-area edge"]
    PAD["ContentView top padding"]
    UD --> OBS
    UD --> TML
    UD --> STCO
    UD --> TESL
    UD --> MSAM
    UD --> CTPM
    OBS -->|onChange callback| CV
    TML -->|titlebar visibility only| TB
    STCO -->|overlay only| SB
    TESL -->|event surface only| ES
    MSAM -->|safe-area edges only| WCV
    CTPM -->|top padding only| PAD
Loading

Reviews (2): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread Sources/Debug/MinimalModeInvalidationProbe.swift
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the minimal-mode toggle hang (#5732) by moving @AppStorage(WorkspacePresentationModeSettings.modeKey) subscriptions out of the three heavy views (ContentView, WorkspaceContentView, VerticalTabsSidebar) and into five narrow leaf views/modifiers, so a mode change only invalidates lightweight chrome rather than re-rendering the entire workspace tree.

  • Leaf-view isolation: Five new types (WorkspaceTitlebarModeLayer, MinimalModeTitlebarEventSurfaceLayer, WorkspaceContentMinimalModeSafeAreaModifier, WorkspacePresentationModeContentTopPaddingModifier, MinimalModeSidebarTitlebarControlsOverlay) each own their own @AppStorage subscription; the heavy views lose theirs entirely.
  • Side-effect dispatch: WorkspacePresentationModeChangeObserver (zero-frame background view) replaces the removed onChange(of: isMinimalMode) in ContentView and calls handleWorkspacePresentationModeChange for window-chrome imperatives (titlebar controls, shadow, traffic-light inset).
  • Regression test: testMinimalModeToggleDoesNotReevaluateChromeHeavyBodies mounts the real ContentView in an NSWindow, toggles UserDefaults, and asserts zero body re-evaluations on the three guarded views — but the probe itself (MinimalModeInvalidationProbe.swift) lands in production Sources/ as a #if DEBUG test-only seam, violating the repo's no-test-seam-in-production-source rule.

Confidence Score: 4/5

The core isolation refactor is correct and the new leaf views are well-scoped; the one concrete issue is a test-only #if DEBUG hook shipped inside production Sources/ that should be removed before merging.

The architectural change is sound — isolating @AppStorage subscriptions to leaf views is the right fix for the re-render hang, and WorkspacePresentationModeChangeObserver cleanly replaces the removed onChange block. The only defect is MinimalModeInvalidationProbe.swift in Sources/: the entire file is #if DEBUG-guarded, has no production caller, and the three paired injection sites in ContentView.swift and WorkspaceContentView.swift are purely for counting test body evaluations. This is precisely the pattern the repo prohibits in production source, and removing it would not affect the correctness of the fix itself.

Sources/MinimalModeInvalidationProbe.swift and its three #if DEBUG companion sites in Sources/ContentView.swift and Sources/WorkspaceContentView.swift.

Important Files Changed

Filename Overview
Sources/MinimalModeInvalidationProbe.swift Entirely #if DEBUG-guarded test hook with no production caller; violates the no-test-seam-in-production-source rule and must move to the test target.
Sources/ContentView.swift Removes @AppStorage from the heavy ContentView body; replaces it with currentIsMinimalMode (direct UserDefaults read) for imperative callsites and delegates reactive observation to leaf views/WorkspacePresentationModeChangeObserver. Contains #if DEBUG probe injection sites that should be removed alongside MinimalModeInvalidationProbe.swift.
Sources/WorkspaceContentView.swift Removes @AppStorage subscription; delegates safe-area edge logic to WorkspaceContentMinimalModeSafeAreaModifier. Contains #if DEBUG probe injection that ties to the production-source seam.
Sources/WorkspacePresentationModeChangeObserver.swift Zero-frame Color.clear background view that owns the sole @AppStorage subscription for mode changes in ContentView's subtree and fires the imperative side-effects callback. Clean pattern.
Sources/WorkspaceTitlebarModeLayer.swift Narrow leaf view that gates titlebar rendering behind its own @AppStorage subscription, isolating invalidation correctly.
Sources/MinimalModeSidebarTitlebarControlsOverlay.swift Thin overlay view that owns its own @AppStorage subscription and only re-renders the sidebar titlebar controls on mode change.
Sources/MinimalModeTitlebarEventSurfaceLayer.swift Thin leaf view wrapping MinimalModeTitlebarEventSurfaceView with its own isolated @AppStorage subscription.
Sources/WorkspaceContentMinimalModeSafeAreaModifier.swift Small ViewModifier that moves the ignoresSafeArea logic into its own @AppStorage-subscribed type, correctly isolating the safe-area edge re-render.
Sources/WorkspacePresentationModeContentTopPaddingModifier.swift Isolates top-padding computation behind its own @AppStorage subscription; delegates calculation to the existing ContentView.effectiveTitlebarPadding static helper.
cmuxTests/WorkspaceContentViewVisibilityTests.swift Adds a regression test that mounts ContentView in a real NSWindow and asserts zero body re-evaluations on minimal-mode toggle; uses 20-iteration RunLoop drain which is acceptable test scaffolding.
cmux.xcodeproj/project.pbxproj Registers the seven new Swift source files in both the file references and build phases; mixed indentation in the new group entries but structurally valid.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User/PaletteCommand
    participant UD as UserDefaults
    participant OBS as WorkspacePresentationModeChangeObserver
    participant CV as ContentView
    participant TML as WorkspaceTitlebarModeLayer
    participant ESL as MinimalModeTitlebarEventSurfaceLayer
    participant SAM as WorkspaceContentMinimalModeSafeAreaModifier
    participant PPM as WorkspacePresentationModeContentTopPaddingModifier
    participant STO as MinimalModeSidebarTitlebarControlsOverlay

    U->>UD: set(modeKey, "minimal")
    UD-->>OBS: "@AppStorage fires onChange"
    OBS->>CV: handleWorkspacePresentationModeChange(isMinimalMode: true)
    CV->>CV: setTitlebarControlsHidden / applyWindowDecorations / syncTrafficLightInset

    UD-->>TML: "@AppStorage fires → body re-evaluates (hides titlebar band)"
    UD-->>ESL: "@AppStorage fires → body re-evaluates (enables event surface)"
    UD-->>SAM: "@AppStorage fires → body re-evaluates (ignoresSafeArea edges)"
    UD-->>PPM: "@AppStorage fires → body re-evaluates (top padding)"
    UD-->>STO: "@AppStorage fires → body re-evaluates (shows sidebar controls)"

    Note over CV: ContentView body NOT re-evaluated
    Note over CV: WorkspaceContentView body NOT re-evaluated
    Note over CV: VerticalTabsSidebar body NOT re-evaluated
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User/PaletteCommand
    participant UD as UserDefaults
    participant OBS as WorkspacePresentationModeChangeObserver
    participant CV as ContentView
    participant TML as WorkspaceTitlebarModeLayer
    participant ESL as MinimalModeTitlebarEventSurfaceLayer
    participant SAM as WorkspaceContentMinimalModeSafeAreaModifier
    participant PPM as WorkspacePresentationModeContentTopPaddingModifier
    participant STO as MinimalModeSidebarTitlebarControlsOverlay

    U->>UD: set(modeKey, "minimal")
    UD-->>OBS: "@AppStorage fires onChange"
    OBS->>CV: handleWorkspacePresentationModeChange(isMinimalMode: true)
    CV->>CV: setTitlebarControlsHidden / applyWindowDecorations / syncTrafficLightInset

    UD-->>TML: "@AppStorage fires → body re-evaluates (hides titlebar band)"
    UD-->>ESL: "@AppStorage fires → body re-evaluates (enables event surface)"
    UD-->>SAM: "@AppStorage fires → body re-evaluates (ignoresSafeArea edges)"
    UD-->>PPM: "@AppStorage fires → body re-evaluates (top padding)"
    UD-->>STO: "@AppStorage fires → body re-evaluates (shows sidebar controls)"

    Note over CV: ContentView body NOT re-evaluated
    Note over CV: WorkspaceContentView body NOT re-evaluated
    Note over CV: VerticalTabsSidebar body NOT re-evaluated
Loading

Reviews (3): Last reviewed commit: "Fix minimal mode notification anchor cal..." | Re-trigger Greptile

Comment thread Sources/Debug/MinimalModeInvalidationProbe.swift
@greptile-apps

greptile-apps Bot commented Jun 30, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the hang when toggling Minimal Mode by moving @AppStorage(workspacePresentationMode) subscriptions out of the three heavy views (ContentView, WorkspaceContentView, VerticalTabsSidebar) and into five narrow, single-purpose leaf views/modifiers, so only chrome-specific subtrees re-evaluate on a mode toggle. A regression test asserts zero body re-evaluations on those heavy views after a UserDefaults mode change.

  • Five new leaf views/modifiers each own exactly one @AppStorage subscription and one piece of chrome behavior (titlebar visibility, event surface, safe-area insets, top padding, sidebar controls).
  • WorkspacePresentationModeChangeObserver is a zero-size background view that fires imperative side effects via onChange, replacing the removed onChange(of: isMinimalMode) chain on ContentView.
  • MinimalModeInvalidationProbe is added entirely under #if DEBUG in Sources/ to let the test count body evaluations — this placement violates the project's no-test-debug-seam-in-production-source rule.

Confidence Score: 4/5

The invalidation fix is sound and the regression test covers the core behavioral contract; relocating MinimalModeInvalidationProbe out of the production source tree should be done before merging.

The five new leaf views correctly scope @AppStorage subscriptions to narrow chrome subtrees, and the imperative side-effect handler preserves the old onChange behavior without reintroducing broad invalidation. The probe mechanism in MinimalModeInvalidationProbe.swift — entirely #if DEBUG, no production caller, placed in Sources/ — breaks the project rule against test-only seams in production source; the same pattern repeats in ContentView and WorkspaceContentView bodies.

Sources/MinimalModeInvalidationProbe.swift and the #if DEBUG probe blocks in Sources/ContentView.swift and Sources/WorkspaceContentView.swift.

Important Files Changed

Filename Overview
Sources/MinimalModeInvalidationProbe.swift New file entirely wrapped in #if DEBUG; placed in Sources/ with no production caller — violates the no-test-debug-seam-in-production-source rule.
Sources/ContentView.swift Removes @AppStorage from ContentView and VerticalTabsSidebar; adds #if DEBUG probe hooks in both bodies; delegates side effects to new handleWorkspacePresentationModeChange; uses currentIsMinimalMode (direct UserDefaults read) in imperative paths.
Sources/WorkspaceContentView.swift Removes @AppStorage subscription and isMinimalMode; delegates safe-area handling to WorkspaceContentMinimalModeSafeAreaModifier; adds #if DEBUG probe hook in body.
Sources/WorkspacePresentationModeChangeObserver.swift Zero-size background view that scopes @AppStorage subscription and fires an imperative handler via onChange; clean SwiftUI pattern for decoupling side effects.
Sources/WorkspaceTitlebarModeLayer.swift New narrow wrapper view that owns the @AppStorage subscription for titlebar visibility; clean implementation.
cmuxTests/WorkspaceContentViewVisibilityTests.swift Adds regression test verifying zero body re-evaluations on ContentView, WorkspaceContentView, and VerticalTabsSidebar during a minimal-mode toggle; drainMainRunLoop uses a short timed runloop drain acceptable as test scaffolding.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    UD["UserDefaults workspacePresentationMode"]
    UD -->|"@AppStorage"| WTL["WorkspaceTitlebarModeLayer"]
    UD -->|"@AppStorage"| MTESL["MinimalModeTitlebarEventSurfaceLayer"]
    UD -->|"@AppStorage"| WCMSAM["WorkspaceContentMinimalModeSafeAreaModifier"]
    UD -->|"@AppStorage"| WPMCTPM["WorkspacePresentationModeContentTopPaddingModifier"]
    UD -->|"@AppStorage"| MMSTCO["MinimalModeSidebarTitlebarControlsOverlay"]
    UD -->|"@AppStorage"| WPMCO["WorkspacePresentationModeChangeObserver"]
    WPMCO --> CVH["ContentView.handleWorkspacePresentationModeChange"]
    subgraph Heavy["Heavy views - no longer subscribe"]
        CV["ContentView.body"]
        WCV["WorkspaceContentView.body"]
        VTS["VerticalTabsSidebar.body"]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    UD["UserDefaults workspacePresentationMode"]
    UD -->|"@AppStorage"| WTL["WorkspaceTitlebarModeLayer"]
    UD -->|"@AppStorage"| MTESL["MinimalModeTitlebarEventSurfaceLayer"]
    UD -->|"@AppStorage"| WCMSAM["WorkspaceContentMinimalModeSafeAreaModifier"]
    UD -->|"@AppStorage"| WPMCTPM["WorkspacePresentationModeContentTopPaddingModifier"]
    UD -->|"@AppStorage"| MMSTCO["MinimalModeSidebarTitlebarControlsOverlay"]
    UD -->|"@AppStorage"| WPMCO["WorkspacePresentationModeChangeObserver"]
    WPMCO --> CVH["ContentView.handleWorkspacePresentationModeChange"]
    subgraph Heavy["Heavy views - no longer subscribe"]
        CV["ContentView.body"]
        WCV["WorkspaceContentView.body"]
        VTS["VerticalTabsSidebar.body"]
    end
Loading

Comments Outside Diff (1)

  1. Sources/MinimalModeInvalidationProbe.swift, line 1-20 (link)

    P1 Test-only seam in production Sources

    MinimalModeInvalidationProbe.swift is placed in Sources/ but is entirely wrapped in #if DEBUG and has no production caller — it exists solely to let tests count body re-evaluations. The same pattern appears in ContentView.swift (ContentView.body and VerticalTabsSidebar.body) and WorkspaceContentView.swift, where #if DEBUG adds @Environment(\.minimalModeInvalidationProbe) reads and let _ = { probe?() }() body hooks with no production effect.

    The custom rule explicitly states: "The compiled-out #if DEBUG guard does not make a test-observability accessor acceptable in shipping source." The preferred fix is to move MinimalModeInvalidationProbe.swift (and the EnvironmentValues extension) to a dedicated debug-support folder or the test target. Because both the views and the test need the same key type, an alternative is to define a minimal protocol in a DebugSupport/ folder rather than in the production source tree.

    Rule Used: Flag Swift files under a production Sources path (... (source)

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (4): Last reviewed commit: "Address minimal mode review feedback" | Re-trigger Greptile

@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.

Actionable comments posted: 2

Caution

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

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

142-160: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove the DEBUG invalidation probe from this production view.

This adds a test-only hook to Sources/WorkspaceContentView.swift and executes it from body, which turns the render path into instrumentation surface. Please keep the regression probe in test-only scaffolding instead of shipping it through the app view.

As per coding guidelines, “Do not add test-only or debug-only seams in production Swift source files” and “Do not mutate state from body or helpers called by body.” As per path instructions, Sources/** must apply .github/review-bot-rules/no-test-debug-seam-in-production-source.md.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/WorkspaceContentView.swift` around lines 142 - 160, Remove the
DEBUG-only invalidation probe from WorkspaceContentView’s production body so the
render path is not instrumented. Keep minimalModeInvalidationProbe and the
workspaceContentBody callback out of Sources/WorkspaceContentView.swift, and
move any regression probing into test-only scaffolding instead. Update
panelVisibleInUI/body so they remain pure view rendering code with no test/debug
seams or state mutation triggered from body.

Sources: Coding guidelines, Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/ContentView.swift`:
- Around line 971-973: Remove the DEBUG-only minimalMode invalidation probe seam
from ContentView and any other production view body paths that call it, since
these callbacks are test/debug-only and should not execute during body
evaluation. Update the relevant ContentView sections that reference
minimalModeInvalidationProbe so they no longer invoke arbitrary probe closures
from body, and replace the coverage with behavior-level tests or production-safe
instrumentation outside the view hierarchy.

In `@Sources/WorkspacePresentationModeChangeObserver.swift`:
- Around line 17-22: The initial Minimal Mode sync is currently only triggered
from the observer’s onAppear path, which can run before the window is registered
and leave the first chrome update skipped. Update
WorkspacePresentationModeChangeObserver and the downstream ContentView handling
so the initial onChange callback is fired from a window-ready signal (such as
window registration) rather than relying on appearance alone, ensuring the first
sync reaches the titlebar-controls/metrics/shadow logic even when observedWindow
is not yet available.

---

Outside diff comments:
In `@Sources/WorkspaceContentView.swift`:
- Around line 142-160: Remove the DEBUG-only invalidation probe from
WorkspaceContentView’s production body so the render path is not instrumented.
Keep minimalModeInvalidationProbe and the workspaceContentBody callback out of
Sources/WorkspaceContentView.swift, and move any regression probing into
test-only scaffolding instead. Update panelVisibleInUI/body so they remain pure
view rendering code with no test/debug seams or state mutation triggered from
body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e9fc7b61-8dd9-4d96-a8c2-131c55b24a68

📥 Commits

Reviewing files that changed from the base of the PR and between 2c25184 and 4f64c32.

📒 Files selected for processing (13)
  • Sources/ContentView.swift
  • Sources/Debug/EnvironmentValues+MinimalModeInvalidationProbe.swift
  • Sources/Debug/MinimalModeInvalidationProbe.swift
  • Sources/Debug/MinimalModeInvalidationProbeKey.swift
  • Sources/MinimalModeSidebarTitlebarControlsOverlay.swift
  • Sources/MinimalModeTitlebarEventSurfaceLayer.swift
  • Sources/WorkspaceContentMinimalModeSafeAreaModifier.swift
  • Sources/WorkspaceContentView.swift
  • Sources/WorkspacePresentationModeChangeObserver.swift
  • Sources/WorkspacePresentationModeContentTopPaddingModifier.swift
  • Sources/WorkspaceTitlebarModeLayer.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/WorkspaceContentViewVisibilityTests.swift

Comment thread Sources/ContentView.swift
Comment thread Sources/WorkspacePresentationModeChangeObserver.swift

This branch was successfully deployed

1 active deployment
Preview – cmux — 0dd40bc8 Deployed Jun 30, 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.

Minimal mode: toggling minimal mode hangs the main thread (large synchronous SwiftUI relayout)

1 participant