Skip to content

Add settings sidebar shell - #3244

Merged
lawrencecchen merged 22 commits into
mainfrom
feat-settings-window-shell
Apr 29, 2026
Merged

lawrencecchen merged 22 commits into
mainfrom
feat-settings-window-shell

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Apr 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Wrap Settings in a native SwiftUI NavigationSplitView
  • Add a searchable table-of-contents sidebar for existing settings sections
  • Keep existing settings content intact for this minimal shell

Verification

  • ./scripts/reload.sh --tag setwin

Summary by cubic

Moves Settings to a SwiftUI WindowGroup with a native split‑view sidebar and smarter search. Adds programmatic navigation, a settings.json entry, and fixes an activation warning when opening the window.

  • New Features

    • Native NavigationSplitView sidebar with search: tokenized match, click‑only fade highlights, “No Results,” latest‑wins auto‑scroll; moved Workspace Colors below Shortcuts; wand icon for Automation.
    • Programmatic navigation (app + terminal), two‑way synced; rejects unknown targets; default 980×680; localized titles/prompts.
    • settings.json section to open the active cmux settings file; docs updated with #settings-json anchor; adds lightweight SettingsShellLab prototype with script/build_and_run.sh.
  • Refactors

    • Replaced SettingsWindowController with SettingsWindowPresenter in a SwiftUI WindowGroup; wired to SwiftUI openWindow; updated AppDelegate and terminal RPC; fixed Settings window activation warning and settings shortcut routing test cleanup.
    • Extracted navigation targets/helpers into SettingsNavigation for reuse.
    • Removed in‑content Settings title and old titlebar debug hooks; consolidated to “About Titlebar Debug”; removed traffic‑light offsets.

Written for commit 0e607d2. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Redesigned Settings: searchable sidebar split-view with selectable sections, icons/titles, and per-section detail panes; synchronized navigation and a Reset All action.
    • Titlebar debug tooling now targets the About panel only.
  • New Prototype

    • Added standalone "Settings Shell Lab" app to explore the new settings shell, with build/run script.
  • Bug Fixes

    • Stabilized window control (traffic-light) positioning.
  • Localization

    • Added localized strings for debug/about, settings search, notifications, and appearance.

@vercel

vercel Bot commented Apr 28, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 29, 2026 6:47am
cmux-staging Building Building Preview, Comment Apr 29, 2026 6:47am

@coderabbitai

coderabbitai Bot commented Apr 28, 2026 •

Copy link
Copy Markdown

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

Refactors titlebar debug from a combined Settings/About implementation to About-only; replaces AppKit Settings window controller with a SwiftUI WindowGroup and searchable NavigationSplitView sidebar; adds Settings navigation/search models and prototype SettingsShellLab app; updates localization and several call-sites to use the new presenter/navigation APIs.

Changes

Cohort / File(s) Summary
About titlebar debug & renames
Sources/cmuxApp.swift, Sources/...AboutTitlebarDebugWindowController, Sources/...
Replaced Settings-oriented titlebar debug wiring with About-only: debug menu items and "Open All Debug Windows" now open AboutTitlebarDebugWindowController; auxiliary-window identifier changed to cmux.aboutTitlebarDebug; removed Settings-specific titlebar debug model/store/ui and renamed Settings* → About*.
Settings hosting & UI (SwiftUI)
Sources/...SettingsWindowPresenter*, Sources/...SettingsRootView, Sources/...SettingsView, Sources/...SettingsNavigationTarget, Sources/...SettingsNavigationRequest
Replaced AppKit-managed SettingsWindowController with a SwiftUI WindowGroup keyed by SettingsWindowPresenter.windowID; introduced SettingsNavigationTarget/SettingsNavigationRequest/search index and reimplemented settings UI as a NavigationSplitView with searchable sidebar and anchor-driven in-view navigation.
App delegates & call-sites
Sources/AppDelegate.swift, Sources/TerminalController.swift
Fallback settings presentation now uses SettingsWindowPresenter.show(navigationTarget:); v2SettingsOpen maps target to SettingsNavigationTarget(rawValue:) and reports unknown raw values; removed direct AppKit show/order calls in favor of presenter.
Window decorations tweak
Sources/WindowDecorationsController.swift
trafficLightOffset(for:) simplified to always return .zero (removes special-case offset for settings window).
SettingsShellLab prototype (new)
Prototypes/SettingsShellLab/Package.swift, .gitignore, environment.toml, script/build_and_run.sh, Sources/SettingsShellLab/...
Adds new Swift package/app SettingsShellLab with SettingsShellLabApp (AppDelegate), SettingsShellView (NavigationSplitView), SettingsDetailView, SettingsSection model, build/run script and env config; implements sidebar search, persisted selection, and detail controls.
Localization additions
Resources/Localizable.xcstrings
Adds debug/about titlebar localization keys (debug.menu.aboutTitlebarDebug, debug.aboutTitlebarDebug.title, debug.aboutTitlebarDebug.resetAbout) and new settings/search/notification strings (en/ja).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Sidebar as Sidebar List
    participant Detail as Detail View
    participant Notifier as NotificationCenter
    participant Presenter as SettingsWindowPresenter

    User->>Sidebar: Select section / use search
    Sidebar->>Detail: Bind selection -> render section
    User->>Notifier: Post SettingsNavigationRequest(target)
    Notifier->>Sidebar: Update selection / scroll to .id(...)
    Sidebar->>Detail: Render requested section
    User->>Detail: Click "Reset" / "Reset All"
    Detail->>Notifier: Post SettingsResetRequest
    Notifier->>Detail: Invoke reset handlers
    User->>Presenter: Open Settings (programmatic)
    Presenter->>Sidebar: Ensure WindowGroup shows selected target
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through sidebars, found a search-lit trail,
Rewired my burrow so anchors never fail,
I twitch my nose at debug lights in the About,
I nibble "Reset" carrots and hop the settings route,
A tiny applet now hums — cozy, clever, and hale.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR description covers the main changes (NavigationSplitView, searchable sidebar, programmatic navigation, SettingsWindowPresenter replacement) but lacks explicit testing methodology and demo video. Expand Testing section with specific verification steps beyond the reload.sh tag, and include a demo video showing the sidebar search and navigation functionality.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title "Add settings sidebar shell" clearly summarizes the main change: introducing a searchable sidebar interface for the Settings window using SwiftUI NavigationSplitView.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ 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 feat-settings-window-shell

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 and usage tips.

@greptile-apps

greptile-apps Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wraps the existing SettingsView in a NavigationSplitView, adding a searchable sidebar of settings sections. SettingsNavigationTarget gains CaseIterable/Identifiable conformance plus title and searchTokens properties; all section headers receive .id() anchors so the existing ScrollViewReader-based scroll navigation continues to work alongside the new sidebar highlight.

Confidence Score: 4/5

Safe to merge; findings are cosmetic layout edge-cases only.

Only P2 findings: the NSWindow minSize (820) is set 40 px below the SwiftUI frame minimum (860), allowing the window to be resized to a width that slightly compresses the NavigationSplitView. No logic errors, data loss, or security concerns.

Sources/cmuxApp.swift — the configureSettingsWindow minSize and the .frame(minWidth:) value should be aligned.

Important Files Changed

Filename Overview
Sources/cmuxApp.swift Wraps SettingsView in a NavigationSplitView with a searchable sidebar; adds CaseIterable/Identifiable to SettingsNavigationTarget with title/searchTokens; adds .id() anchors to all section headers; minor window size inconsistency between NSWindow.minSize (820) and SwiftUI frame(minWidth: 860).

Sequence Diagram

sequenceDiagram
    participant User
    participant SidebarList
    participant sidebarSelection as sidebarSelection Binding
    participant NotificationCenter
    participant SettingsRootView
    participant SettingsView

    User->>SidebarList: tap sidebar item
    SidebarList->>sidebarSelection: set(newValue)
    sidebarSelection->>SettingsRootView: selectedSidebarItem = newValue
    sidebarSelection->>NotificationCenter: post(cmux.settings.navigate)
    NotificationCenter->>SettingsView: onReceive → proxy.scrollTo(target)
    NotificationCenter->>SettingsRootView: onReceive → selectedSidebarItem = target (same value, no-op)

    Note over User,SettingsWindowController: External navigation (e.g. menu item)
    participant SettingsWindowController
    SettingsWindowController->>NotificationCenter: post(target) after 50ms delay
    NotificationCenter->>SettingsRootView: onReceive → selectedSidebarItem = target (sidebar highlights)
    NotificationCenter->>SettingsView: onReceive → proxy.scrollTo(target)
Loading

Reviews (1): Last reviewed commit: "Add settings sidebar shell" | Re-trigger Greptile

Comment thread Sources/cmuxApp.swift Outdated

private func configureSettingsWindow(_ window: NSWindow) {
window.identifier = NSUserInterfaceItemIdentifier("cmux.settings")
window.minSize = NSSize(width: 820, height: 540)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Window minSize smaller than SwiftUI frame minimum

window.minSize is set to 820 but the NavigationSplitView has .frame(minWidth: 860). The OS-enforced window resize limit (820) is 40 px narrower than the SwiftUI layout minimum, so dragging the window to 820–860 px will compress the split view beyond what it was designed for — the sidebar or detail content may clip or reflow unexpectedly.

Suggested change
window.minSize = NSSize(width: 820, height: 540)
window.minSize = NSSize(width: 860, height: 540)

Comment thread Sources/cmuxApp.swift Outdated
SettingsView()
}
.navigationSplitViewStyle(.balanced)
.frame(minWidth: 860, minHeight: 540)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 SwiftUI frame minimum narrower than sidebar column minimum

.frame(minWidth: 860) is the declared minimum for the whole NavigationSplitView. More critically, when the window is at 820 px (the NSWindow minimum set just below), the .frame(minWidth: 860) constraint is exceeded and SwiftUI may silently drop it — consider aligning both to the same value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the current code: Settings now uses one shared minimum size through SettingsWindowPresenter.minimumSize, the SwiftUI root frame, and NSWindow minSize/contentMinSize.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the current code: Settings now uses one shared minimum size through SettingsWindowPresenter.minimumSize, the SwiftUI root frame, and NSWindow minSize/contentMinSize.

— Claude Code

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/cmuxApp.swift`:
- Line 8506: The view uses two different minimum widths (the SwiftUI modifier
.frame(minWidth: 860, minHeight: 540) and the AppKit window minSize.width =
820), causing inconsistent constraints; replace the hard-coded literals by a
single shared constant (e.g., let kMinWindowWidth / minWindowSize) and use that
constant in both the .frame(...) call and the window.minSize.width assignment
(also mirror minHeight if needed) so both UI layers enforce the same minimum
width.
- Around line 8507-8509: When handling SettingsNavigationRequest notifications
in the onReceive closure, clear the sidebar filter by setting searchText = ""
before assigning selectedSidebarItem so the target row is not hidden; update the
closure that listens to SettingsNavigationRequest.notificationName to first
reset searchText (or otherwise ensure the filter is cleared) and then set
selectedSidebarItem = SettingsNavigationRequest.target(from: notification) ??
selectedSidebarItem.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c6b2af2-108e-404d-a5f3-51f7cb278be6

📥 Commits

Reviewing files that changed from the base of the PR and between 302382d and d2c9ca9.

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

Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/cmuxApp.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95d4ca1edd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/cmuxApp.swift Outdated
Comment on lines +8469 to +8474
@State private var searchText = ""

private var sidebarItems: [SettingsNavigationTarget] {
let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
guard !query.isEmpty else { return SettingsNavigationTarget.allCases }
return SettingsNavigationTarget.allCases.filter { item in

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear Settings sidebar filter on window reopen

searchText is stored as @State on SettingsRootView, but this window is reused (SettingsWindowController keeps a single hosting view alive), so closing and reopening Settings preserves the previous search query. In practice, reopening the window can show a partially filtered sidebar with missing sections, which looks like settings disappeared rather than a stale filter. Reset the query when the settings window is shown/closed, or store it in ephemeral view state tied to each presentation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the current code: SettingsRootView clears searchText on appear before replaying navigation, so reopened Settings does not keep a stale filter.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the current code: SettingsRootView clears searchText on appear before replaying navigation, so reopened Settings does not keep a stale filter.

— Claude Code

@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: 3

🧹 Nitpick comments (1)
Prototypes/SettingsShellLab/Sources/SettingsShellLab/App/SettingsShellLabApp.swift (1)

9-13: Consider enforcing a minimum window size (not just default size).

defaultSize only seeds initial geometry. Add content min-size constraints so the shell remains usable when resized very small.

💡 Suggested fix
         WindowGroup(String(localized: "app.window.title", defaultValue: "Settings Shell Lab")) {
-            SettingsShellView()
+            SettingsShellView()
+                .frame(minWidth: 760, minHeight: 520)
         }
         .defaultSize(width: 900, height: 620)
+        .windowResizability(.contentMinSize)
         .commands {
             SidebarCommands()
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@Prototypes/SettingsShellLab/Sources/SettingsShellLab/App/SettingsShellLabApp.swift`
around lines 9 - 13, The WindowGroup currently only sets a .defaultSize which
doesn't prevent the user from resizing the window too small; update the layout
by enforcing a minimum content size—apply a minWidth/minHeight constraint to
SettingsShellView (e.g., via a .frame(minWidth: ..., minHeight: ...)) or
otherwise ensure SettingsShellView's root view enforces minimum sizes so the
shell remains usable when resized; modify the WindowGroup/SettingsShellView
usage around the existing defaultSize call to include these min size
constraints.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsDetailView.swift`:
- Around line 41-45: The two interactive Button instances in SettingsDetailView
(the ones created with localized titles "detail.primaryAction" and
"detail.secondaryAction") have empty action closures and should not be
clickable; either remove them until implemented or make them non-interactive by
replacing with Text or adding .disabled(true) and/or a conditional that hides
them until wired. Locate the Button initializers in SettingsDetailView.swift and
replace the empty closures with a non-interactive placeholder (Text) or append
.disabled(true) / wrap in an if-guard that checks a ready-to-use flag so users
cannot click a no-op control.
- Around line 14-67: The Localizable.xcstrings file is missing nine localization
keys used by SettingsDetailView (strings referenced inline like
String(localized: "detail.section", ...), "detail.toggle", "detail.mode",
"detail.slider", "detail.primaryAction", "detail.secondaryAction") and the Mode
enum titles ("mode.system", "mode.compact", "mode.expanded"); add these keys to
Resources/Localizable.xcstrings with English and Japanese translations matching
the defaultValue text used in SettingsDetailView and Mode.title so the
String(localized: ...) lookups resolve at runtime.

In
`@Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsShellView.swift`:
- Around line 39-45: The Reset toolbar Button currently has an empty action;
either wire it to a concrete reset handler or disable it until implemented.
Implement by calling an existing or new reset method (e.g., add a
resetToDefaults() or resetSettings() on your view model/owner and invoke it
inside the Button action), or if you prefer to block interaction for now, add
.disabled(true) (or conditionally disable using a viewModel.canReset flag) to
the Button/Label so the visible control is not a no-op; update SettingsShellView
to call the chosen reset handler or apply .disabled accordingly.

---

Nitpick comments:
In
`@Prototypes/SettingsShellLab/Sources/SettingsShellLab/App/SettingsShellLabApp.swift`:
- Around line 9-13: The WindowGroup currently only sets a .defaultSize which
doesn't prevent the user from resizing the window too small; update the layout
by enforcing a minimum content size—apply a minWidth/minHeight constraint to
SettingsShellView (e.g., via a .frame(minWidth: ..., minHeight: ...)) or
otherwise ensure SettingsShellView's root view enforces minimum sizes so the
shell remains usable when resized; modify the WindowGroup/SettingsShellView
usage around the existing defaultSize call to include these min size
constraints.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ffc512cf-5317-4092-8533-868e0b82898a

📥 Commits

Reviewing files that changed from the base of the PR and between d2c9ca9 and 95d4ca1.

📒 Files selected for processing (8)
  • Prototypes/SettingsShellLab/.codex/environments/environment.toml
  • Prototypes/SettingsShellLab/.gitignore
  • Prototypes/SettingsShellLab/Package.swift
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/App/SettingsShellLabApp.swift
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/Models/SettingsSection.swift
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsDetailView.swift
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsShellView.swift
  • Prototypes/SettingsShellLab/script/build_and_run.sh
✅ Files skipped from review due to trivial changes (4)
  • Prototypes/SettingsShellLab/.gitignore
  • Prototypes/SettingsShellLab/Package.swift
  • Prototypes/SettingsShellLab/.codex/environments/environment.toml
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/Models/SettingsSection.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f28dc75a87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/cmuxApp.swift Outdated
Comment on lines +8391 to +8393
.onChange(of: selectedSectionRaw) { _, newValue in
guard let target = SettingsNavigationTarget(rawValue: newValue) else { return }
SettingsNavigationRequest.post(target)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Sync initial sidebar selection to settings scroll position

selectedSectionRaw is restored from @SceneStorage, but the detail view only receives navigation events via the onChange publisher. Because onChange does not fire for the initial restored value, opening Settings can show a sidebar selection (for example, "Keyboard Shortcuts") while the scroll content remains at the top section until another selection change occurs. Trigger the same navigation post on first appearance (or otherwise initialize the scroll target) so the highlighted sidebar item and visible section stay consistent.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the current code: SettingsRootView now replays the restored selected section on appear when there is no pending targeted navigation.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the current code: SettingsRootView now replays the restored selected section on appear when there is no pending targeted navigation.

— Claude Code

@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

♻️ Duplicate comments (2)
Sources/cmuxApp.swift (2)

8408-8410: ⚠️ Potential issue | 🟡 Minor

Clear sidebar search before applying incoming navigation target

If a navigation request targets a section filtered out by searchText, selection updates but the row remains hidden in the sidebar.

💡 Suggested fix
         .onReceive(NotificationCenter.default.publisher(for: SettingsNavigationRequest.notificationName)) { notification in
-            selectedSectionRaw = SettingsNavigationRequest.target(from: notification)?.rawValue ?? selectedSectionRaw
+            guard let target = SettingsNavigationRequest.target(from: notification) else { return }
+            if !searchText.isEmpty {
+                searchText = ""
+            }
+            selectedSectionRaw = target.rawValue
         }

Based on learnings: In Sources/cmuxApp.swift, clear sidebar search before handling navigation requests so target entries/anchors are guaranteed to exist and remain visible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 8408 - 8410, In the notification handler
for SettingsNavigationRequest within the onReceive modifier, clear the
searchText before updating selectedSectionRaw to ensure the target section is
not filtered out by an active search. Add a line to set searchText to an empty
string before the selectedSectionRaw assignment so that when the navigation
target is applied, the sidebar is not filtered and the selected section remains
visible to the user.

2464-2474: ⚠️ Potential issue | 🟠 Major

Enforce the settings window minimum size explicitly

Line 2464 sets only the initial size. There’s no minSize/contentMinSize clamp here, so users can resize below the intended settings layout floor.

💡 Suggested fix
     private init() {
+        let defaultSize = NSSize(width: 900, height: 620)
+        let minimumSize = NSSize(width: 820, height: 540)
         let window = NSWindow(
-            contentRect: NSRect(x: 0, y: 0, width: 900, height: 620),
+            contentRect: NSRect(origin: .zero, size: defaultSize),
             styleMask: [.titled, .closable, .miniaturizable, .resizable],
             backing: .buffered,
             defer: false
         )
+        window.minSize = minimumSize
+        window.contentMinSize = minimumSize
         window.isReleasedWhenClosed = false
         window.identifier = NSUserInterfaceItemIdentifier("cmux.settings")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 2464 - 2474, The settings window only
sets an initial size but doesn't enforce a minimum; update the initialization
around the NSWindow named window (before super.init(window: window)) to set
window.minSize and window.contentMinSize to the intended minimum NSSize (e.g.,
width: 900, height: 620) so SettingsRootView's layout cannot be resized below
the floor; ensure both properties are assigned on the same window instance
created there.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/cmuxApp.swift`:
- Around line 8356-8361: Normalize the stored SceneStorage value by validating
selectedSectionRaw on view load and replacing invalid values with a sane
default: check whether SettingsNavigationTarget(rawValue: selectedSectionRaw)
returns nil and if so assign selectedSectionRaw =
SettingsNavigationTarget.account.rawValue (or another chosen default). Do this
validation in the view's initializer or onAppear so selectedSection (which
derives from selectedSectionRaw) always yields a valid enum and the sidebar
shows a selected row; reference selectedSectionRaw, selectedSection, and
SettingsNavigationTarget when locating where to add the check.

In `@Sources/WindowDecorationsController.swift`:
- Around line 71-73: The trafficLightOffset(for:) implementation was changed to
always return .zero, disabling offsets globally; revert this to only return
.zero for the settings window by checking the window identifier (e.g., compare
window.identifier?.rawValue to "cmux.settings") and otherwise compute/return the
original offset value used previously so applyTrafficLightOffset(on:hidden:)
still applies offsets for non-settings windows; update the logic inside
trafficLightOffset(for:) (and ensure any helper used there is preserved) rather
than changing applyTrafficLightOffset(on:hidden:).

---

Duplicate comments:
In `@Sources/cmuxApp.swift`:
- Around line 8408-8410: In the notification handler for
SettingsNavigationRequest within the onReceive modifier, clear the searchText
before updating selectedSectionRaw to ensure the target section is not filtered
out by an active search. Add a line to set searchText to an empty string before
the selectedSectionRaw assignment so that when the navigation target is applied,
the sidebar is not filtered and the selected section remains visible to the
user.
- Around line 2464-2474: The settings window only sets an initial size but
doesn't enforce a minimum; update the initialization around the NSWindow named
window (before super.init(window: window)) to set window.minSize and
window.contentMinSize to the intended minimum NSSize (e.g., width: 900, height:
620) so SettingsRootView's layout cannot be resized below the floor; ensure both
properties are assigned on the same window instance created there.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d8a8f5d0-8943-4996-bd25-11baab51baa2

📥 Commits

Reviewing files that changed from the base of the PR and between 95d4ca1 and 30e443a.

📒 Files selected for processing (4)
  • Resources/Localizable.xcstrings
  • Sources/TerminalController.swift
  • Sources/WindowDecorationsController.swift
  • Sources/cmuxApp.swift
✅ Files skipped from review due to trivial changes (1)
  • Resources/Localizable.xcstrings

Comment thread Sources/cmuxApp.swift
Comment on lines 71 to 73
private func trafficLightOffset(for window: NSWindow) -> NSPoint {
guard window.identifier?.rawValue == "cmux.settings" else { return .zero }
// Nudge controls slightly right/down to align with the custom Settings title row.
return NSPoint(x: 7, y: -4)
return .zero
}

@coderabbitai coderabbitai Bot Apr 28, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep the zero offset scoped to the settings window.

trafficLightOffset(for:) now returns .zero for every window, so applyTrafficLightOffset(on:hidden:) stops applying any offset anywhere this controller is used. If the intent is only to neutralize cmux.settings, keep the identifier check here and preserve the existing offset for other windows.

🔧 Proposed fix
 private func trafficLightOffset(for window: NSWindow) -> NSPoint {
-    return .zero
+    if window.identifier?.rawValue == "cmux.settings" {
+        return .zero
+    }
+    return /* existing non-settings offset logic */
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/WindowDecorationsController.swift` around lines 71 - 73, The
trafficLightOffset(for:) implementation was changed to always return .zero,
disabling offsets globally; revert this to only return .zero for the settings
window by checking the window identifier (e.g., compare
window.identifier?.rawValue to "cmux.settings") and otherwise compute/return the
original offset value used previously so applyTrafficLightOffset(on:hidden:)
still applies offsets for non-settings windows; update the logic inside
trafficLightOffset(for:) (and ensure any helper used there is preserved) rather
than changing applyTrafficLightOffset(on:hidden:).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Verified as not applicable. On main, non-settings windows already returned zero offset; this PR intentionally removes the Settings-only traffic-light offset, so no non-settings offset behavior was lost.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Verified as not applicable. On main, non-settings windows already returned zero offset; this PR intentionally removes the Settings-only traffic-light offset, so no non-settings offset behavior was lost.

— Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@lawrencecchen, understood — the previous implementation had .zero as the default for all non-settings windows, so collapsing the method to always return .zero correctly preserves that behavior while intentionally dropping the Settings-specific nudge (now unnecessary with the native system titlebar via the SwiftUI WindowGroup). My comment was a false positive. Thanks for the clarification!

(ᓀ‸ᓂ)


✏️ Learnings added
Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3244
File: Sources/WindowDecorationsController.swift:71-73
Timestamp: 2026-04-29T06:24:51.350Z
Learning: Repo: manaflow-ai/cmux — Sources/WindowDecorationsController.swift — `trafficLightOffset(for:)` returned `.zero` for all non-settings windows on `main`; the only non-zero offset was for `cmux.settings` (nudge right/down to align with the custom Settings title row). PR `#3244` intentionally removes that offset because Settings now uses a SwiftUI WindowGroup with a native macOS system titlebar, making the nudge unnecessary. Do not flag `trafficLightOffset(for:)` always returning `.zero` as a regression.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:3586-3604
Timestamp: 2026-04-14T20:00:21.500Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — On macOS 26+, the app intentionally keeps NSWindow.StyleMask.fullSizeContentView enabled even with the native (non-transparent) system titlebar. The titlebar handles dragging natively; isMovable=true and isMovableByWindowBackground=false are used. Any comment suggesting “without fullSizeContentView” is outdated.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2124
File: Sources/AppDelegate.swift:0-0
Timestamp: 2026-03-25T08:05:26.034Z
Learning: Repo: manaflow-ai/cmux — File: Sources/AppDelegate.swift — Pattern for “New Window” geometry seed:
In AppDelegate.createMainWindow(initialWorkingDirectory:sessionWindowSnapshot:), compute the existingFrame using preferredMainWindowContextForWorkspaceCreation(debugSource: "createMainWindow.initialGeometry") and then resolvedWindow(for:) rather than relying on NSApp.keyWindow or the first registered mainWindowContext. Rationale: ensures the new window inherits size from the intended main-terminal window even when an auxiliary window is key, and keeps behavior consistent with showOpenFolderPanel().

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3179
File: Sources/ContentView.swift:4005-4021
Timestamp: 2026-04-27T11:59:15.622Z
Learning: Repo: manaflow-ai/cmux — PR `#3179` — Sources/ContentView.swift
Learning: For separate-surface transparent windows, installNativeTitlebarBackdrop intentionally inserts NativeTitlebarBackdropView below contentView (NSHostingView) — positioned .below relativeTo: contentView — so the backdrop fills through transparent hosting without tinting SwiftUI titlebar text/buttons. The titlebar‑chrome ancestor fallback is only for edge cases where contentView isn’t directly under the NSThemeFrame. This layering was pixel‑verified in PR `#3179`; do not “fix” it to sit below the titlebar chrome on this path.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3179
File: Sources/ContentView.swift:3991-4002
Timestamp: 2026-04-27T09:15:53.326Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — Native titlebar backdrop layering: installNativeTitlebarBackdrop must place the backdrop between the NSThemeFrame’s background material and the standard titlebar chrome (insert .below the close button’s ancestor in the theme frame). When the titlebar-chrome ancestor lookup fails, fall back to inserting .below relativeTo: nil so the backdrop is behind existing siblings and does not overpaint the SwiftUI titlebar/sidebar strip. Visual matching has been verified via pixel samples.

Learnt from: atani
Repo: manaflow-ai/cmux PR: 819
File: Sources/ContentView.swift:3194-3196
Timestamp: 2026-03-04T14:05:48.668Z
Learning: In manaflow-ai/cmux (PR `#819`), Sources/ContentView.swift: The command palette’s external window labels intentionally use the global window index from the full orderedSummaries (index + 1), matching the Window menu in AppDelegate. Do not reindex after filtering out the current window to avoid mismatches (“Window 2” for an external window is expected).

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3166
File: Sources/ContentView.swift:0-0
Timestamp: 2026-04-27T06:58:21.434Z
Learning: In the Swift UI code (e.g., Sources/ContentView.swift), when `preferLiquidGlass`/`materialPolicy.preferLiquidGlass` is enabled for sidebar Liquid Glass, only omit the `Color` tint overlay if `NSGlassEffectView` (native Liquid Glass) is actually available. Compute `usingNativeLiquidGlass = materialPolicy.preferLiquidGlass && SidebarVisualEffectBackground.liquidGlassAvailable`, and when `usingNativeLiquidGlass` is false keep the overlay so the non-native `NSVisualEffectView` fallback still receives the configured tint.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3218
File: Sources/AppDelegate.swift:3456-3469
Timestamp: 2026-04-28T11:35:31.575Z
Learning: Repo: manaflow-ai/cmux — Sources/AppDelegate.swift (Swift)
Learning: The DEBUG-only helper unregisterMainWindowContextForTesting(windowId:) should mirror production teardown: after removing matching mainWindowContexts entries, call activateMainWindowContext(mainWindowContexts.values.first) when the active context was removed so tabManager, sidebarState, sidebarSelectionState, and fileExplorerState are repointed (or cleared when none remain). This prevents stale active pointers in tests.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3166
File: Sources/Windowing/WindowAppearanceSnapshot.swift:185-210
Timestamp: 2026-04-27T06:58:26.079Z
Learning: Repo: manaflow-ai/cmux — Sources/GhosttyTerminalView.swift & Sources/Windowing/WindowAppearanceSnapshot.swift — Snapshot refresh contract for usesHostLayerBackground:
`notifyDefaultBackgroundDidChange()` (and therefore the titlebar/window snapshot refresh in ContentView) must fire whenever `usesHostLayerBackground` changes, even if terminal background color and opacity are unchanged. The prior `hasChanged` check in `updateDefaultBackground()` only compared colors; it was extended so that a toggle of `usesHostLayerBackground` alone also triggers the notification. ContentView listens at the window level to rebuild `WindowAppearanceSnapshot` (including `terminalRenderingMode`) on any such notification. Fixed in commits 4913d17–979a683 (PR `#3166`).

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:15597-15615
Timestamp: 2026-04-14T20:05:53.504Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — On macOS 26 in this app, the only NSSplitView in the window’s view tree is the one owned by NavigationSplitView. Therefore SplitViewDividerHiderView.hideDividers()/patchSplitViews(in:) effectively scope to that single split. Do not flag “global divider clearing” for this path unless additional split views are introduced later.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/AppDelegate.swift:10168-10189
Timestamp: 2026-04-14T20:02:41.558Z
Learning: Repo manaflow-ai/cmux — On macOS 26, the notifications popover is owned by ContentView (State isNotificationsPopoverPresented). AppDelegate APIs (toggle/show/dismiss/isShown) must route via NotificationCenter with a window-scoped userInfo["windowId"] and track visibility per-window, not call UpdateTitlebarAccessoryController. ContentView should observe show/dismiss/toggle and post a visibilityDidChange notification {windowId, visible}.

Learnt from: tranquillum
Repo: manaflow-ai/cmux PR: 2827
File: Sources/Sidebar/ProviderAccountsFooterPanel.swift:174-198
Timestamp: 2026-04-17T15:46:53.298Z
Learning: Repo: manaflow-ai/cmux — Sources/Sidebar/ProviderAccountsFooterPanel.swift + Sources/Sidebar/ProviderAccountsPopover.swift (PR `#2827`):
- `ProviderStatusRanking.impactSeverity(_:)` intentionally returns `-1` for any unrecognized/`"none"` impact string.
- When `worstImpactSeverity == -1` (every incident in the list has an unknown/none impact), `ProviderStatusLabel.statusText` and `.dotColor` fall through to the `default` branch, which shows "Operational" / green.
- This is a deliberate design choice: `StatuspageIOFetcher.fetch` already defaults missing impact fields to `"none"`, so the only way to reach `worstImpactSeverity == -1` is a genuinely benign incident array; showing "Operational" here avoids false-positive yellow.
- The popover always surfaces the full raw incident rows independently, so no incident is invisible to the user.
- Do NOT flag the `-1 → Operational` fallback as a bug or suggest raising unknown impacts to `minor` severity.

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-28T22:24:17.987Z
Learning: Repo: manaflow-ai/cmux — In Sources/Workspace.swift (PR `#3237`, commit 9dcb9c2), the `newTerminalSurface(...)` method gained an `allowInitialInputWithRemoteStartupCommand: Bool` parameter (default `false`). The session-restore call site in `createPanel(from:inPane:)` passes `true` only when `!panelWasRemoteBacked && combinedInitialInput != nil`, making the per-panel snapshot flag the sole authority for restore-time `initialInput` injection. All 12 other call sites keep `false`, preserving the existing remote-drop defense.

Learnt from: MaTriXy
Repo: manaflow-ai/cmux PR: 1460
File: Sources/TerminalController.swift:0-0
Timestamp: 2026-03-16T08:02:06.824Z
Learning: In Swift sources, for any panel_id-only route handling in v2PanelMarkBackground(params:) and v2PanelMarkForeground(params:), first attempt v2ResolveTabManager(params:). Use the manager only if it actually owns the panelId; otherwise fall back to AppDelegate.shared?.locateSurface(surfaceId:) to locate the correct TabManager across windows. Apply this pattern to all panel_id-only routes to avoid active-window bias.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3164
File: Sources/cmuxApp.swift:189-197
Timestamp: 2026-04-26T06:03:08.863Z
Learning: Repo: manaflow-ai/cmux — PR `#3164` — Bootstrap contract: In cmuxApp.bootstrapMainWindowScene(), AppDelegate.bootstrapInitialMainWindowIfNeeded(...) calls ensureInitialMainWindowIfNeeded() to create (if needed) the real AppKit main window via AppDelegate.createMainWindow, which wires CmuxConfigStore and FileExplorerState and calls registerMainWindow(... fileExplorerState:). Only after registration does updateSocketController() start TerminalController using AppDelegate.synchronizeActiveMainWindowContext(...). Conclusion: per‑window config/file‑explorer/directory initialization is handled in AppDelegate and occurs before TerminalController starts; do not flag missing setup in WindowGroup.onAppear.

Learnt from: arieltobiana
Repo: manaflow-ai/cmux PR: 1873
File: Sources/TerminalController.swift:4071-4087
Timestamp: 2026-03-20T17:18:30.333Z
Learning: Repo: manaflow-ai/cmux — In Sources/TerminalController.swift, v2WorkspaceAction(params:) -> case "set_color": palette names are resolved via WorkspaceTabColorSettings.defaultPaletteWithOverrides(), whose entries are always valid hex (validated by the UI). Therefore, additional normalization of entry.hex is unnecessary.

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-28T22:24:17.987Z
Learning: Repo: manaflow-ai/cmux — In Sources/GhosttyTerminalView.swift (PR `#3237`, commit 9dcb9c2), the explicit-vs-baseConfig.initialInput resolution logic was extracted from `TerminalSurface.createSurface(for:)` into a standalone `TerminalSurface.resolveInitialInput(...)` static/instance method for unit-testability. Behavior is unchanged. A companion `TerminalSurfaceResolveInitialInputTests` suite asserts the contract, including byte-for-byte preservation of Ghostty raw-bytes startup-input via `baseConfig.initialInput`.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2528
File: Sources/AppDelegate.swift:2196-2200
Timestamp: 2026-04-03T03:36:45.112Z
Learning: Repo: manaflow-ai/cmux — In Sources/AppDelegate.swift, when KeyboardShortcutSettings.didChangeNotification fires, AppDelegate must clear configured-chord caches (pendingConfiguredShortcutChord and activeConfiguredShortcutChordPrefixForCurrentEvent) via clearConfiguredShortcutChordState() before refreshing tooltips/UI. Also clear chord state on applicationWillResignActive to avoid cross-activity leakage. Verified by cmuxTests/AppDelegateShortcutRoutingTests.swift::testShortcutChangeClearsPendingConfiguredChord.

Learnt from: Horacehxw
Repo: manaflow-ai/cmux PR: 1980
File: Sources/AppDelegate.swift:0-0
Timestamp: 2026-04-06T09:33:43.688Z
Learning: Repo: manaflow-ai/cmux — File: Sources/AppDelegate.swift — UI test launch stabilization: in stabilizeUITestLaunchWindowAndForeground(attempt:), call moveUITestWindowToTargetDisplayIfNeeded() only on the first pass (attempt == 0) so the helper’s own 20-step retry handles display moves; subsequent attempts only activate the app and write diagnostics. This avoids nested overlapping retries and stage-log churn.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:15597-15614
Timestamp: 2026-04-14T20:29:58.471Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift (PR `#2647`, macOS 26): SplitViewDividerHiderView hides split dividers by calling the private selector setDividerColor: only when responds(to:) is true (no KVC). Scope is effectively the single NSSplitView owned by NavigationSplitView. Do not flag KVC misuse or “global divider clearing” for this path unless additional NSSplitViews are added.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:2889-2911
Timestamp: 2026-04-14T19:59:54.878Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — On macOS 26, the NavigationSplitView sidebar width is synchronized back to model state via a GeometryReader that updates sidebarWidth and SidebarState.persistedWidth on width changes. Do not flag “write-only sidebar width” drift for this path going forward.

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-28T22:24:17.987Z
Learning: Repo: manaflow-ai/cmux — In Sources/GhosttyTerminalView.swift (PR `#3237`, commit 9dcb9c2), `private var initialInput` on `TerminalSurface` was changed to `private(set) var initialInput` to allow `testable` consumers to assert the post-normalization value. The setter remains class-private.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3166
File: Sources/Windowing/WindowAppearanceSnapshot.swift:185-210
Timestamp: 2026-04-27T06:58:26.717Z
Learning: Repo: manaflow-ai/cmux — Sources/GhosttyTerminalView.swift & Sources/ContentView.swift — Snapshot refresh contract for usesHostLayerBackground (post PR `#3166`):
- `updateDefaultBackground()` must fire `notifyDefaultBackgroundDidChange()` (or equivalent generation bump) whenever `usesHostLayerBackground` changes, even if terminal background color and opacity are unchanged, because `WindowAppearanceSnapshot.terminalRenderingMode(usesHostLayerBackground:)` depends on it.
- ContentView must subscribe to the Ghostty default-background notification at the window level (not just color/opacity changes) so the titlebar/window snapshot (`windowAppearanceSnapshot`) is recomputed whenever the rendering mode toggles.
- Do NOT gate the notification solely on color hasChanged; rendering-mode-only transitions must also propagate through the same notification channel.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2575
File: Sources/cmuxApp.swift:0-0
Timestamp: 2026-04-06T08:59:21.923Z
Learning: Repo: manaflow-ai/cmux — File: Sources/cmuxApp.swift — In SettingsView, when handling SettingsNavigationRequest, clear the sidebar search first (settingsSearchText = "") before calling scrollTo; sections are conditionally rendered based on the active query, so clearing ensures the target anchors (e.g., SettingsSection.browser, SettingsSection.keyboardShortcuts, SettingsNavigationTarget.browserImport) exist.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:2918-2932
Timestamp: 2026-04-14T20:05:27.511Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift (PR `#2647`): On macOS 26, NavigationSplitView provides a native collapse chevron in the sidebar column header when the sidebar is visible. The code intentionally: (1) shows a custom toolbar toggle only when the sidebar is hidden to reopen it, and (2) uses SystemSidebarToggleStripper to remove the system-injected toolbar toggle to prevent duplication. Do not flag “no collapse affordance” while the sidebar is open.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2575
File: Sources/cmuxApp.swift:0-0
Timestamp: 2026-04-06T09:01:51.979Z
Learning: Repo: manaflow-ai/cmux — File: Sources/cmuxApp.swift — SettingsView now uses SwiftUI `.searchable(text:placement:prompt:)` for the sidebar search, which provides a native (accessible) clear button. Do not flag missing accessibility on a custom clear button in this view going forward.

Learnt from: atani
Repo: manaflow-ai/cmux PR: 819
File: Sources/AppDelegate.swift:0-0
Timestamp: 2026-03-04T14:05:42.574Z
Learning: Guideline: In Swift files (cmux project), when handling pluralized strings, prefer using localization keys with the ICU-style plural forms .one and .other. For example, use keys like statusMenu.unreadCount.one for the singular case (1) and statusMenu.unreadCount.other for all other counts, and similarly for statusMenu.tooltip.unread.one/other. Rationale: ensures correct pluralization across locales and makes localization keys explicit. Review code to ensure any unread count strings and related tooltips follow this .one/.other key pattern and verify the correct value is chosen based on the count.

Learnt from: austinywang
Repo: manaflow-ai/cmux PR: 954
File: Sources/TerminalController.swift:0-0
Timestamp: 2026-03-05T22:04:34.712Z
Learning: Adopt the convention: for health/telemetry tri-state values in Swift, prefer Optionals (Bool?) over sentinel booleans. In TerminalController.swift, socketConnectable is Bool? and only set when socketProbePerformed is true; downstream logic must treat nil as 'not probed'. Ensure downstream code checks for nil before using a value and uses explicit non-nil checks to determine state, improving clarity and avoiding misinterpretation of default false.

Learnt from: moyashin63
Repo: manaflow-ai/cmux PR: 1074
File: Sources/AppDelegate.swift:7523-7545
Timestamp: 2026-03-09T01:38:24.337Z
Learning: When the command palette is visible (as in manaflow-ai/cmux Sources/AppDelegate.swift), ensure the shortcut handling consumes most Command shortcuts to protect the palette's text input. Specifically, do not allow UI zoom shortcuts (Cmd+Shift+= / Cmd+Shift+− / Cmd+Shift+0) to trigger while the palette is open. Do not reorder shortcut handlers (e.g., uiZoomShortcutAction(...)) to bypass this guard; users must close the palette before performing zoom actions. This guideline should apply to Swift source files handling global shortcuts within the app.

Learnt from: zlatkoc
Repo: manaflow-ai/cmux PR: 1368
File: Sources/Panels/BrowserPanel.swift:69-69
Timestamp: 2026-03-13T13:46:01.733Z
Learning: Do not wrap engine/brand name literals (e.g., displayName values such as Google, DuckDuckGo, Bing, Kagi, Startpage) in String(localized: ...). These are brand/product names that are not translatable UI text. Localization should apply to generic UI strings (labels, buttons, error messages, etc.). Apply this guideline across Swift source files under Sources/ (notably in BrowserPanel.swift and similar UI/engine-related strings) and flag only brand-name strings that are part of user-facing UI text appropriately for translation scope.

Learnt from: kjb0787
Repo: manaflow-ai/cmux PR: 1461
File: Sources/GhosttyTerminalView.swift:5904-5905
Timestamp: 2026-03-15T19:22:32.330Z
Learning: In Swift files under the Sources directory that manage terminal/scroll behavior, ensure the following: when preserving scroll across workspace switches, save savedScrollRow only if the scrollbar offset is greater than 0 (indicating the user has scrolled up). On restore, call scroll_to_row only if savedScrollRow is non-nil; if it is nil, rely on synchronizeScrollView() to keep bottom-pinned sessions following new output. This pattern should be applied wherever GhosttyTerminalView-like views implement setVisibleInUI(_:) to maintain consistent user scroll state across workspace switches.

Learnt from: pratikpakhale
Repo: manaflow-ai/cmux PR: 2011
File: Resources/Localizable.xcstrings:15256-15368
Timestamp: 2026-03-23T21:39:50.795Z
Learning: When reviewing this repo’s Swift localization usage, do not flag missing `String.localizedStringWithFormat` for calls that use the modern overload `String(localized: "key", defaultValue: "...\(variable)")` (where `defaultValue` is a `String.LocalizationValue` built with `\(…)`). That overload natively supports interpolation and the xcstrings/runtime substitution handles the resulting placeholders automatically. Only require `String.localizedStringWithFormat` when using the older `String(localized:)` overload that takes a plain `String` (i.e., where format arguments must be passed separately), such as for keys like `clipboard.sshError.single`.

Learnt from: thunter009
Repo: manaflow-ai/cmux PR: 1825
File: Sources/TerminalController.swift:3620-3622
Timestamp: 2026-03-25T00:32:54.735Z
Learning: When validating or reporting workspace/tab colors in this repo, only accept and use 6-digit hex colors in the form `#RRGGBB` (no alpha, i.e., do not allow `#RRGGBBAA`). Ensure validation logic matches the existing behavior (e.g., WorkspaceTabColorSettings.normalizedHex(...) and TabManager.setTabColor(tabId:color:) as well as CLI/cmux.swift). Update any error/help text for workspace color to reference only `#RRGGBB` (not `#RRGGBBAA`).

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 2545
File: Sources/GhosttyTerminalView.swift:3891-3903
Timestamp: 2026-04-02T21:37:21.463Z
Learning: In Swift source files like Sources/GhosttyTerminalView.swift, avoid logging raw startup commands or initialInput even in DEBUG (to prevent leaking sensitive paths/tokens and multiline content). If you need to diagnose startup/input, log only non-sensitive metadata such as (1) presence flags (e.g., hasStartupCommand/hasInitialInput), (2) byte counts, and (3) the relevant surface id (so issues can be correlated without exposing the underlying strings).

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2528
File: Sources/cmuxApp.swift:6439-6444
Timestamp: 2026-04-03T03:35:54.082Z
Learning: In this repo’s keyboard shortcut implementation, ensure `KeyboardShortcutSettings.setShortcut(...)` does nothing (no-op) when `KeyboardShortcutSettings.isManagedBySettingsFile(action)` returns `true` (i.e., the shortcut is managed via `settings.json`). This prevents writing back overrides into `UserDefaults` and keeps `settings.json` as the source of truth.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2964
File: Sources/ContentView.swift:0-0
Timestamp: 2026-04-17T21:35:25.493Z
Learning: In this repo’s shell session resume flow, always build resume commands using the cwd guard helper exposed by SessionEntry (e.g., resumeCommandWithCwd). The helper should produce a command of the form `cd <shell-quoted cwd> && <resumeCommand>`. Update all call sites that generate “resume” commands (e.g., clipboard actions, drag-drop terminal, and in-app resume) to use this helper so that rc files and newly spawned shells cannot start outside the intended directory. Avoid constructing resume commands directly without the guarded `cd` + shell-quoting + `&&` composition.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2978
File: Sources/Workspace.swift:0-0
Timestamp: 2026-04-22T08:13:36.833Z
Learning: In manaflow-ai/cmux, note that Sources/RestorableAgentSession.swift’s `SessionRestorableAgentSnapshot.resumeCommand` already includes a cwd guard when `workingDirectory` is present (it returns a string like `cd <shell-quoted cwd> && <resumeCommand>`). At call sites (e.g., `Workspace.createPanel(...)`), pass `.resumeCommand` through directly and do not prepend another `cd`/cwd guard or wrap it with an additional `cd <...> &&`—otherwise the working directory may be applied twice or incorrectly.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2978
File: Sources/Workspace.swift:0-0
Timestamp: 2026-04-22T08:14:04.901Z
Learning: In the cmux Swift sources, when restoring a restorable agent session, call sites should pass `resumeCommand` directly (ensuring it has the expected trailing newline if required) to `sendInputWhenReady`. Do not wrap `resumeCommand` with an additional `cd '<cwd>' && ...` guard, because `SessionRestorableAgentSnapshot.resumeCommand` already returns a `cwd`-guarded command; adding another guard can result in `double-cd`. Apply this especially along restore paths (e.g., `Sources/Workspace.swift` restore logic) whenever using `resumeCommand`.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3084
File: Sources/AppDelegate.swift:4958-4975
Timestamp: 2026-04-22T11:37:36.238Z
Learning: In Swift code, when re-registering or updating an existing window/session context (e.g., in AppDelegate.registerMainWindow or similar flows), only update an existing *cmuxConfigStore* (or equivalent per-window configuration store) if the incoming configuration/store value is non-nil. Do not overwrite an existing per-window store with nil, so the previous per-window configuration is preserved across re-registration paths.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3128
File: Sources/Panels/BrowserPanelView.swift:4271-4290
Timestamp: 2026-04-23T11:23:49.934Z
Learning: In OmnibarSuggestionsView (and other omnibar-related debug/telemetry logging), never log raw omnibar suggestion content (e.g., URLs, titles, queries). Instead, log only non-sensitive metadata such as suggestion kind/category and the byte length of the text (e.g., "browser.suggestionClick kind=<kind> textBytes=<len>"). Apply this rule consistently to all omnibar-related debug logs to avoid leaking user/search data.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3046
File: Sources/TerminalController.swift:2096-2136
Timestamp: 2026-04-24T22:17:52.550Z
Learning: In Swift request/JSON handlers (e.g., v2 JSON-socket handlers) in Sources, prefer using the v2 helpers for numeric parsing—use `v2Int(params, "<key>")` for normal integer inputs and `v2StrictInt(...)` when strictness is required—rather than casting with `as? Int`. JSONSerialization may yield NSNumber/Double for numeric fields, so v2Int/v2StrictInt ensures correct extraction and type handling. If parsing is used for safety (e.g., timeouts), clamp/validate the parsed value as appropriate (as in `vm.exec` parsing `timeout_ms` and enforcing `>= 1`).

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3182
File: Sources/ContentView.swift:10850-10875
Timestamp: 2026-04-27T10:11:36.830Z
Learning: When computing NSTextView content height for NSTextView-based editors (e.g., a method like naturalDocumentHeight(...)), account for trailing newline layout. Specifically, include `layoutManager.extraLineFragmentRect.height` in the measured height only when `extraLineFragmentTextContainer == textContainer`. If you don’t, the caret on the final blank line can be clipped. Apply this rule to future NSTextView-based editors in this repo.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3139
File: Sources/Panels/FilePreviewPanel.swift:534-537
Timestamp: 2026-04-28T05:45:32.192Z
Learning: When implementing workspace/panel teardown or close-confirmation logic in Sources (e.g., close/collapse/workspace-close flows), rely on the shared dirty-state gate driven by the panel’s `isDirty` property rather than adding panel-specific teardown special-casing. Ensure each panel (including `FilePreviewPanel`) exposes and keeps its `isDirty` state up to date (e.g., via `Published private(set) var isDirty` and any subscriptions/synchronization logic), so the generic `panel.isDirty` check correctly covers all panel types during teardown.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3218
File: Sources/AppDelegate.swift:4800-4802
Timestamp: 2026-04-28T11:43:53.356Z
Learning: In Swift code that selects or activates the next main-window context (e.g., iterating window-context collections), avoid iterating `mainWindowContexts.values` directly while calling `resolvedWindow(for:)`. Since `resolvedWindow(for:)` may reindex/mutate `mainWindowContexts`, this can cause mutation-during-enumeration issues. Instead, snapshot first with `Array(mainWindowContexts.values)`, then resolve/reindex against that snapshot, and only then call `activateMainWindowContext(_:)` using the resolved result.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3247
File: Sources/ContentView.swift:0-0
Timestamp: 2026-04-29T01:08:39.652Z
Learning: Maintain the behavior contract for “Copy Workspace ID(s)”: when triggered from the sidebar context menu (e.g., in Sources/ContentView.swift TabItemView), the command must copy plain UUIDs (IDs-only), not references/refs. For command palette identifier-copy commands where refs are required, ensure the implementation explicitly passes includeRefs: true. This preserves backward compatibility for scripts expecting UUID-only output while allowing the palette to return richer payloads when needed.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3256
File: Sources/TerminalController.swift:0-0
Timestamp: 2026-04-29T01:20:59.683Z
Learning: In this repo’s Swift implementation, keep browser-creation/open behavior consistent when `BrowserAvailabilitySettings` is disabled. For both V1 and V2, any command that includes a URL when creating/opening a browser (e.g., `open_browser` with a URL, `v2 surface.create` with `type=browser` and `url`, `browser.tab.new` with `url`, `v2 browser.open_split` with `url`) must open the URL externally using `NSWorkspace.shared.open(...)` and return appropriate success metadata. Only URL-less/blank browser creations should fail with the `browser_disabled` error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@lawrencecchen, thanks for clarifying. You're right — looking at the original implementation, trafficLightOffset(for:) returned .zero for all non-settings windows already, so the only offset ever applied was the NSPoint(x: 7, y: -4) nudge specific to cmux.settings. Since PR #3244 migrates Settings to a native SwiftUI WindowGroup titlebar (eliminating the need for that manual alignment), collapsing the whole function to return .zero is correct and loses nothing. I'll record this for future reviews.


✏️ Learnings added
Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3244
File: Sources/WindowDecorationsController.swift:71-73
Timestamp: 2026-04-29T06:25:03.715Z
Learning: Repo: manaflow-ai/cmux — Sources/WindowDecorationsController.swift — `trafficLightOffset(for:)` previously returned a non-zero offset (`NSPoint(x: 7, y: -4)`) only for the `cmux.settings` window identifier (to align traffic-light buttons with a custom Settings title row), and `.zero` for all other windows. PR `#3244` intentionally removed this Settings-specific offset by collapsing the function to `return .zero`, because Settings was migrated to a SwiftUI `WindowGroup` with a native system titlebar that no longer needs manual traffic-light nudging. Do not flag the unconditional `return .zero` in this method as a regression.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:3586-3604
Timestamp: 2026-04-14T20:00:21.500Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — On macOS 26+, the app intentionally keeps NSWindow.StyleMask.fullSizeContentView enabled even with the native (non-transparent) system titlebar. The titlebar handles dragging natively; isMovable=true and isMovableByWindowBackground=false are used. Any comment suggesting “without fullSizeContentView” is outdated.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2124
File: Sources/AppDelegate.swift:0-0
Timestamp: 2026-03-25T08:05:26.034Z
Learning: Repo: manaflow-ai/cmux — File: Sources/AppDelegate.swift — Pattern for “New Window” geometry seed:
In AppDelegate.createMainWindow(initialWorkingDirectory:sessionWindowSnapshot:), compute the existingFrame using preferredMainWindowContextForWorkspaceCreation(debugSource: "createMainWindow.initialGeometry") and then resolvedWindow(for:) rather than relying on NSApp.keyWindow or the first registered mainWindowContext. Rationale: ensures the new window inherits size from the intended main-terminal window even when an auxiliary window is key, and keeps behavior consistent with showOpenFolderPanel().

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3179
File: Sources/ContentView.swift:4005-4021
Timestamp: 2026-04-27T11:59:15.622Z
Learning: Repo: manaflow-ai/cmux — PR `#3179` — Sources/ContentView.swift
Learning: For separate-surface transparent windows, installNativeTitlebarBackdrop intentionally inserts NativeTitlebarBackdropView below contentView (NSHostingView) — positioned .below relativeTo: contentView — so the backdrop fills through transparent hosting without tinting SwiftUI titlebar text/buttons. The titlebar‑chrome ancestor fallback is only for edge cases where contentView isn’t directly under the NSThemeFrame. This layering was pixel‑verified in PR `#3179`; do not “fix” it to sit below the titlebar chrome on this path.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3179
File: Sources/ContentView.swift:3991-4002
Timestamp: 2026-04-27T09:15:53.326Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — Native titlebar backdrop layering: installNativeTitlebarBackdrop must place the backdrop between the NSThemeFrame’s background material and the standard titlebar chrome (insert .below the close button’s ancestor in the theme frame). When the titlebar-chrome ancestor lookup fails, fall back to inserting .below relativeTo: nil so the backdrop is behind existing siblings and does not overpaint the SwiftUI titlebar/sidebar strip. Visual matching has been verified via pixel samples.

Learnt from: atani
Repo: manaflow-ai/cmux PR: 819
File: Sources/ContentView.swift:3194-3196
Timestamp: 2026-03-04T14:05:48.668Z
Learning: In manaflow-ai/cmux (PR `#819`), Sources/ContentView.swift: The command palette’s external window labels intentionally use the global window index from the full orderedSummaries (index + 1), matching the Window menu in AppDelegate. Do not reindex after filtering out the current window to avoid mismatches (“Window 2” for an external window is expected).

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3166
File: Sources/ContentView.swift:0-0
Timestamp: 2026-04-27T06:58:21.434Z
Learning: In the Swift UI code (e.g., Sources/ContentView.swift), when `preferLiquidGlass`/`materialPolicy.preferLiquidGlass` is enabled for sidebar Liquid Glass, only omit the `Color` tint overlay if `NSGlassEffectView` (native Liquid Glass) is actually available. Compute `usingNativeLiquidGlass = materialPolicy.preferLiquidGlass && SidebarVisualEffectBackground.liquidGlassAvailable`, and when `usingNativeLiquidGlass` is false keep the overlay so the non-native `NSVisualEffectView` fallback still receives the configured tint.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3218
File: Sources/AppDelegate.swift:3456-3469
Timestamp: 2026-04-28T11:35:31.575Z
Learning: Repo: manaflow-ai/cmux — Sources/AppDelegate.swift (Swift)
Learning: The DEBUG-only helper unregisterMainWindowContextForTesting(windowId:) should mirror production teardown: after removing matching mainWindowContexts entries, call activateMainWindowContext(mainWindowContexts.values.first) when the active context was removed so tabManager, sidebarState, sidebarSelectionState, and fileExplorerState are repointed (or cleared when none remain). This prevents stale active pointers in tests.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3166
File: Sources/Windowing/WindowAppearanceSnapshot.swift:185-210
Timestamp: 2026-04-27T06:58:26.079Z
Learning: Repo: manaflow-ai/cmux — Sources/GhosttyTerminalView.swift & Sources/Windowing/WindowAppearanceSnapshot.swift — Snapshot refresh contract for usesHostLayerBackground:
`notifyDefaultBackgroundDidChange()` (and therefore the titlebar/window snapshot refresh in ContentView) must fire whenever `usesHostLayerBackground` changes, even if terminal background color and opacity are unchanged. The prior `hasChanged` check in `updateDefaultBackground()` only compared colors; it was extended so that a toggle of `usesHostLayerBackground` alone also triggers the notification. ContentView listens at the window level to rebuild `WindowAppearanceSnapshot` (including `terminalRenderingMode`) on any such notification. Fixed in commits 4913d17–979a683 (PR `#3166`).

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:15597-15615
Timestamp: 2026-04-14T20:05:53.504Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — On macOS 26 in this app, the only NSSplitView in the window’s view tree is the one owned by NavigationSplitView. Therefore SplitViewDividerHiderView.hideDividers()/patchSplitViews(in:) effectively scope to that single split. Do not flag “global divider clearing” for this path unless additional split views are introduced later.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/AppDelegate.swift:10168-10189
Timestamp: 2026-04-14T20:02:41.558Z
Learning: Repo manaflow-ai/cmux — On macOS 26, the notifications popover is owned by ContentView (State isNotificationsPopoverPresented). AppDelegate APIs (toggle/show/dismiss/isShown) must route via NotificationCenter with a window-scoped userInfo["windowId"] and track visibility per-window, not call UpdateTitlebarAccessoryController. ContentView should observe show/dismiss/toggle and post a visibilityDidChange notification {windowId, visible}.

Learnt from: tranquillum
Repo: manaflow-ai/cmux PR: 2827
File: Sources/Sidebar/ProviderAccountsFooterPanel.swift:174-198
Timestamp: 2026-04-17T15:46:53.298Z
Learning: Repo: manaflow-ai/cmux — Sources/Sidebar/ProviderAccountsFooterPanel.swift + Sources/Sidebar/ProviderAccountsPopover.swift (PR `#2827`):
- `ProviderStatusRanking.impactSeverity(_:)` intentionally returns `-1` for any unrecognized/`"none"` impact string.
- When `worstImpactSeverity == -1` (every incident in the list has an unknown/none impact), `ProviderStatusLabel.statusText` and `.dotColor` fall through to the `default` branch, which shows "Operational" / green.
- This is a deliberate design choice: `StatuspageIOFetcher.fetch` already defaults missing impact fields to `"none"`, so the only way to reach `worstImpactSeverity == -1` is a genuinely benign incident array; showing "Operational" here avoids false-positive yellow.
- The popover always surfaces the full raw incident rows independently, so no incident is invisible to the user.
- Do NOT flag the `-1 → Operational` fallback as a bug or suggest raising unknown impacts to `minor` severity.

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-28T22:24:17.987Z
Learning: Repo: manaflow-ai/cmux — In Sources/Workspace.swift (PR `#3237`, commit 9dcb9c2), the `newTerminalSurface(...)` method gained an `allowInitialInputWithRemoteStartupCommand: Bool` parameter (default `false`). The session-restore call site in `createPanel(from:inPane:)` passes `true` only when `!panelWasRemoteBacked && combinedInitialInput != nil`, making the per-panel snapshot flag the sole authority for restore-time `initialInput` injection. All 12 other call sites keep `false`, preserving the existing remote-drop defense.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3164
File: Sources/cmuxApp.swift:189-197
Timestamp: 2026-04-26T06:03:08.863Z
Learning: Repo: manaflow-ai/cmux — PR `#3164` — Bootstrap contract: In cmuxApp.bootstrapMainWindowScene(), AppDelegate.bootstrapInitialMainWindowIfNeeded(...) calls ensureInitialMainWindowIfNeeded() to create (if needed) the real AppKit main window via AppDelegate.createMainWindow, which wires CmuxConfigStore and FileExplorerState and calls registerMainWindow(... fileExplorerState:). Only after registration does updateSocketController() start TerminalController using AppDelegate.synchronizeActiveMainWindowContext(...). Conclusion: per‑window config/file‑explorer/directory initialization is handled in AppDelegate and occurs before TerminalController starts; do not flag missing setup in WindowGroup.onAppear.

Learnt from: pstanton237
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-06T12:03:16.921Z
Learning: Repo: manaflow-ai/cmux — In CLI/cmux.swift, `claudeTeamsLaunchArguments` must use two independent branches: (1) prepend `--teammate-mode auto` only when the caller has NOT already supplied `--teammate-mode`; (2) append `--settings <claudeHooksJSON>` unconditionally (modulo `CMUX_CLAUDE_HOOKS_DISABLED=1` opt-out). These two branches must never be coupled in a single early-return or conditional block — coupling them was the original `#2229` regression (hook JSON silently bypassed when `--teammate-mode auto|manual` was explicit). Mirrors `Resources/bin/claude:208`: `exec "$REAL_CLAUDE" --settings "$HOOKS_JSON" "$@"` where `$@` content never gates hook injection.

Learnt from: MaTriXy
Repo: manaflow-ai/cmux PR: 1460
File: Sources/TerminalController.swift:0-0
Timestamp: 2026-03-16T08:02:06.824Z
Learning: In Swift sources, for any panel_id-only route handling in v2PanelMarkBackground(params:) and v2PanelMarkForeground(params:), first attempt v2ResolveTabManager(params:). Use the manager only if it actually owns the panelId; otherwise fall back to AppDelegate.shared?.locateSurface(surfaceId:) to locate the correct TabManager across windows. Apply this pattern to all panel_id-only routes to avoid active-window bias.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2528
File: Sources/AppDelegate.swift:2196-2200
Timestamp: 2026-04-03T03:36:45.112Z
Learning: Repo: manaflow-ai/cmux — In Sources/AppDelegate.swift, when KeyboardShortcutSettings.didChangeNotification fires, AppDelegate must clear configured-chord caches (pendingConfiguredShortcutChord and activeConfiguredShortcutChordPrefixForCurrentEvent) via clearConfiguredShortcutChordState() before refreshing tooltips/UI. Also clear chord state on applicationWillResignActive to avoid cross-activity leakage. Verified by cmuxTests/AppDelegateShortcutRoutingTests.swift::testShortcutChangeClearsPendingConfiguredChord.

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-28T22:24:17.987Z
Learning: Repo: manaflow-ai/cmux — In Sources/GhosttyTerminalView.swift (PR `#3237`, commit 9dcb9c2), the explicit-vs-baseConfig.initialInput resolution logic was extracted from `TerminalSurface.createSurface(for:)` into a standalone `TerminalSurface.resolveInitialInput(...)` static/instance method for unit-testability. Behavior is unchanged. A companion `TerminalSurfaceResolveInitialInputTests` suite asserts the contract, including byte-for-byte preservation of Ghostty raw-bytes startup-input via `baseConfig.initialInput`.

Learnt from: arieltobiana
Repo: manaflow-ai/cmux PR: 1873
File: Sources/TerminalController.swift:4071-4087
Timestamp: 2026-03-20T17:18:30.333Z
Learning: Repo: manaflow-ai/cmux — In Sources/TerminalController.swift, v2WorkspaceAction(params:) -> case "set_color": palette names are resolved via WorkspaceTabColorSettings.defaultPaletteWithOverrides(), whose entries are always valid hex (validated by the UI). Therefore, additional normalization of entry.hex is unnecessary.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:15597-15614
Timestamp: 2026-04-14T20:29:58.471Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift (PR `#2647`, macOS 26): SplitViewDividerHiderView hides split dividers by calling the private selector setDividerColor: only when responds(to:) is true (no KVC). Scope is effectively the single NSSplitView owned by NavigationSplitView. Do not flag KVC misuse or “global divider clearing” for this path unless additional NSSplitViews are added.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:2889-2911
Timestamp: 2026-04-14T19:59:54.878Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift — On macOS 26, the NavigationSplitView sidebar width is synchronized back to model state via a GeometryReader that updates sidebarWidth and SidebarState.persistedWidth on width changes. Do not flag “write-only sidebar width” drift for this path going forward.

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 0
File: :0-0
Timestamp: 2026-04-28T22:24:17.987Z
Learning: Repo: manaflow-ai/cmux — In Sources/GhosttyTerminalView.swift (PR `#3237`, commit 9dcb9c2), `private var initialInput` on `TerminalSurface` was changed to `private(set) var initialInput` to allow `testable` consumers to assert the post-normalization value. The setter remains class-private.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3166
File: Sources/Windowing/WindowAppearanceSnapshot.swift:185-210
Timestamp: 2026-04-27T06:58:26.717Z
Learning: Repo: manaflow-ai/cmux — Sources/GhosttyTerminalView.swift & Sources/ContentView.swift — Snapshot refresh contract for usesHostLayerBackground (post PR `#3166`):
- `updateDefaultBackground()` must fire `notifyDefaultBackgroundDidChange()` (or equivalent generation bump) whenever `usesHostLayerBackground` changes, even if terminal background color and opacity are unchanged, because `WindowAppearanceSnapshot.terminalRenderingMode(usesHostLayerBackground:)` depends on it.
- ContentView must subscribe to the Ghostty default-background notification at the window level (not just color/opacity changes) so the titlebar/window snapshot (`windowAppearanceSnapshot`) is recomputed whenever the rendering mode toggles.
- Do NOT gate the notification solely on color hasChanged; rendering-mode-only transitions must also propagate through the same notification channel.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2575
File: Sources/cmuxApp.swift:0-0
Timestamp: 2026-04-06T08:59:21.923Z
Learning: Repo: manaflow-ai/cmux — File: Sources/cmuxApp.swift — In SettingsView, when handling SettingsNavigationRequest, clear the sidebar search first (settingsSearchText = "") before calling scrollTo; sections are conditionally rendered based on the active query, so clearing ensures the target anchors (e.g., SettingsSection.browser, SettingsSection.keyboardShortcuts, SettingsNavigationTarget.browserImport) exist.

Learnt from: rodchristiansen
Repo: manaflow-ai/cmux PR: 2647
File: Sources/ContentView.swift:2918-2932
Timestamp: 2026-04-14T20:05:27.511Z
Learning: Repo: manaflow-ai/cmux — Sources/ContentView.swift (PR `#2647`): On macOS 26, NavigationSplitView provides a native collapse chevron in the sidebar column header when the sidebar is visible. The code intentionally: (1) shows a custom toolbar toggle only when the sidebar is hidden to reopen it, and (2) uses SystemSidebarToggleStripper to remove the system-injected toolbar toggle to prevent duplication. Do not flag “no collapse affordance” while the sidebar is open.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2575
File: Sources/cmuxApp.swift:0-0
Timestamp: 2026-04-06T09:01:51.979Z
Learning: Repo: manaflow-ai/cmux — File: Sources/cmuxApp.swift — SettingsView now uses SwiftUI `.searchable(text:placement:prompt:)` for the sidebar search, which provides a native (accessible) clear button. Do not flag missing accessibility on a custom clear button in this view going forward.

Learnt from: atani
Repo: manaflow-ai/cmux PR: 819
File: Sources/AppDelegate.swift:0-0
Timestamp: 2026-03-04T14:05:42.574Z
Learning: Guideline: In Swift files (cmux project), when handling pluralized strings, prefer using localization keys with the ICU-style plural forms .one and .other. For example, use keys like statusMenu.unreadCount.one for the singular case (1) and statusMenu.unreadCount.other for all other counts, and similarly for statusMenu.tooltip.unread.one/other. Rationale: ensures correct pluralization across locales and makes localization keys explicit. Review code to ensure any unread count strings and related tooltips follow this .one/.other key pattern and verify the correct value is chosen based on the count.

Learnt from: austinywang
Repo: manaflow-ai/cmux PR: 954
File: Sources/TerminalController.swift:0-0
Timestamp: 2026-03-05T22:04:34.712Z
Learning: Adopt the convention: for health/telemetry tri-state values in Swift, prefer Optionals (Bool?) over sentinel booleans. In TerminalController.swift, socketConnectable is Bool? and only set when socketProbePerformed is true; downstream logic must treat nil as 'not probed'. Ensure downstream code checks for nil before using a value and uses explicit non-nil checks to determine state, improving clarity and avoiding misinterpretation of default false.

Learnt from: moyashin63
Repo: manaflow-ai/cmux PR: 1074
File: Sources/AppDelegate.swift:7523-7545
Timestamp: 2026-03-09T01:38:24.337Z
Learning: When the command palette is visible (as in manaflow-ai/cmux Sources/AppDelegate.swift), ensure the shortcut handling consumes most Command shortcuts to protect the palette's text input. Specifically, do not allow UI zoom shortcuts (Cmd+Shift+= / Cmd+Shift+− / Cmd+Shift+0) to trigger while the palette is open. Do not reorder shortcut handlers (e.g., uiZoomShortcutAction(...)) to bypass this guard; users must close the palette before performing zoom actions. This guideline should apply to Swift source files handling global shortcuts within the app.

Learnt from: zlatkoc
Repo: manaflow-ai/cmux PR: 1368
File: Sources/Panels/BrowserPanel.swift:69-69
Timestamp: 2026-03-13T13:46:01.733Z
Learning: Do not wrap engine/brand name literals (e.g., displayName values such as Google, DuckDuckGo, Bing, Kagi, Startpage) in String(localized: ...). These are brand/product names that are not translatable UI text. Localization should apply to generic UI strings (labels, buttons, error messages, etc.). Apply this guideline across Swift source files under Sources/ (notably in BrowserPanel.swift and similar UI/engine-related strings) and flag only brand-name strings that are part of user-facing UI text appropriately for translation scope.

Learnt from: kjb0787
Repo: manaflow-ai/cmux PR: 1461
File: Sources/GhosttyTerminalView.swift:5904-5905
Timestamp: 2026-03-15T19:22:32.330Z
Learning: In Swift files under the Sources directory that manage terminal/scroll behavior, ensure the following: when preserving scroll across workspace switches, save savedScrollRow only if the scrollbar offset is greater than 0 (indicating the user has scrolled up). On restore, call scroll_to_row only if savedScrollRow is non-nil; if it is nil, rely on synchronizeScrollView() to keep bottom-pinned sessions following new output. This pattern should be applied wherever GhosttyTerminalView-like views implement setVisibleInUI(_:) to maintain consistent user scroll state across workspace switches.

Learnt from: pratikpakhale
Repo: manaflow-ai/cmux PR: 2011
File: Resources/Localizable.xcstrings:15256-15368
Timestamp: 2026-03-23T21:39:50.795Z
Learning: When reviewing this repo’s Swift localization usage, do not flag missing `String.localizedStringWithFormat` for calls that use the modern overload `String(localized: "key", defaultValue: "...\(variable)")` (where `defaultValue` is a `String.LocalizationValue` built with `\(…)`). That overload natively supports interpolation and the xcstrings/runtime substitution handles the resulting placeholders automatically. Only require `String.localizedStringWithFormat` when using the older `String(localized:)` overload that takes a plain `String` (i.e., where format arguments must be passed separately), such as for keys like `clipboard.sshError.single`.

Learnt from: thunter009
Repo: manaflow-ai/cmux PR: 1825
File: Sources/TerminalController.swift:3620-3622
Timestamp: 2026-03-25T00:32:54.735Z
Learning: When validating or reporting workspace/tab colors in this repo, only accept and use 6-digit hex colors in the form `#RRGGBB` (no alpha, i.e., do not allow `#RRGGBBAA`). Ensure validation logic matches the existing behavior (e.g., WorkspaceTabColorSettings.normalizedHex(...) and TabManager.setTabColor(tabId:color:) as well as CLI/cmux.swift). Update any error/help text for workspace color to reference only `#RRGGBB` (not `#RRGGBBAA`).

Learnt from: mrosnerr
Repo: manaflow-ai/cmux PR: 2545
File: Sources/GhosttyTerminalView.swift:3891-3903
Timestamp: 2026-04-02T21:37:21.463Z
Learning: In Swift source files like Sources/GhosttyTerminalView.swift, avoid logging raw startup commands or initialInput even in DEBUG (to prevent leaking sensitive paths/tokens and multiline content). If you need to diagnose startup/input, log only non-sensitive metadata such as (1) presence flags (e.g., hasStartupCommand/hasInitialInput), (2) byte counts, and (3) the relevant surface id (so issues can be correlated without exposing the underlying strings).

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2528
File: Sources/cmuxApp.swift:6439-6444
Timestamp: 2026-04-03T03:35:54.082Z
Learning: In this repo’s keyboard shortcut implementation, ensure `KeyboardShortcutSettings.setShortcut(...)` does nothing (no-op) when `KeyboardShortcutSettings.isManagedBySettingsFile(action)` returns `true` (i.e., the shortcut is managed via `settings.json`). This prevents writing back overrides into `UserDefaults` and keeps `settings.json` as the source of truth.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2964
File: Sources/ContentView.swift:0-0
Timestamp: 2026-04-17T21:35:25.493Z
Learning: In this repo’s shell session resume flow, always build resume commands using the cwd guard helper exposed by SessionEntry (e.g., resumeCommandWithCwd). The helper should produce a command of the form `cd <shell-quoted cwd> && <resumeCommand>`. Update all call sites that generate “resume” commands (e.g., clipboard actions, drag-drop terminal, and in-app resume) to use this helper so that rc files and newly spawned shells cannot start outside the intended directory. Avoid constructing resume commands directly without the guarded `cd` + shell-quoting + `&&` composition.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2978
File: Sources/Workspace.swift:0-0
Timestamp: 2026-04-22T08:13:36.833Z
Learning: In manaflow-ai/cmux, note that Sources/RestorableAgentSession.swift’s `SessionRestorableAgentSnapshot.resumeCommand` already includes a cwd guard when `workingDirectory` is present (it returns a string like `cd <shell-quoted cwd> && <resumeCommand>`). At call sites (e.g., `Workspace.createPanel(...)`), pass `.resumeCommand` through directly and do not prepend another `cd`/cwd guard or wrap it with an additional `cd <...> &&`—otherwise the working directory may be applied twice or incorrectly.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 2978
File: Sources/Workspace.swift:0-0
Timestamp: 2026-04-22T08:14:04.901Z
Learning: In the cmux Swift sources, when restoring a restorable agent session, call sites should pass `resumeCommand` directly (ensuring it has the expected trailing newline if required) to `sendInputWhenReady`. Do not wrap `resumeCommand` with an additional `cd '<cwd>' && ...` guard, because `SessionRestorableAgentSnapshot.resumeCommand` already returns a `cwd`-guarded command; adding another guard can result in `double-cd`. Apply this especially along restore paths (e.g., `Sources/Workspace.swift` restore logic) whenever using `resumeCommand`.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3084
File: Sources/AppDelegate.swift:4958-4975
Timestamp: 2026-04-22T11:37:36.238Z
Learning: In Swift code, when re-registering or updating an existing window/session context (e.g., in AppDelegate.registerMainWindow or similar flows), only update an existing *cmuxConfigStore* (or equivalent per-window configuration store) if the incoming configuration/store value is non-nil. Do not overwrite an existing per-window store with nil, so the previous per-window configuration is preserved across re-registration paths.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3128
File: Sources/Panels/BrowserPanelView.swift:4271-4290
Timestamp: 2026-04-23T11:23:49.934Z
Learning: In OmnibarSuggestionsView (and other omnibar-related debug/telemetry logging), never log raw omnibar suggestion content (e.g., URLs, titles, queries). Instead, log only non-sensitive metadata such as suggestion kind/category and the byte length of the text (e.g., "browser.suggestionClick kind=<kind> textBytes=<len>"). Apply this rule consistently to all omnibar-related debug logs to avoid leaking user/search data.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3046
File: Sources/TerminalController.swift:2096-2136
Timestamp: 2026-04-24T22:17:52.550Z
Learning: In Swift request/JSON handlers (e.g., v2 JSON-socket handlers) in Sources, prefer using the v2 helpers for numeric parsing—use `v2Int(params, "<key>")` for normal integer inputs and `v2StrictInt(...)` when strictness is required—rather than casting with `as? Int`. JSONSerialization may yield NSNumber/Double for numeric fields, so v2Int/v2StrictInt ensures correct extraction and type handling. If parsing is used for safety (e.g., timeouts), clamp/validate the parsed value as appropriate (as in `vm.exec` parsing `timeout_ms` and enforcing `>= 1`).

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3182
File: Sources/ContentView.swift:10850-10875
Timestamp: 2026-04-27T10:11:36.830Z
Learning: When computing NSTextView content height for NSTextView-based editors (e.g., a method like naturalDocumentHeight(...)), account for trailing newline layout. Specifically, include `layoutManager.extraLineFragmentRect.height` in the measured height only when `extraLineFragmentTextContainer == textContainer`. If you don’t, the caret on the final blank line can be clipped. Apply this rule to future NSTextView-based editors in this repo.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3139
File: Sources/Panels/FilePreviewPanel.swift:534-537
Timestamp: 2026-04-28T05:45:32.192Z
Learning: When implementing workspace/panel teardown or close-confirmation logic in Sources (e.g., close/collapse/workspace-close flows), rely on the shared dirty-state gate driven by the panel’s `isDirty` property rather than adding panel-specific teardown special-casing. Ensure each panel (including `FilePreviewPanel`) exposes and keeps its `isDirty` state up to date (e.g., via `Published private(set) var isDirty` and any subscriptions/synchronization logic), so the generic `panel.isDirty` check correctly covers all panel types during teardown.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3218
File: Sources/AppDelegate.swift:4800-4802
Timestamp: 2026-04-28T11:43:53.356Z
Learning: In Swift code that selects or activates the next main-window context (e.g., iterating window-context collections), avoid iterating `mainWindowContexts.values` directly while calling `resolvedWindow(for:)`. Since `resolvedWindow(for:)` may reindex/mutate `mainWindowContexts`, this can cause mutation-during-enumeration issues. Instead, snapshot first with `Array(mainWindowContexts.values)`, then resolve/reindex against that snapshot, and only then call `activateMainWindowContext(_:)` using the resolved result.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3247
File: Sources/ContentView.swift:0-0
Timestamp: 2026-04-29T01:08:39.652Z
Learning: Maintain the behavior contract for “Copy Workspace ID(s)”: when triggered from the sidebar context menu (e.g., in Sources/ContentView.swift TabItemView), the command must copy plain UUIDs (IDs-only), not references/refs. For command palette identifier-copy commands where refs are required, ensure the implementation explicitly passes includeRefs: true. This preserves backward compatibility for scripts expecting UUID-only output while allowing the palette to return richer payloads when needed.

Learnt from: lawrencecchen
Repo: manaflow-ai/cmux PR: 3256
File: Sources/TerminalController.swift:0-0
Timestamp: 2026-04-29T01:20:59.683Z
Learning: In this repo’s Swift implementation, keep browser-creation/open behavior consistent when `BrowserAvailabilitySettings` is disabled. For both V1 and V2, any command that includes a URL when creating/opening a browser (e.g., `open_browser` with a URL, `v2 surface.create` with `type=browser` and `url`, `browser.tab.new` with `url`, `v2 browser.open_split` with `url`) must open the URL externally using `NSWorkspace.shared.open(...)` and return appropriate success metadata. Only URL-less/blank browser creations should fail with the `browser_disabled` error.

@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: 4

Caution

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

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

1112-1243: ⚠️ Potential issue | 🟡 Minor

Localize the renamed About titlebar debug UI.

This rename still leaves new user-facing strings hard-coded in English (About Window, Hidden, Visible, Reset All, etc.). The window title key was localized, but the rest of the new surface still bypasses xcstrings.

As per coding guidelines, "All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text") with keys in Resources/Localizable.xcstrings for all supported languages (English and Japanese), never use bare string literals in SwiftUI Text(), Button(), alert titles, etc."

Also applies to: 1404-1493

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 1112 - 1243, The UI strings introduced in
AboutWindowKind.displayTitle, TitlebarVisibilityOption.displayTitle,
TitlebarToolbarStyleOption.displayTitle and any hard-coded labels used with
AboutTitlebarDebugOptions (e.g., "About Window", "Hidden", "Visible", "Reset
All", the default windowTitle "About cmux", etc.) must be replaced with
localized lookups using String(localized: "key.name", defaultValue: "English
text"); update each enum's displayTitle and the default windowTitle in
AboutTitlebarDebugOptions.defaults to call String(localized:...,
defaultValue:...), create corresponding keys in Resources/Localizable.xcstrings
for English and Japanese, and ensure any SwiftUI Text/Button/Alert usages refer
to these localized values rather than raw string literals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/cmuxApp.swift`:
- Around line 2427-2438: The Settings window is missing a minimum size, so add a
constraint on the NSWindow instance (the window created and passed to
super.init) to prevent the split view from collapsing: set window.minSize (or
window.contentMinSize) to the intended minimum dimensions for the two-column
layout after creating the window and before assigning contentViewController —
reference the existing window variable and SettingsRootView to determine
appropriate minimum width/height and apply via window.minSize = NSSize(width:
..., height: ...) (or window.contentMinSize) so the UI cannot be resized smaller
than the intended layout.
- Around line 8354-8364: The toolbar currently only adds a Reset action; add a
second ToolbarItem in the same .toolbar block that exposes the quick
"settings.json" action so users can open/edit the raw settings from everywhere
in Settings. Locate the existing ToolbarItem/ Button that calls
SettingsResetRequest.post() and add another ToolbarItem (or inline sibling) with
a Button that calls the settings-json action (e.g.
SettingsOpenJSONRequest.post() or the existing method used elsewhere to open the
settings.json editor), using a clear Label like String(localized:
"settings.section.openJSON", defaultValue: "Edit settings.json") and an
appropriate systemImage such as "doc.text" so it mirrors the Keyboard Shortcuts
entry point.
- Around line 2623-2647: The sidebar searchText property currently appends
hard-coded English keyword strings (in the switch cases of the searchText
computed property) which must be localized; replace each literal keyword string
in every case (e.g., in the .account, .app, .terminal, .workspaceColors,
.sidebarAppearance, .automation, .browser, .browserImport, .globalHotkey,
.keyboardShortcuts, .reset branches of var searchText) with String(localized:
"sidebar.searchKeywords.<case>", defaultValue: "<English keywords>") and ensure
matching keys and Japanese translations are added to
Resources/Localizable.xcstrings so the search corpus works in localized builds.
- Around line 8320-8374: The stored selectedSectionRaw isn't being replayed when
the Settings view first appears, so add a one-time replay on appearance: in the
Settings view (where selectedSectionRaw, selectedSection and filteredSections
are defined) call SettingsNavigationRequest.post(selectedSection) inside an
.onAppear or .task handler so the sidebar/detail scroll sync runs on initial
presentation; use the existing selectedSection computed property and the
SettingsNavigationRequest.post(...) API to trigger the same behavior you already
use in the onChange handler.

---

Outside diff comments:
In `@Sources/cmuxApp.swift`:
- Around line 1112-1243: The UI strings introduced in
AboutWindowKind.displayTitle, TitlebarVisibilityOption.displayTitle,
TitlebarToolbarStyleOption.displayTitle and any hard-coded labels used with
AboutTitlebarDebugOptions (e.g., "About Window", "Hidden", "Visible", "Reset
All", the default windowTitle "About cmux", etc.) must be replaced with
localized lookups using String(localized: "key.name", defaultValue: "English
text"); update each enum's displayTitle and the default windowTitle in
AboutTitlebarDebugOptions.defaults to call String(localized:...,
defaultValue:...), create corresponding keys in Resources/Localizable.xcstrings
for English and Japanese, and ensure any SwiftUI Text/Button/Alert usages refer
to these localized values rather than raw string literals.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f16ea4f1-76ef-4fa3-a27a-d0a8a5a46cf0

📥 Commits

Reviewing files that changed from the base of the PR and between 30e443a and d440142.

📒 Files selected for processing (2)
  • Resources/Localizable.xcstrings
  • Sources/cmuxApp.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Resources/Localizable.xcstrings

Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/cmuxApp.swift
Comment thread Sources/cmuxApp.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 187987d1b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/cmuxApp.swift

@MainActor
enum SettingsWindowPresenter {
static let windowID = "settings"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep settings window identifier consistent for shortcut routing

This migration stops assigning the settings window the legacy cmux.settings identifier, but shortcut routing still keys off that value (for example cmuxWindowShouldOwnCloseShortcut and the closeOtherTabsInPane branch in AppDelegate). When Settings is focused, commands like Cmd+W can now be misrouted to the active terminal workspace instead of closing the Settings window, which can close user tabs unexpectedly.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: the SwiftUI Settings window is now configured through WindowAccessor with the legacy cmux.settings NSWindow identifier for shortcut routing.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: the SwiftUI Settings window is now configured through WindowAccessor with the legacy cmux.settings NSWindow identifier for shortcut routing.

— Claude Code

Comment thread Sources/cmuxApp.swift
cmuxDebugLog("settings.window.show path=swiftuiWindowGroup")
#endif
if let navigationTarget {
pendingNavigationTarget = navigationTarget

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear stale pending navigation target after direct navigation

show(navigationTarget:) always stores pendingNavigationTarget, then also posts navigation immediately. If Settings is already open, onAppear never runs, so that pending value is never consumed. The next time Settings is reopened without a target, consumePendingNavigationTarget() applies that stale value and jumps to an unexpected section. This is reproducible by opening Settings, sending a targeted open request (e.g. keyboard shortcuts), closing Settings, then reopening normally.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: targeted opens only leave pendingNavigationTarget for new or queued windows; existing windows clear pending state and receive the navigation request directly.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: targeted opens only leave pendingNavigationTarget for new or queued windows; existing windows clear pending state and receive the navigation request directly.

— Claude Code

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a918a4cbb2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/cmuxApp.swift Outdated
if let navigationTarget {
pendingNavigationTarget = navigationTarget
}
openWindow?()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard settings open calls before presenter setup

SettingsWindowPresenter.show silently no-ops when openWindow hasn’t been configured yet (openWindow?()), but callers like presentPreferencesWindow and the socket settings.open path treat show as guaranteed to display Settings. Because configuration only happens later in MainWindowBootstrapView.onAppear, any early call before that lifecycle point drops the request and leaves users with no window (only app activation). Add a fallback or queue-open behavior when openWindow is still nil.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: SettingsWindowPresenter now queues an open request if openWindow is not configured yet, then replays it when configure(openWindow:) runs.

— Claude Code

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

1129-1204: ⚠️ Potential issue | 🟡 Minor

Localize the new About-titlebar debug labels and fallback titles.

This rename introduced several new raw UI strings (About Window, Hidden, Visible, Reset All, Apply Now, About cmux, etc.). They will stay English in localized builds.

As per coding guidelines, "All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text") with keys in Resources/Localizable.xcstrings for all supported languages (English and Japanese), never use bare string literals in SwiftUI Text(), Button(), alert titles, etc."

Also applies to: 1237-1242, 1427-1505

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 1129 - 1204, Replace all bare user-facing
string literals in the shown enums and related UI code with localized lookups
using String(localized: "key.name", defaultValue: "English text"); specifically
update TitlebarVisibilityOption.displayTitle ("Hidden", "Visible"),
TitlebarToolbarStyleOption.displayTitle (all case titles), the About-related
properties displayTitle and fallbackTitle ("About Window", "About cmux"), and
any other new UI labels like "Reset All" and "Apply Now" to use
String(localized:..., defaultValue:...); add corresponding keys to
Resources/Localizable.xcstrings for English and Japanese and keep the same
defaultValue English text so localized builds pick up translations.
♻️ Duplicate comments (3)
Sources/cmuxApp.swift (3)

2502-2526: ⚠️ Potential issue | 🟠 Major

Localize the sidebar search keyword corpus too.

These search synonyms are still hard-coded English. In Japanese builds, sidebar search will only reliably match the localized titles and miss most of the extra keywords introduced here.

As per coding guidelines, "All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text") with keys in Resources/Localizable.xcstrings for all supported languages (English and Japanese), never use bare string literals in SwiftUI Text(), Button(), alert titles, etc."

Also applies to: 2596-2668

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 2502 - 2526, The hard-coded English
keyword corpus in the searchText computed property (and the similar block around
lines 2596-2668) must be localized: replace each literal return string in the
switch cases (e.g., cases .account, .app, .terminal, .workspaceColors,
.sidebarAppearance, .automation, .browser, .browserImport, .globalHotkey,
.keyboardShortcuts, .reset) with calls to String(localized:
"sidebar.search.<case>.keywords", defaultValue: "English keywords…") using
unique keys added to Resources/Localizable.xcstrings; keep the title
interpolation (e.g., "\(title) …") but localize the rest of the keyword text so
Japanese builds get translated synonyms.

8451-8457: ⚠️ Potential issue | 🟠 Major

Replay the restored section when Settings first appears.

When selectedSectionRaw restores to something other than .account, this path only reselects the sidebar row. The detail SettingsView never gets a navigation request, so the window can reopen with “Browser” selected while the scroll position is still at the top.

💡 Suggested fix
         .onAppear {
             if let target = SettingsWindowPresenter.consumePendingNavigationTarget() {
                 navigate(to: target, postRequest: true)
             } else if !isSearching {
                 selectedSidebarEntryID = SettingsSearchIndex.sectionID(for: selectedSection)
+                DispatchQueue.main.async {
+                    SettingsNavigationRequest.post(selectedSection)
+                }
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 8451 - 8457, When Settings appears and
selectedSectionRaw restored to something other than .account, the code only sets
selectedSidebarEntryID and never sends a navigation request to the SettingsView;
update the .onAppear block so that when there's no pending navigation target AND
selectedSectionRaw (or selectedSection) is not .account you explicitly call
navigate(to: ..., postRequest: true) with a Settings navigation target (use the
same target shape used elsewhere, e.g. the enum/case passed into
navigate(to:postRequest:)) or obtain a target from SettingsWindowPresenter and
pass it into navigate(to:postRequest:) so the detail SettingsView receives the
navigation request and restores its scroll/state.

743-749: ⚠️ Potential issue | 🟠 Major

Reapply the Settings minimum size on the SwiftUI scene.

defaultSize only affects the first presentation. The new WindowGroup path no longer enforces the old minimum, so the split view can still be dragged below the intended two-column layout and start clipping.

💡 Suggested fix
 WindowGroup(String(localized: "settings.title", defaultValue: "Settings"), id: SettingsWindowPresenter.windowID) {
     SettingsRootView()
+        .frame(minWidth: 820, minHeight: 540)
 }
 .defaultSize(width: 980, height: 680)
 .commands {
     SidebarCommands()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/cmuxApp.swift` around lines 743 - 749, The Settings WindowGroup (the
one with SettingsWindowPresenter.windowID and SettingsRootView) currently uses
.defaultSize which only affects the first presentation; instead locate the
actual NSWindow for that window ID when the scene appears (e.g., in an .onAppear
or .task attached to the WindowGroup / SettingsRootView) and set its minSize to
CGSize(width: 980, height: 680) (and optionally maxSize if needed) so the split
view cannot be resized below the intended two-column layout; ensure you
reference SettingsWindowPresenter.windowID when finding the NSWindow and apply
the minSize change there.
🧹 Nitpick comments (1)
Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsShellView.swift (1)

22-27: Show an explicit empty-search state instead of a blank sidebar.

When filteredSections is empty, the list currently renders nothing, which makes search feel broken. Add a localized “No Results” row/state for clearer UX.

Suggested tweak
             List(selection: $selectedSectionRaw) {
-                ForEach(filteredSections) { section in
-                    Label(section.title, systemImage: section.symbolName)
-                        .tag(section.rawValue)
+                if filteredSections.isEmpty {
+                    Text(String(localized: "settings.search.noResults", defaultValue: "No Results"))
+                        .foregroundStyle(.secondary)
+                } else {
+                    ForEach(filteredSections) { section in
+                        Label(section.title, systemImage: section.symbolName)
+                            .tag(section.rawValue)
+                    }
                 }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsShellView.swift`
around lines 22 - 27, When filteredSections is empty the sidebar shows nothing;
update the List in SettingsShellView to display a localized "No Results" row
instead of rendering nothing: check filteredSections.isEmpty and, when true, add
a single Label row (e.g. using Label(NSLocalizedString("No Results", comment:
""), systemImage: "magnifyingglass") or a localized string key) that is not
selectable (doesn't modify selectedSectionRaw) so users see an explicit
empty-search state; keep the existing ForEach(section in filteredSections) path
unchanged for non-empty results.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/AppDelegate.swift`:
- Around line 6601-6614: presentPreferencesWindow currently calls
SettingsWindowPresenter.show(navigationTarget:) which forwards to openWindow?()
without checking existing windows; update show() (and/or
presentPreferencesWindow) to first search for an existing Settings window
instance (by window title, windowScene identifier, or a stored window identifier
used by the Settings WindowGroup), and if found: if window.isMiniaturized call
window.deminiaturize(nil) and then call
window.makeKeyAndOrderFront(nil)/orderFrontRegardless (or
NSRunningApplication.current.activate with .activateAllWindows) to re-raise it
instead of opening a new window; only call openWindow?() to create a new
settings window when no existing window is found. Ensure changes touch
SettingsWindowPresenter.show and presentPreferencesWindow (and any stored
identifier used by the WindowGroup) so repeated calls deduplicate and re-front
minimized windows.

In `@Sources/cmuxApp.swift`:
- Around line 2741-2751: The show(navigationTarget:) function leaves stale
pendingNavigationTarget when called without a target; change it so that when
navigationTarget is non-nil you set pendingNavigationTarget = navigationTarget,
and when navigationTarget is nil you explicitly clear pendingNavigationTarget
(set to nil) before calling openWindow, then only call
SettingsNavigationRequest.post(navigationTarget) when navigationTarget is
non-nil; update the logic around pendingNavigationTarget, openWindow, and
SettingsNavigationRequest.post to perform the clear-first-or-set behavior.

---

Outside diff comments:
In `@Sources/cmuxApp.swift`:
- Around line 1129-1204: Replace all bare user-facing string literals in the
shown enums and related UI code with localized lookups using String(localized:
"key.name", defaultValue: "English text"); specifically update
TitlebarVisibilityOption.displayTitle ("Hidden", "Visible"),
TitlebarToolbarStyleOption.displayTitle (all case titles), the About-related
properties displayTitle and fallbackTitle ("About Window", "About cmux"), and
any other new UI labels like "Reset All" and "Apply Now" to use
String(localized:..., defaultValue:...); add corresponding keys to
Resources/Localizable.xcstrings for English and Japanese and keep the same
defaultValue English text so localized builds pick up translations.

---

Duplicate comments:
In `@Sources/cmuxApp.swift`:
- Around line 2502-2526: The hard-coded English keyword corpus in the searchText
computed property (and the similar block around lines 2596-2668) must be
localized: replace each literal return string in the switch cases (e.g., cases
.account, .app, .terminal, .workspaceColors, .sidebarAppearance, .automation,
.browser, .browserImport, .globalHotkey, .keyboardShortcuts, .reset) with calls
to String(localized: "sidebar.search.<case>.keywords", defaultValue: "English
keywords…") using unique keys added to Resources/Localizable.xcstrings; keep the
title interpolation (e.g., "\(title) …") but localize the rest of the keyword
text so Japanese builds get translated synonyms.
- Around line 8451-8457: When Settings appears and selectedSectionRaw restored
to something other than .account, the code only sets selectedSidebarEntryID and
never sends a navigation request to the SettingsView; update the .onAppear block
so that when there's no pending navigation target AND selectedSectionRaw (or
selectedSection) is not .account you explicitly call navigate(to: ...,
postRequest: true) with a Settings navigation target (use the same target shape
used elsewhere, e.g. the enum/case passed into navigate(to:postRequest:)) or
obtain a target from SettingsWindowPresenter and pass it into
navigate(to:postRequest:) so the detail SettingsView receives the navigation
request and restores its scroll/state.
- Around line 743-749: The Settings WindowGroup (the one with
SettingsWindowPresenter.windowID and SettingsRootView) currently uses
.defaultSize which only affects the first presentation; instead locate the
actual NSWindow for that window ID when the scene appears (e.g., in an .onAppear
or .task attached to the WindowGroup / SettingsRootView) and set its minSize to
CGSize(width: 980, height: 680) (and optionally maxSize if needed) so the split
view cannot be resized below the intended two-column layout; ensure you
reference SettingsWindowPresenter.windowID when finding the NSWindow and apply
the minSize change there.

---

Nitpick comments:
In
`@Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsShellView.swift`:
- Around line 22-27: When filteredSections is empty the sidebar shows nothing;
update the List in SettingsShellView to display a localized "No Results" row
instead of rendering nothing: check filteredSections.isEmpty and, when true, add
a single Label row (e.g. using Label(NSLocalizedString("No Results", comment:
""), systemImage: "magnifyingglass") or a localized string key) that is not
selectable (doesn't modify selectedSectionRaw) so users see an explicit
empty-search state; keep the existing ForEach(section in filteredSections) path
unchanged for non-empty results.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 73a23c29-b54d-4dd1-b60e-9b98b751c1a0

📥 Commits

Reviewing files that changed from the base of the PR and between d440142 and 10a792d.

📒 Files selected for processing (6)
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/App/SettingsShellLabApp.swift
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/Views/SettingsShellView.swift
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate.swift
  • Sources/TerminalController.swift
  • Sources/cmuxApp.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • Prototypes/SettingsShellLab/Sources/SettingsShellLab/App/SettingsShellLabApp.swift
  • Sources/TerminalController.swift

Comment thread Sources/AppDelegate.swift
Comment thread Sources/cmuxApp.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10a792d1d4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/cmuxApp.swift Outdated
}
}

WindowGroup(String(localized: "settings.title", defaultValue: "Settings"), id: SettingsWindowPresenter.windowID) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Replace Settings WindowGroup with singleton Window scene

Defining Settings as a WindowGroup allows repeated openWindow(id:) calls to create additional Settings windows, which regresses the previous singleton behavior and makes global navigation broadcasts (SettingsNavigationRequest.post) affect every open Settings window at once. This is user-visible when opening Settings multiple times from menu/shortcuts or socket commands, because section selection and scroll routing no longer target a single canonical window.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: the Settings scene is now Window rather than WindowGroup, so repeated openWindow calls target one Settings window.

— Claude Code

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec7ca8ff8a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/SettingsNavigation.swift Outdated
Comment on lines +204 to +205
TimelineView(.animation) { context in
let opacity = highlightOpacity(at: context.date, for: highlightState)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Render highlight animation only when a row is active

Gate the TimelineView(.animation) behind an active highlight check, because this modifier is attached to nearly every searchable row/section and currently creates a continuously animating timeline even when highlightOpacity is 0. In practice, simply opening Settings with no active search highlight still drives periodic redraw work across the full settings tree, which can cause unnecessary CPU/GPU usage and UI jank in this window.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: the highlight modifier now creates TimelineView only for the active matching search highlight row.

— Claude Code

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 440d5cce94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/cmuxApp.swift Outdated
Comment on lines +2514 to +2516
NSRunningApplication.current.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
window.makeKeyAndOrderFront(nil)
window.orderFrontRegardless()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Honor non-activating settings.open requests

settings.open is routed through the non-activating path when activate is false (v2SettingsOpen calls SettingsWindowPresenter.show), but focus(_:) now unconditionally calls NSRunningApplication.current.activate(...) and orderFrontRegardless(). This makes background automation requests steal macOS focus whenever a Settings window already exists (visible or minimized), which breaks the command’s non-activation contract and the socket focus policy.

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Preview – cmux — 0e607d22 Deployed Apr 29, 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.

1 participant