Skip to content

Bound overflowing confirmation dialog content - #8296

Merged
austinywang merged 11 commits into
mainfrom
issue-8295-close-dialog-scroll
Jul 17, 2026
Merged

austinywang merged 11 commits into
mainfrom
issue-8295-close-dialog-scroll

Conversation

@austinywang

@austinywang austinywang commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This fixes #8295.

Closes #8295

Regression coverage

  • the first commit expands the existing close-workspaces XCUITest to create 48 workspaces and require an internal scroll view while Close and Cancel remain hittable
  • AppKit behavior tests cover the screen-relative cap, automatic overflow fallback for unstructured text, compact-content preservation, duplicate-substring-safe trailing detail separation, and the Save Workspace Layout disclosure budget
  • the test-only XCUITest was attempted on its first-commit SHA, but hosted-runner infrastructure prevented every run from reaching the new assertions: Warp and Blacksmith could not activate the app, depot-macos-14 used an incompatible old Xcode, and depot-macos-latest's test process could not complete its socket ping even though app diagnostics reported the socket ready with PONG; these failures are not presented as red-regression evidence

Validation

  • CmuxAlertContentTests: 4/4 passed on final HEAD 018e3d1ca2 in Actions run 29544286373
  • WorkspaceActionSaveDialogAccessoryTests: passed in Actions run 29542004606
  • focused fixed-head XCUITest attempted on Warp, Blacksmith, and depot-macos-latest; each runner failed during app/test-harness startup before the alert assertions
  • xcrun swiftc -typecheck for the new alert types
  • parser validation for every touched Swift file
  • ./scripts/check-pbxproj.sh
  • ./scripts/lint-pbxproj-test-wiring.sh (534 test files)
  • python3 scripts/check-test-determinism.py
  • workspace package-group and Package.resolved policy checks
  • localization audit: 95 touched-file keys verified in both English and Japanese; no new user-facing strings
  • no changes to .github/swift-warning-budget.tsv; every new Swift file remains below 500 lines

Per the issue instructions, I did not run local xcodebuild tests or a local app build. The requested python3 scripts/swift_file_length_budget.py check could not run because that script and .github/swift-file-length-budget.tsv do not exist on current main; no budget file was created or modified.

Summary by CodeRabbit

  • New Features

    • Long alert details now render in bounded, scrollable areas, improving readability across confirmation dialogs.
    • Close-workspace, process-kill, and resume prompts now handle large detail text more gracefully.
    • Workspace save dialog disclosure sections adapt their height to available screen space.
  • Bug Fixes

    • Prevented alert text/accessory content from extending beyond the visible screen area.
    • Corrected localized resume-message placeholder ordering.
  • Tests

    • Added and expanded coverage for scrollable alert behavior, screen-height limits, and large close-workspaces UI scenarios.

@greptile-apps

greptile-apps Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces CmuxAlertContent and CmuxAlertScrollableDetailsView to bound overflowing confirmation-dialog text: compact alerts stay native, but user-sized detail lists are moved into a screen-relative (40%) scrollable accessory view. The shared policy is applied across workspace/tab/pane close dialogs, Task Manager PID lists, project/resume command approvals, browser-extension permission lists, and the Save Workspace Layout disclosure sections. The PR also fixes the format-argument order for the resume-command approval strings in both English and Japanese.

  • CmuxModalAlertPresentation is cleaned up by moving isCmuxMainWindow, cmuxMainWindowForModalPresentation, and runCmuxModalAlert from top-level free functions into NSWindow, NSApplication, and NSAlert extensions respectively, eliminating ambient global state.
  • Previous review findings (duplicate CmuxAlertScrollableDetailsView allocation in the summary-overflow branch, and range(of:) matching the first rather than last occurrence of details) are both addressed in the head commit.

Confidence Score: 5/5

Safe to merge; the alert-content bounding logic is well-contained AppKit work that gracefully degrades to native presentation for short text.

All changed paths are modal-alert presentation code on the main actor; the new measurement and scroll-view construction is synchronous AppKit layout with no persistence or concurrency side-effects. Previous review concerns (duplicate layout pass, wrong occurrence removal) are resolved. The one inconsistency is a single missed migration in the resume-command approval dialog — it continues to use runModal() directly as it did before this PR, so no regression is introduced.

Sources/TerminalController+ControlSurfaceContext4.swift — uses content.apply + runModal() directly while all other migrated callers now use runCmuxModal(content:).

Important Files Changed

Filename Overview
Sources/CmuxAlertContent.swift New struct encapsulating scrollable-detail policy; backward search and duplicate-allocation fixes from prior review threads are in place; logic is correct.
Sources/CmuxAlertScrollableDetailsView.swift New NSScrollView subclass that measures AppKit text height after sizeToFit() and caps viewport to 40% of screen; MainActor annotation is correct; accessibility identifier is a production label, not a test seam.
Sources/CmuxModalAlertPresentation.swift Refactors top-level free functions into NSWindow/NSApplication/NSAlert extensions; removes ambient global state; logic is unchanged.
Sources/TerminalController+ControlSurfaceContext4.swift Applies CmuxAlertContent to bound the resume-command text, but still calls alert.runModal() directly instead of alert.runCmuxModal(content:), bypassing app activation and sheet-preferred presentation.
Sources/Workspace.swift Resume-command dialog migrated to runCmuxModal(content:); BonsplitDelegate dialog now uses cmuxMainWindowForModalPresentation() with an explicit attachedSheet == nil guard before beginSheetModal.
Sources/CmuxConfigExecutor.swift Content applied before both sheet-modal and app-modal paths; sanitizeForDisplay(command) is the exact last argument used to build informativeText, so the backward range search is guaranteed to succeed.
Sources/TaskManagerWindowController.swift PID list now routed through CmuxAlertContent.scrollingAll; single-process path wraps in CmuxAlertContent(informativeText:); both migrated to runCmuxModal(content:).
Sources/WorkspaceActionSaveDialogAccessory.swift Disclosure section heights now capped per-section to 40%-of-screen / sectionCount; visibleFrame injected through init for testability.
Resources/Localizable.xcstrings Format-argument order swapped to put CWD first in both English and Japanese entries; matches updated call sites.
Sources/DockSplitStore+CloseConfirmation.swift Pane close dialog wired up to CmuxAlertContent with scrollable bullet list; migrated to runCmuxModal(content:).
Sources/TabManager.swift confirmClose and runCloseConfirmationAlert now accept and forward CmuxAlertContent; confirmCloseHandler still receives content.flattenedText (unchanged from original message string).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["alert.runCmuxModal(content:)"] --> B["content.apply(to: alert, presentingWindow:)"]
    B --> C{scrollableDetails?}
    C -- yes --> D["CmuxAlertScrollableDetailsView(flattenedText)"]
    D --> E{isContentHeightCapped?}
    E -- no --> F["alert.informativeText = flattenedText\n(native layout)"]
    E -- yes --> G{informativeText empty?}
    G -- yes --> H["accessoryView = flattenedMeasurement\n(scroll all)"]
    G -- no --> I["CmuxAlertScrollableDetailsView(summary)\ncheck 20% budget"]
    I --> J{summaryOverflows?}
    J -- yes --> H
    J -- no --> K["informativeText = summary\naccessoryView = scrollableDetails view"]
    C -- no --> L["CmuxAlertScrollableDetailsView(informativeText)"]
    L --> M{isContentHeightCapped?}
    M -- no --> F
    M -- yes --> N["informativeText = ''\naccessoryView = overflowView"]
    B --> O["resolve hostWindow\nactivate app"]
    O --> P{hostWindow & no attached sheet?}
    P -- yes --> Q["beginSheetModal"]
    P -- no --> R["runModal (app-modal)"]
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
    A["alert.runCmuxModal(content:)"] --> B["content.apply(to: alert, presentingWindow:)"]
    B --> C{scrollableDetails?}
    C -- yes --> D["CmuxAlertScrollableDetailsView(flattenedText)"]
    D --> E{isContentHeightCapped?}
    E -- no --> F["alert.informativeText = flattenedText\n(native layout)"]
    E -- yes --> G{informativeText empty?}
    G -- yes --> H["accessoryView = flattenedMeasurement\n(scroll all)"]
    G -- no --> I["CmuxAlertScrollableDetailsView(summary)\ncheck 20% budget"]
    I --> J{summaryOverflows?}
    J -- yes --> H
    J -- no --> K["informativeText = summary\naccessoryView = scrollableDetails view"]
    C -- no --> L["CmuxAlertScrollableDetailsView(informativeText)"]
    L --> M{isContentHeightCapped?}
    M -- no --> F
    M -- yes --> N["informativeText = ''\naccessoryView = overflowView"]
    B --> O["resolve hostWindow\nactivate app"]
    O --> P{hostWindow & no attached sheet?}
    P -- yes --> Q["beginSheetModal"]
    P -- no --> R["runModal (app-modal)"]
Loading

Reviews (7): Last reviewed commit: "Use shared alert window resolution" | Re-trigger Greptile

Comment thread Sources/CmuxAlertContent.swift
@cursor

cursor Bot commented Jul 16, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds shared structured alert content and bounded, selectable scrolling views. Confirmation, resume, process, workspace, and configuration dialogs now use screen-aware sizing, while close-workspace flows propagate formatted details and validate visible controls.

Changes

Alert content and sizing

Layer / File(s) Summary
Structured alert content and bounded details view
Sources/CmuxAlertContent.swift, Sources/CmuxAlertScrollableDetailsView.swift, cmuxTests/CmuxAlertContentTests.swift, cmux.xcodeproj/project.pbxproj
Separates fixed and scrollable text, caps details using visible-screen geometry, and tests structured, unstructured, repeated, and short-content layouts.
Workspace action disclosure sizing
Sources/WorkspaceActionSaveDialogAccessory.swift, Sources/AppDelegate+WorkspaceActionSave.swift, cmuxTests/WorkspaceActionSaveDialogAccessoryTests.swift
Passes screen geometry into disclosure layout and clamps section heights to a visible-frame-derived maximum.

Modal and confirmation integration

Layer / File(s) Summary
Modal presentation and migrated prompts
Sources/CmuxModalAlertPresentation.swift, Sources/CmuxConfigExecutor.swift, Sources/TaskManagerWindowController.swift, Sources/TerminalController+ControlSurfaceContext4.swift, Sources/Workspace.swift, Resources/Localizable.xcstrings, Sources/ContentView+SavedLayoutCommands.swift, Sources/SidebarWorkspaceGroupDialogs.swift, Sources/QuitConfirmationAlertPresenter.swift
Adds structured content to modal presentation and migrates configuration, process, resume, saved-layout, group, and close-tab alerts while preserving presentation paths.
Scrollable close confirmations
Sources/DockSplitStore+CloseConfirmation.swift, Sources/TabManager.swift, Sources/WorkspaceCloseTabsBatching.swift, cmuxUITests/CloseWorkspacesConfirmDialogUITests.swift
Propagates formatted tab, pane, and workspace details and verifies scrolling with visible confirmation controls.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CloseWorkflow
  participant TabManager
  participant NSAlert
  participant CmuxAlertContent
  participant CmuxAlertScrollableDetailsView
  CloseWorkflow->>TabManager: submit message and scrollableDetails
  TabManager->>NSAlert: run structured confirmation
  NSAlert->>CmuxAlertContent: apply using screen geometry
  CmuxAlertContent->>CmuxAlertScrollableDetailsView: create capped details view
  CmuxAlertScrollableDetailsView->>NSAlert: install scrollable accessory
Loading

Possibly related issues

  • manaflow-ai/cmux-dev-artifacts#4241 — Covers the added CmuxAlertContent implementation and tests.
  • manaflow-ai/cmux-dev-artifacts#4236 — Covers UI validation for scrollable close-workspaces details and visible controls.
  • Issue 8295 — Directly describes the bulk close-workspaces dialog overflow addressed here.

Possibly related PRs

Suggested reviewers: azooz2003-bit


Important

Pre-merge checks failed

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

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Package Boundaries ❌ Error The diff adds reusable alert-content/scroll-view logic under app-target Sources, although CmuxAppKitSupportUI is the existing package for AppKit support pieces. Extract CmuxAlertContent and CmuxAlertScrollableDetailsView into Packages/macOS/CmuxAppKitSupportUI (expose CmuxAlertContent first); keep only cmux-specific window selection in the app target.
Cmux Full Internationalization ❌ Error Touched catalog keys like surfaceResumeApproval.* and dialog.cmuxConfig.disclosure.* have only en/ja values, but the catalog supports 20 locales. Add translated values for every existing catalog locale (ar, bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant) for the changed keys.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (22 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and describes the main change: preventing overflowing confirmation dialogs.
Description check ✅ Passed It covers summary, regression coverage, and validation in detail, but omits the demo video, review trigger, and checklist sections.
Linked Issues check ✅ Passed The changes add capped internal scrolling for bulk close dialogs and keep the buttons visible, satisfying #8295.
Out of Scope Changes check ✅ Passed The broader alert refactor supports the same overflow fix across related dialogs, so it stays within the stated general scope.
Cmux Swift Actor Isolation ✅ Passed New alert content is a pure Sendable value type; AppKit work stays on @MainActor UI types, and no new background access or shared mutable Sendable state was introduced.
Cmux Swift Blocking Runtime ✅ Passed Touched production diff adds no semaphores, waits, sleeps, syncs, polls, or locks; changes are modal alert/content/layout refactors.
Cmux Browser Automation Off-Main ✅ Passed HEAD touches only alert/content UI files; no changes in the browser automation router or target files, so the off-main WebKit wait rule isn’t implicated.
Cmux Expensive Synchronous Load ✅ Passed Diff only refactors modal alerts and height caps; no new agent-history/JSONL sync load was added to an interactive path.
Cmux Cache Substitution Correctness ✅ Passed The PR only refactors modal alerts and UI sizing; it does not replace a fresh authoritative read with a stale or opportunistic cache in any persistence/history/undo/snapshot path.
Cmux No Hacky Sleeps ✅ Passed Diff only changes Swift app/tests; no TS/JS/shell/build/runtime files were touched, so the no-hacky-sleeps rule is not implicated.
Cmux Algorithmic Complexity ✅ Passed No new nested scans or per-target rescans on scalable collections; changes are one-pass alert/workspace text assembly and fixed-size UI measurements.
Cmux Swift Concurrency ✅ Passed Diff adds sync AppKit modal helpers; the only Task/DispatchQueue hops are UI/callback boundaries, with no new Combine or background async patterns.
Cmux Swift @Concurrent ✅ Passed Touched code adds only synchronous @MainActor/AppKit alert plumbing; no new @concurrent, nonisolated async, or off-actor heavy work was introduced.
Cmux Swiftpm Lockfiles ✅ Passed PR touches only source/test files; no Package.swift, Package.resolved, or package-reference edits, and no .gitignore ignores Package.resolved.
Cmux Swift Logging ✅ Passed No parent→HEAD added lines in touched files contain print/debugPrint/dump/NSLog/Logger or stdout/stderr/file logging; existing logs are unchanged.
Cmux User-Facing Error Privacy ✅ Passed Changed alerts only show product/user data (titles, commands, PIDs, config paths); no banned vendor, token, or raw upstream details were added.
Cmux Swiftui State Layout ✅ Passed Diff only rewires AppKit alert presentation and bridge views; no new SwiftUI state/layout patterns, GeometryReader changes, or render-time state writes were introduced.
Cmux Architecture Rethink ✅ Passed PASS: The PR centralizes alert content/presentation in CmuxAlertContent and NSAlert.runCmuxModal, adds no new runtime race workarounds, and keeps state ownership clear.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No new standalone NSWindow/NSPanel/NSWindowController/WindowGroup was added; changes only refactor alerts/accessories, and existing Task Manager window identifier stayed unchanged.
Cmux Source Artifacts ✅ Passed All changed paths are intentional Swift source/test files; none are logs, caches, build output, temp folders, or other source-control artifacts.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No new test/debug-only seam appears in the touched Sources diff; new alert APIs are exercised by production callers and add no #if DEBUG or *ForTesting exposure.
Cmux No Ambient Global State ✅ Passed PASS: the patch moves the modal-presenter helpers onto NSApplication/NSAlert and adds constructable types, with no new file-scope API or singleton state.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8295-close-dialog-scroll

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.

@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/TerminalController+ControlSurfaceContext4.swift (1)

134-149: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reorder localized string placeholders to match accessory view layout.

When binding.command is extracted into the scrollable accessory view by CmuxAlertContent, it is removed from the informativeText and rendered natively at the bottom of the NSAlert. Because the command is currently placed before the working directory in the localized string, extracting it inverts the reading order (the "Working directory" line will appear above the command).

Consider updating the localized strings and format arguments so the visual order matches the text when the accessory view is used.

  • Sources/TerminalController+ControlSurfaceContext4.swift#L134-L149: Reorder surfaceResumeApproval.proposal.message so the command placeholder is at the end.
  • Sources/Workspace.swift#L2491-L2505: Reorder surfaceResumeApproval.runPrompt.message so the command placeholder is at the end.
🤖 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/TerminalController`+ControlSurfaceContext4.swift around lines 134 -
149, Reorder the command and working-directory placeholders in the localized
message format and their arguments so the command appears last, matching
CmuxAlertContent’s separatingScrollableDetails layout. Apply this to
Sources/TerminalController+ControlSurfaceContext4.swift:134-149 for
surfaceResumeApproval.proposal.message and Sources/Workspace.swift:2491-2505 for
surfaceResumeApproval.runPrompt.message; preserve the existing displayed content
and localization keys.
🤖 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/Panels/BrowserWebExtensionSupport`+WKWebExtensionControllerDelegate.swift:
- Around line 394-397: Update confirmPermissionRequest so the nil
scrollableDetails fallback uses CmuxAlertContent(informativeText:
informativeText) instead of CmuxAlertContent.scrollingAll(informativeText),
while preserving the existing separated-details path.

In `@Sources/TaskManagerWindowController.swift`:
- Around line 245-256: Replace the String(format:) calls in the single-process
and multi-process kill-message branches with String.localizedStringWithFormat,
preserving the existing localized strings and arguments so process IDs and
counts use the user’s locale.

---

Outside diff comments:
In `@Sources/TerminalController`+ControlSurfaceContext4.swift:
- Around line 134-149: Reorder the command and working-directory placeholders in
the localized message format and their arguments so the command appears last,
matching CmuxAlertContent’s separatingScrollableDetails layout. Apply this to
Sources/TerminalController+ControlSurfaceContext4.swift:134-149 for
surfaceResumeApproval.proposal.message and Sources/Workspace.swift:2491-2505 for
surfaceResumeApproval.runPrompt.message; preserve the existing displayed content
and localization keys.
🪄 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: 3099699c-0e66-4f03-baca-13e0f2fce2e4

📥 Commits

Reviewing files that changed from the base of the PR and between 76b8ac8 and 77d6614.

📒 Files selected for processing (17)
  • Sources/AppDelegate+WorkspaceActionSave.swift
  • Sources/CmuxAlertContent.swift
  • Sources/CmuxAlertScrollableDetailsView.swift
  • Sources/CmuxConfigExecutor.swift
  • Sources/CmuxModalAlertPresentation.swift
  • Sources/DockSplitStore+CloseConfirmation.swift
  • Sources/Panels/BrowserWebExtensionSupport+WKWebExtensionControllerDelegate.swift
  • Sources/TabManager.swift
  • Sources/TaskManagerWindowController.swift
  • Sources/TerminalController+ControlSurfaceContext4.swift
  • Sources/Workspace.swift
  • Sources/WorkspaceActionSaveDialogAccessory.swift
  • Sources/WorkspaceCloseTabsBatching.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CmuxAlertContentTests.swift
  • cmuxTests/WorkspaceActionSaveDialogAccessoryTests.swift
  • cmuxUITests/CloseWorkspacesConfirmDialogUITests.swift

Comment thread Sources/Panels/BrowserWebExtensionSupport+WKWebExtensionControllerDelegate.swift Outdated
Comment thread Sources/TaskManagerWindowController.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

CodeRabbit outside-diff resume-command ordering finding: no code change. The overflow layout intentionally keeps fixed metadata—including the working directory—in the native informative text while the user-sized command moves into the accessory below it. Reordering the localization placeholders would also change the existing compact/native dialog order, even though compact content does not need an accessory. The overflow order is therefore an intentional consequence of keeping fixed context visible, not a lost or incorrect value.

@austinywang

Copy link
Copy Markdown
Contributor Author

Follow-up to the earlier CodeRabbit resume-order reply: after maintainer triage, addressed in 4108017. Both resume-prompt formats now place the localized working-directory line before the command placeholder, their format arguments match that order, and the English/Japanese catalog values were updated together. This preserves reading order when the command moves into the accessory view.

Comment thread Sources/CmuxAlertContent.swift Outdated
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@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 (3)
Sources/Workspace.swift (3)

11298-11307: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check for attachedSheet to prevent queuing sheets behind existing dialogs.

To maintain consistent behavior with runCmuxModalAlert, ensure the window does not already have an attached sheet before attempting beginSheetModal. Without this check, the close confirmation may queue invisibly behind an existing sheet.

💚 Proposed fix
         let content = CmuxAlertContent(informativeText: message)
         // Prefer a sheet if we can find a window, otherwise fall back to modal.
-        if let window = NSApp.keyWindow ?? NSApp.mainWindow {
+        if let window = NSApp.keyWindow ?? NSApp.mainWindow, window.attachedSheet == nil {
             content.apply(to: alert, presentingWindow: window)
             return await withCheckedContinuation { continuation in
                 alert.beginSheetModal(for: window) { response in
🤖 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/Workspace.swift` around lines 11298 - 11307, Update the sheet
presentation branch in the alert flow to check the selected window’s
attachedSheet before calling beginSheetModal. If a sheet is already attached,
skip the sheet path and allow the existing modal fallback behavior, matching
runCmuxModalAlert.

1871-1886: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace legacy DispatchQueue.main.asyncAfter with modern Swift concurrency.

As per path instructions, avoid introducing new DispatchQueue.main.asyncAfter or callback pyramids when Task and Task.sleep are appropriate. Since the timeout handler already hops into a Task { @mainactor ... }, you can remove the GCD wrapper and handle the delay directly within the Task.

♻️ Proposed refactor
         guard let timeout else { return }
-        DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { [weak self, registration] in
-            Task { `@MainActor` [weak self, registration] in
-                guard
-                    let self,
-                    self.hasPendingTerminalInputObserver(registration, forPanelId: panelId)
-                else {
-                    return
-                }
-
-                self.removePendingTerminalInputObserver(registration, forPanelId: panelId)
-                `#if` DEBUG
-                NSLog("[CmuxConfig] surface not ready after 3s, dropping command (%d chars)", text.count)
-                `#endif`
-            }
+        Task { `@MainActor` [weak self, registration] in
+            try? await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
+            guard
+                let self,
+                self.hasPendingTerminalInputObserver(registration, forPanelId: panelId)
+            else {
+                return
+            }
+
+            self.removePendingTerminalInputObserver(registration, forPanelId: panelId)
+            `#if` DEBUG
+            NSLog("[CmuxConfig] surface not ready after 3s, dropping command (%d chars)", text.count)
+            `#endif`
         }
🤖 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/Workspace.swift` around lines 1871 - 1886, Replace the
DispatchQueue.main.asyncAfter wrapper in the timeout handling with a Task-based
delay using Task.sleep, keeping the existing `@MainActor` task and observer
validation/removal logic in the timeout handler. Preserve weak self and
registration capture, and return cleanly if the sleep is cancelled or throws.

Source: Path instructions


42-44: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Provide a safety explanation for @unchecked Sendable or use @MainActor.

As per coding guidelines, do not mark shared mutable reference types as @unchecked Sendable without a clear safety explanation. If WorkspacePendingTerminalInputObserver is only mutated on the main thread, apply explicit @MainActor isolation instead to inherit Sendable safely without disabling compiler checks.

🤖 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/Workspace.swift` around lines 42 - 44, Update
WorkspacePendingTerminalInputObserver to use `@MainActor` isolation if all
accesses to its mutable observer property occur on the main thread, and adjust
callers as needed to preserve that isolation. Otherwise, retain `@unchecked`
Sendable only with a clear safety explanation documenting synchronization for
observer access.

Source: Coding guidelines

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

Outside diff comments:
In `@Sources/Workspace.swift`:
- Around line 11298-11307: Update the sheet presentation branch in the alert
flow to check the selected window’s attachedSheet before calling
beginSheetModal. If a sheet is already attached, skip the sheet path and allow
the existing modal fallback behavior, matching runCmuxModalAlert.
- Around line 1871-1886: Replace the DispatchQueue.main.asyncAfter wrapper in
the timeout handling with a Task-based delay using Task.sleep, keeping the
existing `@MainActor` task and observer validation/removal logic in the timeout
handler. Preserve weak self and registration capture, and return cleanly if the
sleep is cancelled or throws.
- Around line 42-44: Update WorkspacePendingTerminalInputObserver to use
`@MainActor` isolation if all accesses to its mutable observer property occur on
the main thread, and adjust callers as needed to preserve that isolation.
Otherwise, retain `@unchecked` Sendable only with a clear safety explanation
documenting synchronization for observer access.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0641372c-e367-4d68-b5cb-494981766b5d

📥 Commits

Reviewing files that changed from the base of the PR and between 018e3d1 and 33e9d74.

📒 Files selected for processing (4)
  • Resources/Localizable.xcstrings
  • Sources/DockSplitStore+CloseConfirmation.swift
  • Sources/TabManager.swift
  • Sources/Workspace.swift
💤 Files with no reviewable changes (1)
  • Sources/DockSplitStore+CloseConfirmation.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

Review follow-up for 0b3a8ab:

  • Fixed the valid sheet-queue finding: the async Workspace close prompt now falls back to app-modal presentation when the selected window already owns a sheet.
  • Removed the new file-scope modal APIs by moving window resolution onto NSApplication and presentation onto NSAlert, preserving one shared presentation path.
  • Reused the already measured view for scrollingAll overflow and added focused behavior coverage.
  • No change to the Workspace pending-input asyncAfter or unchecked-Sendable code: both are inherited unchanged from current origin/main and are outside this alert PR. Replacing the existing deadline with an uncancelled Task sleep would also broaden behavior beyond this fix.
  • No change to Save Layout environment-key disclosure: that user-visible behavior predates this PR; this diff only gives the existing disclosure a screen-relative height cap.

Parser/typecheck, project/test wiring, determinism, package policy, and budget checks pass. Focused CI and canonical autoreview are running on this commit.

@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/Workspace.swift (1)

11298-11311: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consolidate window resolution for modal presentation.

This PR introduces NSApp.cmuxMainWindowForModalPresentation() which provides robust resolution by filtering out auxiliary windows like settings panels. Consider using it here instead of the raw keyWindow ?? mainWindow fallback to align perfectly with the rest of the dialogs.

♻️ Proposed refactor
         let content = CmuxAlertContent(informativeText: message)
         // Prefer a sheet if we can find a window, otherwise fall back to modal.
-        if let window = NSApp.keyWindow ?? NSApp.mainWindow,
+        if let window = NSApp.cmuxMainWindowForModalPresentation(),
            window.attachedSheet == nil {
             content.apply(to: alert, presentingWindow: window)
             return await withCheckedContinuation { continuation in
🤖 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/Workspace.swift` around lines 11298 - 11311, Update the modal
presentation window lookup in the alert flow to use
NSApp.cmuxMainWindowForModalPresentation() instead of the raw keyWindow ??
mainWindow fallback. Preserve the existing sheet presentation when a suitable
window without an attached sheet is returned, and retain the modal fallback
otherwise.
🤖 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.

Outside diff comments:
In `@Sources/Workspace.swift`:
- Around line 11298-11311: Update the modal presentation window lookup in the
alert flow to use NSApp.cmuxMainWindowForModalPresentation() instead of the raw
keyWindow ?? mainWindow fallback. Preserve the existing sheet presentation when
a suitable window without an attached sheet is returned, and retain the modal
fallback otherwise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3fb9d57e-5ba2-4fd2-ae1f-e66e6acfd62f

📥 Commits

Reviewing files that changed from the base of the PR and between 33e9d74 and 0b3a8ab.

📒 Files selected for processing (10)
  • Sources/CmuxAlertContent.swift
  • Sources/CmuxModalAlertPresentation.swift
  • Sources/ContentView+SavedLayoutCommands.swift
  • Sources/DockSplitStore+CloseConfirmation.swift
  • Sources/QuitConfirmationAlertPresenter.swift
  • Sources/SidebarWorkspaceGroupDialogs.swift
  • Sources/TabManager.swift
  • Sources/TaskManagerWindowController.swift
  • Sources/Workspace.swift
  • cmuxTests/CmuxAlertContentTests.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

Follow-up review fix in 0d07477: the async Workspace close prompt now resolves its host through NSApp.cmuxMainWindowForModalPresentation(), then verifies attachedSheet is nil before beginning the sheet. This keeps auxiliary windows out of the path and shares the same host-selection policy as the rest of the alert presenter.

@austinywang
austinywang merged commit 82d7c99 into main Jul 17, 2026
6 checks passed
@austinywang

Copy link
Copy Markdown
Contributor Author

Final automated-review triage for the current HEAD:

  • Cmux Swift Package Boundaries: no code change. CmuxAlertContent and CmuxAlertScrollableDetailsView are internal and currently have only app-target consumers. Moving them into CmuxAppKitSupportUI would widen package API and add package dependencies without a second consumer or build seam; the canonical cmux architecture/Aziz review reports no policy finding.
  • Cmux Full Internationalization: no code change. This PR changes values for exactly two existing resume-approval keys and introduces no new user-facing string. Their English and Japanese entries were updated together, with matching placeholder counts and order, as required by the task and repo instructions. The cited dialog.cmuxConfig.disclosure.* keys are not changed by this diff.
  • Docstring Coverage: no repo-wide churn. The new shared modal-presentation APIs/types are documented; the reported 80% repository-wide threshold is unrelated to this focused change.
  • The two remaining Cursor bot items are service availability/billing notices, not review findings.

All inline review threads are resolved. The final canonical autoreview is clean (confidence 0.90), including the merge-conflict gate and cmux policy checks.

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.

Bulk close-workspaces confirmation dialog grows taller than the screen with many workspaces; needs internal scrolling

1 participant