Skip to content

Reimplement Settings as CmuxSettings/CmuxSettingsUI SPM packages (Claude) - #4975

Merged
azooz2003-bit merged 30 commits into
mainfrom
feat-spm-settings
May 29, 2026
Merged

azooz2003-bit merged 30 commits into
mainfrom
feat-spm-settings

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented May 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Reimplements cmux's Settings window as two new SPM packages — CmuxSettings (Foundation-only catalog + stores) and CmuxSettingsUI (SwiftUI shell + section views) — so new settings don't require touching 4–6 files in the app target.
  • Drives the package output to byte-for-byte parity with the legacy SettingsView in Sources/cmuxApp.swift — same chrome, same row order, same control types, same subtitles, same picker styles.
  • Reduces the host wiring to a thin SettingsHostActions shim that exposes external side effects (browser history clear, file-picker, restart, etc.).

What's in the diff

  • Packages/CmuxSettings/ — SettingKey, UserDefaultsSettingsStore, JSONConfigStore, value types, migration runner, full catalog.
  • Packages/CmuxSettingsUI/ — SettingsWindowScene, every section view, recorder, theme/icon pickers, chrome primitives, sidebar navigation.
  • Sources/HostSettingsActions.swift — the host implementation of the package's SettingsHostActions protocol.

Parity work (this commit)

Window shell: .defaultSize(980, 680), .contentMinSize, localized title, SidebarCommands, .toggleStyle(.switch) on the scroll so every Toggle renders as a switch, DispatchQueue.main.async scroll-to-section.

Theme + App Icon: ported ThemeWindowThumbnail, ThemePickerRow, AppIconPickerRow from legacy; dropped the placeholder AppIconGridPicker.

Account: identity card matches legacy AuthSettingsRow — no avatar, title = primary email, subtitle = display name, plain Sign Out, PII redaction preserved.

App: File Drops uses legacy displayName / settingsSubtitle; Notification Sound custom path uses NSOpenPanel; Telemetry shows "Change takes effect on next launch." when the value drifts; AccountTeamPicker localized.

Terminal: removed fabricated Resume Commands TextEditor card; threaded hostActions through the section.

Automation: SocketControlMode.uiCases/.displayName/.description ported from Sources/SocketControlSettings.swift; password row shows save/clear/error status.

Browser: Memory Saver Delay reads Xm Ys; Browsing History subtitle is a dynamic count; Clear is disabled at 0; Refresh button + footnote restored in import block; added SettingsHostActions.browserHistoryEntryCount() with a default nil extension.

Global Hotkey: recorder persists chords to shortcuts.bindings["showHideAllWindows"]; streams via JSONConfigStore.values(for:); Reset button matches legacy.

Keyboard Shortcuts: filtered out .showHideAllWindows (Global Hotkey owns it); dropped per-row rawValue caption + extra chord toggle button; X / restore SF Symbol icon button replaces text Reset / Clear.

Reset: fires immediately, no confirmation dialog.

Workspace Colors: palette source switched from JSON overrides to catalog.workspaceColors.palette; built-ins always render with default hexes; Base: <hex> subtitle for built-ins; Remove gated to custom entries.

Test plan

  • Build the tagged DEV app: ./scripts/reload.sh --tag spm-settings.
  • Open Settings. Window opens at 980×680 and is resizable.
  • Every Toggle in the page renders as a macOS switch (not a checkbox).
  • App section → Notification Sound → Custom File: Choose… opens an NSOpenPanel; the selected file's basename appears truncated.
  • Account section: top row shows your email (medium) above your display name (caption), no avatar.
  • Automation → Socket Control Mode reads "Off / cmux processes only / Automation mode / Password mode / Full open access" with the matching descriptions. Password row shows "Saved." after Set.
  • Global Hotkey: recording a chord persists and survives a Settings close + reopen.
  • Browser → Memory Saver Delay: stepping past 60s reads 1m, 1m 30s, etc. Browsing History subtitle reads the actual saved-page count.
  • Keyboard Shortcuts: rows show only the action name; each row has an SF Symbol X / restore icon button (no text Reset / Clear).
  • Workspace Colors: built-in palette entries show Base: <hex> subtitle and no Remove button. Custom entries do show Remove.
  • Reset: clicking Reset All Settings fires immediately, no confirmation dialog.

Generated with Claude Code


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


Summary by cubic

Reimplemented Settings as two Swift packages — CmuxSettings (typed stores) and CmuxSettingsUI (SwiftUI UI) — and switched the app to the new SettingsWindowScene with pixel-for-pixel parity. Also lowered CmuxSettingsUI to Swift tools 6.0 and added explicit Swift 6.1 fixes.

  • New Features

    • CmuxSettings: DefaultsKey/JSONKey, UserDefaultsSettingsStore and JSONConfigStore actors, JSONC sanitizer, file watcher, and a single SettingCatalog (~100+ entries).
    • CmuxSettingsUI: legacy-style window shell and chrome, curated settings search with aliases and deeplinks, bound sidebar collapse, reusable row primitives, inline error surfacing via SettingsErrorLog.
    • Host seams: SettingsRuntime for DI, SettingsHostActions (open config/editor, clear history, test/request notifications, restart, import, feedback, open Terminal Config), and AccountFlow for identity/team selection.
    • Keyboard shortcuts: bindings persisted in JSON (shortcuts.bindings), recorder supports two-stroke chords and named-key display, conflict hints, and reset (per-row and all).
    • Quality: unit tests for both packages + Settings XCUITests; localized settings error alerts; corrected defaults/encodings; JSON writes self-notify; recorder shows “None” when unbound.
    • Tooling: CmuxSettingsUI manifest dropped to Swift tools 6.0; explicit @MainActor/@preconcurrency annotations for Swift 6.1 CI; fixed a Swift 6.1 build issue by removing a nonisolated UserDefaults sync read in DefaultsValueModel (model now seeds from the key default until the observation stream updates); fixed a Swift 6.1 warning by dropping a @MainActor default argument in HostAccountFlow.init; rebased onto latest main with no functional changes; CI tests-build-and-lag timeout increased from 20 to 35 minutes to avoid cold-cache timeouts and warm the cache.
  • Migration

    • Replace @AppStorage(...) with @Setting(\.<catalog.path>); add new settings by declaring a key in the right *CatalogSection.
    • Route side effects through SettingsHostActions; construct and inject dependencies with SettingsRuntime (no singletons).

Written for commit 47d2e51. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • New strongly‑typed settings package and UI: unified Settings window with searchable sidebar, curated index, many settings sections (App, Terminal, Sidebar, Browser, Automation, Shortcuts, Account, Workspace Colors, Beta, Reset).
    • JSON config support with tolerant parsing, live file‑watching, migrations, and one‑click reset.
    • Shortcut recorder (single & chord), global hotkey controls, app icon/theme pickers, palette editor, account identity UI, and host actions (open config, import, preview sound, restart).
  • Documentation

    • Package README and docs with usage, examples, testing guidance, and concurrency policy.
  • Tests

    • New unit and UI test suites covering stores, codables, catalog, search index, and Settings UI behavior.

Review Change Stack

azooz2003-bit and others added 20 commits May 28, 2026 12:45
… target

Foundation for the settings system migration. Both packages compile into
cmux DEV.app and pass their own unit-test suites, but the running
Settings window is still the legacy `SettingsView`; the new
`SettingsWindowScene` is exported and wired but not yet substituted.
The cutover is deliberately a separate, later change.

CmuxSettings (Foundation only; 22/22 Swift Testing green)
- `DefaultsKey<V>` / `JSONKey<V>` — phantom-typed setting handles; each
  store accepts only its flavor, so wrong-store mismatches are compile
  errors, not runtime traps.
- `UserDefaultsSettingsStore` / `JSONConfigStore` — both `actor`s, no
  locks, no KVO, no `@Published`. Observation surfaces as
  `AsyncStream<V>`. UserDefaults observation via
  `NotificationCenter.default.notifications(named:)` async sequence;
  JSON observation via `JSONConfigFileWatcher` (DispatchSource hidden
  behind an `AsyncStream<Void>`).
- `SettingCatalog` + `SettingCatalogSection` — struct-based registry
  composed of sub-section structs by dotted-id prefix. `catalog.all`
  walks `Mirror` recursively so there is no parallel hand-maintained
  list; adding a key in any `*CatalogSection` shows up everywhere.
- ~107 catalog entries declared across nine sections (App, Terminal,
  Notifications, Sidebar, SidebarAppearance, WorkspaceColors,
  Automation, Browser, BetaFeatures, Shortcuts).
- Value types for every storable enum, plus `StoredShortcut` +
  `ShortcutStroke` + 77-case `ShortcutAction` enum with display
  names + group categorization.
- Legacy-key migration is type-validated: `AnySettingKey` captures the
  underlying `Value` in a closure so type-incompatible legacy values
  are skipped rather than silently coerced.

CmuxSettingsUI (depends on CmuxSettings; 5/5 Swift Testing green)
- Default-isolated to `MainActor` via `.defaultIsolation(MainActor.self)`
  (swift-tools-version 6.2).
- `@Observable @MainActor` view-models (`DefaultsValueModel<V>`,
  `JSONValueModel<V>`) bridge the actor stores to SwiftUI synchronous
  Bindings. No `deinit` cleanup needed; `[weak self]` inside the
  observation Task ends naturally on dealloc.
- Row primitives: `SettingsToggleRow`, `SettingsPickerRow<V>`,
  `SettingsTextFieldRow`, `SettingsStepperRow`,
  `ShortcutRecorderView` (NSViewRepresentable over a focusable NSView
  capturing keyDown with modifier flags).
- `SettingsWindowScene` exposes a `Scene` the app composes; renders
  `NavigationSplitView` with a searchable sidebar and detail-switched
  section views.
- 12 of 13 section views are real and bound to catalog keys; the
  remaining one (Account) is intentional out-of-scope until the auth
  storage moves out of `CMUXAuthCore`.
- `KeyboardShortcutsSection` lists all 77 actions in groups and reads
  / writes `[ActionID: StoredShortcut]` through the JSON store with
  the recorder primitive.

App-target integration
- `cmux.xcodeproj/project.pbxproj`: both packages added as
  `XCLocalSwiftPackageReference`s and linked into the `cmux` target's
  frameworks build phase.
- `cmux.xcworkspace`: both packages added to the `Packages` workspace
  group so they appear in the navigator.
- `Sources/AppSettingsRuntime.swift`: app-owned singleton holder that
  constructs one `SettingCatalog`, one `UserDefaultsSettingsStore`
  (running migrations against `UserDefaults.standard`), and one
  `JSONConfigStore` (against `~/.config/cmux/cmux.json`). The package
  itself has no shared statics; this file is the DI seam.

CLAUDE.md
- New "Package design discipline" section codifying the rules that
  emerged this session: no `static let standard` singletons in
  packages, no namespace-enums, no parallel hand-maintained
  registries, prefer compile-time invariants over runtime traps,
  file-scope `private func` vs `private static func`, nested types
  still count for one-type-per-file.
- New "Testability" section: every public type in `Packages/` must be
  testable without launching the app, without booting AppKit, and
  without depending on `UserDefaults.standard` or the user's
  filesystem. No global state in package code; DI via constructor.

What's still pending (so the diff for the next round is interpretable)
- Scene swap in `cmuxApp.swift` to present `SettingsWindowScene` in
  place of the legacy `SettingsView`. Held back until the explanatory
  sections (`Account`, `GlobalHotkey`) have real catalog-backed
  controls so the cutover does not visibly regress the running app.
- ~50–60 more catalog entries to reach JSON-path parity with
  `Sources/CmuxSettingsJSONPathSupport.swift`.
- ~200 `@AppStorage(...)` call sites across `Sources/` that still
  target legacy keys directly; the new catalog mirrors those keys
  exactly so the conversion is mechanical but bulky.
- Default-bindings table for `ShortcutAction` (mining from the legacy
  `KeyboardShortcutSettings.swift` defaults dictionary).
- Conflict detection and two-stroke chord recording in
  `ShortcutRecorderView`.
- Runtime bridge from `[String: StoredShortcut]` to the legacy
  `KeyboardShortcutSettings.shared` lookup so the app's keyboard
  event path consults the new persistence.
- `BetaFeaturesCatalogSection` is wired but its single `dock` flag is
  the only entry — additional beta flags add as they land.
- `AccountCatalogSection` does not exist yet; `AccountSection` renders
  an explanation pointing at `Sources/Auth/AuthSettingsStore.swift`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SettingsWindowRootView gated its body on a `shouldRenderSettingsContent`
flag driven by NSWindow notifications. `WindowAccessor` defaulted to
dedupe-by-window, so the second open of the same SwiftUI Window reused
the same NSWindow and the visibility handler was suppressed — leaving
the body stuck on the Color.clear placeholder. Removed the gate; the
content view now renders unconditionally and WindowAccessor is set to
`dedupeByWindow: false` so the presenter is wired every reopen.

Also wired the SettingsRuntime + SettingsErrorAlertModifier into both
the Settings and Config window scenes (sibling Window scenes don't
inherit environment values from the main WindowGroup), and hardened
the JSON config error path:

- JSONConfigStore.readFromDisk() now throws on corrupt / malformed
  JSON instead of silently returning [:]. mutateRoot() reads through
  the throwing path so writes refuse to overwrite a corrupt file
  (previously a stale-cache write would have destroyed user data).
  File-not-found is still treated as the empty state.
- KeyboardShortcutsSection now records write failures into the
  injected SettingsErrorLog instead of `try?`-swallowing them.
- DefaultsValueModel's asymmetry with JSONValueModel is documented
  (UserDefaults.set is non-throwing in Foundation; no error to route).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Converts the Right-Sidebar Dock beta toggle in `SettingsView` from
`@AppStorage(RightSidebarBetaFeatureSettings.dockEnabledKey)` to
`@Setting(\.betaFeatures.rightSidebarDock)`.

The UserDefaults key is unchanged (`rightSidebar.beta.dock.enabled`),
so existing user values round-trip. Reads now flow through
`UserDefaultsSettingsStore` -> `DefaultsValueModel`'s `AsyncStream`
projection, and writes go through the actor instead of synchronous
UserDefaults. Same Binding<Bool> shape via the wrapper's
`projectedValue`, so the existing `$rightSidebarDockEnabled` call
site at cmuxApp.swift:7149 keeps working unchanged.

This is the first concrete site exercising `SettingsRuntime` injection
into the Settings window environment, and the first time
`DefaultsValueModel` runs at app runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All flagged via the catalog's existing DefaultsKey entries; UserDefaults
keys are unchanged so existing user values round-trip. Storage now goes
through UserDefaultsSettingsStore actor + DefaultsValueModel.

App section: warnBeforeQuit, warnBeforeClosingTab,
warnBeforeClosingTabXButton, hideTabCloseButton,
renameSelectsExistingName, commandPaletteSearchesAllSurfaces,
workspaceInheritWorkingDirectory, keepWorkspaceOpenWhenClosingLastSurface,
focusPaneOnFirstClick, menuBarOnly, reorderOnNotification, iMessageMode.

Notifications: dockBadge, unreadPaneRing, paneFlash, showInMenuBar.

Terminal: showScrollBar, copyOnSelect, autoResumeAgentSessions,
agentHibernationEnabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…BrowserSection / AccountSection parity

Catalog additions:
- IntegrationsCatalogSection (claude/cursor/gemini hooks, claude path,
  ripgrep path, subagent notification suppression)
- SidebarCatalogSection: activeTabIndicatorStyle, selectionColorHex,
  notificationBadgeColorHex
- BrowserCatalogSection: disabled, importHintVariant, importHintDismissed

Section views built out to legacy parity (or close):
- AppSection: Appearance / Workspace Behavior / Command Palette /
  Quit-and-Close / Editor / File Handling / Workspace Presentation /
  Notifications / Telemetry subsections wired to ~25 catalog entries
- SidebarSection: full Material / BlendMode / State pickers, light +
  dark tint hex fields, blur opacity slider, Custom Colors subsection
- BrowserSection: disabled toggle, custom search engine fields,
  multiline hostname pattern editors for hosts/external/insecure
- AccountSection: real Form with integrations toggles + custom paths +
  notification suppression. Sign-in / team selection still in legacy

New row primitive:
- SettingsDefaultsTextFieldRow — sibling of SettingsTextFieldRow for
  UserDefaults-backed strings with optional subtitle

cmuxApp.swift @AppStorage -> @setting conversions:
- claudeCodeHooksEnabled, customClaudePath, customRipgrepPath,
  suppressSubagentNotifications, cursorHooksEnabled, geminiHooksEnabled,
  sendAnonymousTelemetry, preferredEditorCommand,
  openSupportedFilesInCmux, openMarkdownInCmuxViewer,
  browserSearchSuggestionsEnabled, browserDisabled,
  browserHiddenWebViewDiscardEnabled, browserHiddenWebViewDiscardDelay,
  isBrowserImportHintDismissed, showBrowserImportHintOnBlankTabs,
  reactGrabVersion, openTerminalLinksInCmuxBrowser

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catalog:
- AppCatalogSection: add systemWideHotkeyEnabled (key
  "systemWideHotkey.enabled") so the global-hotkey toggle has a home
- AnySettingKey: add resetInJSON closure so ResetSection can wipe
  JSON-backed entries in addition to UserDefaults overrides

Row primitives:
- SettingsDoubleStepperRow for bounded Double-typed catalog entries
  (hibernation idle seconds, etc.)

Section views built out:
- TerminalSection: idle-threshold stepper + multi-line Resume Commands
  editor backed by the JSON-config resumeCommands list
- NotificationsRows: add Notification Sound subsection with sound
  identifier, custom file path, and custom command rows
- BrowserImportSection: import-hint variant + dismissed toggle in
  addition to blank-tab toggle; explanatory copy for the actual flow
- GlobalHotkeySection: real enable toggle + pointer at Keyboard
  Shortcuts for the chord
- AutomationSection: portBase + portRange steppers added
- ResetSection: now resets both UserDefaults and JSON-backed catalog
  entries
- SettingsSearchIndex: route integrations.* under Account section,
  rightSidebar.beta.*/betaFeatures.* under Beta Features

Window swap:
- cmuxApp.swift Settings Window now hosts SettingsWindowRoot(runtime:)
  from the package. SettingsWindowPresenter wiring kept via outer
  WindowAccessor(dedupeByWindow: false). The legacy SettingsView /
  SettingsRootView / SettingsWindowRootView types remain in source for
  now (unreachable from the running UI) and will be deleted in a
  follow-up cleanup commit once the package UI has been dogfooded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CuratedSettingEntries: hand-curated table mirroring the legacy
SettingsSearchIndex.settingEntries + SettingsSearchAliasIndex tables.
~85 entries with user-facing titles plus the synonym strings the
legacy index supported (so typing "copy on select", "imessage", "kagi",
"react grab", etc. surfaces the right row).

SettingsSearchIndex now combines three sources:
  1. Section entries (one per SettingsSectionID, default sidebar view)
  2. Curated setting entries (high-quality titles + synonyms)
  3. Fallback dotted-id entries for catalog keys not yet curated

isCovered dedupes catalog keys that the curated table already covers,
so each setting surfaces exactly once.

SettingsWindowRoot subscribes to NotificationCenter
"cmux.settings.navigate" (the same name the legacy SettingsNavigationRequest
uses). Existing host-app deeplinks route the user to the correct
section automatically once the window swaps to the package UI. Anchor
scroll-to + highlight glow are intentionally not reproduced - each
section is its own pane in the split-view layout, so there is no
cross-section scroll.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per the package-design discipline rule against namespace-enums
("no `enum Foo { static func bar() }` posing as a namespace"), the
hand-curated search table is no longer hidden behind an enum.

- CuratedSettingEntry: the public struct (one type per file).
- CuratedSettingEntry+Default.swift: extension on
  [CuratedSettingEntry] exposing the cmux-shipped default table as
  `.cmuxDefault`.
- SettingsSearchIndex.init now takes
  `curatedEntries: [CuratedSettingEntry] = .cmuxDefault` so tests
  pass an empty or focused list and hosts can append their own
  entries without forking the package.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t defaults+conflicts

CmuxSettings:
- PIIDisplayMode value type
- AccountCatalogSection: piiDisplayMode, selectedTeamID, welcomeShown
- ShortcutAction+Defaults: every action's factory ShortcutStroke
  ported from the legacy KeyboardShortcutSettings.Action table

CmuxSettingsUI:
- AccountFlow protocol + AccountIdentity + AccountTeamSummary types.
  Host wraps its own auth surface in an AccountFlow and injects via
  SettingsRuntime; the package's AccountSection drives sign-in,
  sign-out, refresh, team selection, identity card, and the PII
  display mode without depending on CMUXAuthCore.
- AccountIdentityCard with avatar, redaction (PII display mode),
  Sign In / Sign Out / Refresh affordances.
- AccountTeamPicker bound to the flow's selected team.
- AccountSection rebuilt: Identity, Team, Privacy, Claude Code,
  Cursor, Gemini, Tools, Agent Notifications subsections.
- AppIconGridPicker: visual 3-tile grid mirroring legacy App Icon UI.
- WorkspaceColorsSection rebuilt: indicator + selection/badge color
  hex + custom palette array editor + palette overrides editor,
  observed and persisted through the JSON store with error-log
  surfacing on write failures.
- KeyboardShortcutsSection: shows the factory default in the
  recorder placeholder ("⌘N (default)"); flags real conflict between
  two actions resolving to the same first chord; Reset (per row) and
  Reset All (section footer).

cmuxApp.swift:
- HostAccountFlow wraps AuthManager + AuthSettingsStore behind the
  package's AccountFlow protocol.
- Settings runtime now injects accountFlow: HostAccountFlow() at app
  startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ation, restart, browser import, feedback

SettingsHostActions protocol surfaces seven host-side actions the
package's UI invokes:
  - clearBrowserHistory()
  - openConfigInExternalEditor()
  - sendFeedback()
  - sendTestNotification()
  - openSystemNotificationSettings()
  - restartApp()
  - openBrowserImportFlow()

Wired through SettingsRuntime alongside AccountFlow. Sections check
for nil before showing the corresponding button so previews / tests
don't need a host.

Section UI updates:
  - BrowserSection: "Clear Browser History" with confirmation dialog
  - SettingsJSONSection: "Open in External Editor" alongside the
    inline editor
  - AppSection: Onboarding "Welcome Shown" toggle (resurfaces the
    welcome flow on next launch), Feedback "Send Feedback…" button,
    language picker restart prompt that fires when the language
    changes
  - NotificationsRows: "Send Test" + "Open System Notification
    Settings…" buttons
  - BrowserImportSection: "Import Browser Data…" button
  - WorkspaceColorsSection: per-row Reset buttons for selection +
    notification badge color hex fields

cmuxApp wiring: HostSettingsActions routes to BrowserHistoryStore,
NSWorkspace, TerminalNotificationStore, BrowserDataImportCoordinator,
and the GitHub issues URL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the four pbxproj entries each for HostAccountFlow.swift and
HostSettingsActions.swift so xcodebuild picks them up. The normalize
script ran clean and check-pbxproj.sh passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ShortcutRecorderView: opt-in chord mode captures two consecutive
strokes and yields a chorded StoredShortcut via a new onChord
callback. Escape during chord-pending aborts cleanly. Single-stroke
recording is unchanged.

KeyboardShortcutsSection: each row exposes a per-row "chord" toggle
button next to the recorder. While the toggle is on, the next two
key strokes are captured as a chord (tmux-style); after the chord
commits, the toggle automatically resets to single-stroke mode.

SettingsHostActions: adds requestNotificationAuthorization() so the
package's NotificationsRows can offer a "Request Permission" button
alongside the existing "Open System Notification Settings" and
"Send Test" affordances.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the NavigationSplitView + Form/Section chrome with cmux's
legacy in-app layout — one tall ScrollView of stacked sections, each
section as a SettingsSectionHeader + SettingsCard pair. Sidebar is a
List that scrolls the proxy to the section anchor on click. Mirrors
what the user sees today and resolves the architectural mismatch
flagged in the dogfood pass.

Package additions (Chrome/ folder):
- SettingsSectionHeader (small secondary-colored title above a card)
- SettingsCard<Content> (13pt rounded grouped container with stroke)
- SettingsCardRow<Trailing> (title + optional subtitle + control;
  configurationReview slot for dotted-cmux.json-path metadata)
- SettingsCardDivider (1pt half-opacity separator)
- SettingsConfigurationReview (.json("..."), .action, .settingsOnly,
  .debugOnly enum mirroring the legacy review metadata)

All 13 sections rewritten as VStack(SettingsSectionHeader,
SettingsCard) groups. Helper functions per section (toggleRow,
textRow, pickerRow, stepperRow, sliderRow) keep call sites tight.
Behavior is unchanged; chrome now matches legacy exactly.

SettingsWindowRoot scrolls to the selected section's anchor (`.id`)
in the LazyVStack, animating with easeInOut. Search via the
sidebar's `.searchable` placement; deeplinks via the
`cmux.settings.navigate` notification still route to the right
section as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves the integration toggles (Claude Code / Cursor / Gemini /
ripgrep / subagent suppression) out of Account and into Automation
to match legacy ordering. AccountSection is now just
SettingsSectionHeader + SettingsCard wrapping AccountIdentityCard.

Adds SettingsCardNote primitive (caption-style note rendered inside a
card) ported from legacy chrome.

Reorders sectionStack in SettingsWindowRoot to match legacy:
account, app, terminal, sidebar, beta, automation, browser,
browserImport, globalHotkey, keyboardShortcuts, workspaceColors,
settingsJSON, reset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Keyboard Shortcuts, Reset

Each section now mirrors the legacy SettingsView body row-for-row
with the exact localized strings + accessibility identifiers + card
structure ported from cmuxApp.swift:

- AutomationSection: 8 cards (Socket Control with conditional
  password subrow + warnings, Claude Code, Claude Path, Ripgrep
  Path, Suppress Subagent, Cursor, Gemini, Port Base/Range) with
  exact SettingsCardNote copy
- TerminalSection: scroll bar, textBoxMaxLines, copy on selection,
  auto resume, hibernation, idle seconds, max live terminals — all
  with the legacy dynamic subtitles
- SidebarSection: match terminal + 18 sidebar detail toggles with
  the legacy conditional disabled() chain (hideAllDetails / PR
  visibility / PR clickability gating)
- BetaFeaturesSection: dock toggle + warning note (ported
  BetaFeaturesWarningNote chrome)
- GlobalHotkeySection: enable toggle + recorder + caption note
- KeyboardShortcutsSection: chord docs link + Reset Defaults +
  per-action recorder rows with chord-mode toggle
- ResetSection: centered Reset All Settings button +
  confirmation dialog
- AccountSection trimmed to identity card only (legacy lives in
  one card with AuthSettingsRow)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final parity pass with the legacy SettingsView body. Every row in
every section now uses the exact String(localized:defaultValue:)
literals, accessibility identifiers, controlWidth values, divider
placement, and (where applicable) conditional disabled() / hidden
branches from the legacy implementation.

AppSection: single SettingsCard containing 30 rows in legacy order —
Language (with restart subtitle), Appearance, App Icon, New
Workspace Placement, Inherit Working Directory, Minimal Mode, Keep
Workspace Open When Closing Last Surface, Focus Pane on First Click,
File Drops, Open Files With, Open Supported Files in cmux, Open
Markdown in cmux Viewer, iMessage Mode, Reorder on Notification,
Dock Badge, Menu Bar Only, Show in Menu Bar (disabled when
menuBarOnly), Unread Pane Ring, Pane Flash, Desktop Notifications
(host actions), Notification Sound, Notification Command, Send
anonymous telemetry, Warn Before Quit (segmented), Warn Before
Closing Tab, Warn Before Tab Close Button (disabled when hide), Hide
Tab Close Button, Rename Selects Existing Name, Command Palette
Searches All Surfaces.

BrowserSection: single SettingsCard with Enable cmux Browser,
Default Search Engine (custom subrows conditional), Show Search
Suggestions, Browser Theme, Memory Saver + Delay, Open Terminal
Links + Intercept open, conditional Hosts / External Patterns text
editors, HTTP Allowlist editor with hint, embedded Import Browser
Data block, React Grab Version, Browsing History (host action).

BrowserImportSection collapsed to an empty deeplink anchor since the
legacy renders import inline inside the Browser card.

WorkspaceColorsSection: indicator-style picker, selection +
notification badge color rows with ColorPicker + Reset, palette
note, per-entry editor with ColorPicker + Remove, Reset Palette
action.

SettingsJSONSection: User config file row with display path + Open
button, Documentation row with Open Docs link.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ization

Catalog encoding alignment with legacy on-disk format (BLOCKERs from
iter 7):
- sidebar.branchVerticalLayout is Bool-backed (true=vertical) matching
  legacy sidebarBranchVerticalLayout key. Removed dead
  SidebarBranchLayout enum.
- FileDropDefaultBehavior cases changed to .text + .preview (drop
  .path / .editor) matching legacy on-disk rawValue. Default flipped
  from .path to .text.

AppSection: added Terminal Config row (Open Config button) via new
SettingsHostActions.openTerminalConfigWindow(); rebuilt Notification
Sound row to match legacy — Picker over NSSound system names +
Preview button + conditional custom-file path/Clear when "custom".

BrowserSection: added Save button + draftState to HTTP Allowlist
editor (Save disabled when no unsaved changes), Browser Theme
subtitle interpolates mode (system/light/dark), added
SettingsHostActions.previewNotificationSound() seam.

AutomationSection: Socket Mode Picker iterates SocketControlMode
.allCases via socketModeLabel(_:) helper (matching legacy
displayName routing).

AccountSection / AccountIdentityCard: section header now localized,
all visible identity strings routed through String(localized:); Sign
In/Sign Out buttons honor isWorkingOnAuth as disabled state; dropped
Refresh button (not present in legacy).

KeyboardShortcutsSection: added Open cmux.json button next to Chord
docs (matches legacy chord row affordances).

WorkspaceColorsSection: indicator Picker iterates
WorkspaceIndicatorStyle.allCases; selection/badge color rows show
"Default" sentinel when hex is empty (matching legacy nil sentinel).

BrowserImport navigation: removed BrowserImportSection's empty pane.
SettingsWindowRoot sidebar filters out .browserImport, and the
inline import block inside BrowserSection now carries the
`section:browserImport` anchor id so deeplinks scroll there.

Deleted dead-code section files: SidebarAppearanceSection,
PlaceholderSection, NotificationsRows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The sidebar's .searchable field was previously bound to a state but
not consumed. Now it filters the sidebar List in real time:
- "Sections" group: section rows whose title or section keywords
  contain every query token (case- and diacritic-insensitive).
- "Settings" group: curated entries whose title or synonym string
  match; each renders as a button that selects the parent section.
- "No results" placeholder when both groups are empty.

Updated stale catalog-key synonym (sidebar.branchLayout →
sidebar.branchVerticalLayout) in CuratedSettingEntry+Default so
searches for "branch layout" hit the right entry, and so the
SettingsSearchIndex's isCovered() dedupe correctly recognizes the
catalog key (was producing a duplicate raw dotted-id entry).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The legacy SettingsView body wraps everything in:
- NavigationSplitView with sidebar.navigationSplitViewColumnWidth(210)
- ScrollView -> VStack(spacing: 14) -> all headers + cards flat
- .padding(.horizontal, 20) .padding(.bottom, 20) .padding(.top, 20)
- Sidebar entries via SettingsSidebarEntryRow (16pt icon + 1-line
  title + optional 1-line caption subtitle)

The package previously rendered each section wrapped in its own
inner VStack(spacing: 14) and used `LazyVStack(spacing: 18)` +
padding (24, 22) at the root, which double-spaced sibling sections
and left visible chrome offsets vs the legacy. All 12 section views
now emit content via a flat `Group { … }` so the root VStack lays
out every SettingsSectionHeader + SettingsCard pair at 14pt
spacing, matching the legacy exactly.

Sidebar restored to show every SettingsSectionID (Browser Import
included, matching legacy `SettingsNavigationTarget.allCases`).
Selecting Browser Import scrolls to the inline import block inside
BrowserSection via the section:browserImport anchor id.

Added SettingsSidebarEntryRow chrome (icon + 16pt slot + 1-line
title + caption secondary subtitle) ported byte-for-byte from
Sources/cmuxApp.swift:9119-9142.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the legacy in-app SettingsView controls into the CmuxSettingsUI
package row-by-row so the new SPM-backed window matches the stable cmux
build pixel-for-pixel.

Window shell
- defaultSize(980,680), .contentMinSize, localized title, SidebarCommands
- .toggleStyle(.switch) on the scroll container so every Toggle renders
  as a macOS switch
- DispatchQueue.main.async scroll-to-section (no animation) to match legacy

Theme + App Icon pickers
- ThemeWindowThumbnail + ThemePickerRow + AppIconPickerRow ported from
  legacy; AppIconGridPicker removed

Account
- AccountIdentityCard rewritten as the legacy AuthSettingsRow shape:
  no avatar, title = primary email, subtitle = display name, plain Sign
  Out button, padding 14h/10v, PII redaction preserved

App
- File Drops uses legacy displayName / settingsSubtitle
- Notification Sound custom path uses NSOpenPanel Choose... with
  truncated last-path-component preview
- Telemetry shows "Change takes effect on next launch." when toggled
  from launch value
- AccountTeamPicker strings localized

Terminal
- Removed fabricated Resume Commands TextEditor card
- Threaded hostActions through TerminalSection

Automation
- SocketControlMode.uiCases + .displayName + .description ported from
  Sources/SocketControlSettings.swift
- Password row shows save / clear / error status text under the field

Browser
- Memory Saver Delay label uses Xm Ys format
- Browsing History subtitle is dynamic count; Clear disabled at 0
- Refresh button + footnote restored in import block
- Added SettingsHostActions.browserHistoryEntryCount() (default nil)

Global Hotkey
- Recorder now persists chords to shortcuts.bindings["showHideAllWindows"]
- Streams updates back via JSONConfigStore.values(for:)
- Reset button matches legacy

Keyboard Shortcuts
- Filtered out .showHideAllWindows (Global Hotkey owns it)
- Dropped per-row rawValue caption + extra chord toggle button
- X / restore SF-Symbol icon button replaces text Reset / Clear

Reset
- Fires immediately, no confirmation dialog (matches legacy)

Workspace Colors
- Palette source switched from JSON to catalog.workspaceColors.palette
- Built-in palette always renders with default hexes
- Base: <hex> subtitle for built-ins; Remove gated to custom entries

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel

vercel Bot commented May 29, 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 May 29, 2026 8:45pm
cmux-staging Building Building Preview, Comment May 29, 2026 8:45pm

@coderabbitai

coderabbitai Bot commented May 29, 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

</review_stack_artifact>

Walkthrough

Adds two new packages (CmuxSettings + CmuxSettingsUI) providing typed setting contracts (SettingCodable, DefaultsKey/JSONKey), actor-backed stores (UserDefaultsSettingsStore, JSONConfigStore with JSONC sanitizer and file watcher), UI bindings/view-models, many SwiftUI settings sections/components, host integrations, app wiring, tests, and project/package manifests.

Changes

Cmux Settings + UI

Layer / File(s) Summary
All changes (single checkpoint)
multiple files under Packages/CmuxSettings, Packages/CmuxSettingsUI, Sources/*, cmux.xcodeproj, cmux.xcworkspace
Adds typed settings protocol and coders, concrete SettingCodable conformances, DefaultsKey/JSONKey and SettingCatalog, AnySettingKey, JSONPath, JSONC sanitizer, JSON file watcher, JSONConfigStore and UserDefaultsSettingsStore actors, many catalog sections and value types, UI bindings (DefaultsValueModel/JSONValueModel/Setting property wrapper), dozens of SwiftUI components/sections for Settings, host-side adapters (HostAccountFlow, HostSettingsActions), test suites, package manifests, and Xcode workspace/project wiring.

Sequence Diagram(s)

sequenceDiagram
  participant SettingsWindowRoot
  participant SettingsRuntime
  participant DefaultsStore as UserDefaultsSettingsStore
  participant JSONStore as JSONConfigStore
  participant FileWatcher as JSONConfigFileWatcher
  SettingsWindowRoot->>SettingsRuntime: read injected runtime
  SettingsWindowRoot->>DefaultsStore: DefaultsValueModel / Setting reads
  SettingsWindowRoot->>JSONStore: JSONValueModel / Setting reads
  FileWatcher->>JSONStore: file change event
  JSONStore->>SettingsWindowRoot: AsyncStream<Value> updates
  DefaultsStore->>SettingsWindowRoot: AsyncStream<Value> updates
Loading

Estimated code review effort:
🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs:

"A rabbit scampers, keys in paw,
New settings sprout without a flaw,
Actors hum and streams float by,
UI petals open wide and shy.
Hooray — the settings garden grows 🥕"

✨ 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-spm-settings

…polish (Claude)

Final parity-loop passes over the package:

- SettingsWindowScene: bind NavigationSplitView columnVisibility so the
  sidebar chevron / SidebarCommands toggle works; resolve sidebar
  selection through SettingsSearchIndex entries instead of re-parsing
  the id string, matching legacy SettingsRootView.selectSidebarEntry
- GlobalHotkeySection + KeyboardShortcutsSection: render named keys
  (Tab, Space, return, media keys) via a keyDisplayString helper that
  mirrors legacy ShortcutStroke.keyDisplayString
- AppSection + BrowserSection: column-width and delay-bound alignment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously requested changes May 29, 2026

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

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

Inline comments:
In `@CLAUDE.md`:
- Line 302: Typo: change the single-word "runloop" to the conventional two-word
spelling "run loop" in the sentence mentioning AsyncStream and
NotificationCenter; update the occurrence near the phrase "Avoid
`NotificationCenter`-only patterns where the test has to spin a runloop" to read
"spin a run loop", referencing the terms `AsyncStream` and `NotificationCenter`
so it's easy to locate.

In `@Packages/CmuxSettings/README.md`:
- Line 146: Add blank lines before and after fenced Swift code blocks in the
README to satisfy markdown conventions: ensure there is an empty line
immediately above the opening ```swift fence and an empty line immediately below
the closing ``` fence for the example that defines FooCatalogSection and the
subsequent snippet; update the README so the FooCatalogSection example
(references: FooCatalogSection, SettingCatalogSection, DefaultsKey) and the
follow-up instruction to add `public let foo = FooCatalogSection()` to
SettingCatalog are each surrounded by blank lines around their fenced code
blocks.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Codable/Bool`+SettingCodable.swift:
- Around line 4-21: Add Swift-DocC triple-slash documentation comments for the
newly added public methods so every public symbol is documented: add concise ///
comments above Bool+SettingCodable.decodeFromUserDefaults(_:),
Bool+SettingCodable.encodeForUserDefaults(),
Bool+SettingCodable.decodeFromJSON(_:), and Bool+SettingCodable.encodeForJSON()
describing purpose, parameters (e.g. the raw Any? input), return values (when it
returns a Bool or nil, or what type is encoded), and any important behavior
(e.g. CFBoolean detection for NSNumber), matching the package documentation
guideline for public symbols.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Codable/Int`+SettingCodable.swift:
- Around line 9-15: The decodeFromUserDefaults implementation must reject
boolean-backed values before allowing an Int conversion: first check for Bool
(raw as? Bool) and for NSNumber whose CFGetTypeID equals CFBooleanGetTypeID()
and return nil for those cases; only after rejecting booleans, perform the Int
extraction (keep the existing decodeFromUserDefaults function name:
decodeFromUserDefaults(_:), use the Int cast and NSNumber.intValue branches) so
BOOL-backed NSNumbers cannot slip through as 1/0.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Codable/RawRepresentable`+SettingCodable.swift:
- Around line 19-29: The four public methods decodeFromUserDefaults(_:),
encodeForUserDefaults(), decodeFromJSON(_:), and encodeForJSON() in the
RawRepresentable+SettingCodable extension lack per-symbol Swift‑DocC comments;
add triple‑slash /// comments immediately above each public declaration that
include a brief description plus - Parameter and - Returns: tags (for the two
taking a raw Any? parameter and for the return values) so each public symbol is
individually documented according to the package guidelines.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Codable/String`+SettingCodable.swift:
- Around line 5-9: Add Swift-DocC triple-slash comments for each public symbol
in the String conformance: document public static func
decodeFromUserDefaults(_:), public func encodeForUserDefaults(), public static
func decodeFromJSON(_:), and public func encodeForJSON() with a brief one-line
description of purpose, expected input/output and any important behavior (e.g.,
returns nil if cast fails), using /// comments immediately above each
declaration.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Codable/URL`+SettingCodable.swift:
- Around line 6-18: Add Swift-DocC triple-slash comments to each new public
symbol in URL+SettingCodable.swift: add a brief summary, parameter description
and return description for decodeFromUserDefaults(_:), encodeForUserDefaults(),
decodeFromJSON(_:), and encodeForJSON(). For the two decode methods
(decodeFromUserDefaults and decodeFromJSON) document the raw parameter (Any?)
and that it returns an optional URL when the input is a String/valid URL, and
for the two encode methods (encodeForUserDefaults and encodeForJSON) document
that they return the URL as an absolute String suitable for user defaults or
JSON serialization respectively.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/AccountCatalogSection.swift`:
- Around line 12-28: Add Swift-DocC triple-slash comments for the three public
DefaultsKey symbols: piiDisplayMode, selectedTeamID, and welcomeShown in
AccountCatalogSection (or the file where they are declared). For each symbol add
a concise /// comment above the declaration describing what the setting
represents and its expected values or purpose (e.g., piiDisplayMode controls how
PII is shown, selectedTeamID stores the current team identifier, welcomeShown
flags whether onboarding was shown). Ensure comments are triple-slash DocC style
(///) and placed immediately above each public property.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift`:
- Around line 5-166: Add Swift-DocC triple-slash comments (///) above each
public DefaultsKey declaration (e.g., appearance, language, appIcon,
menuBarOnly, newWorkspacePlacement, workspaceInheritWorkingDirectory,
presentationMode, keepWorkspaceOpenWhenClosingLastSurface,
focusPaneOnFirstClick, preferredEditor, openSupportedFilesInCmux,
openMarkdownInCmuxViewer, iMessageMode, reorderOnNotification,
sendAnonymousTelemetry, confirmQuitMode, warnBeforeQuit, warnBeforeClosingTab,
warnBeforeClosingTabXButton, hideTabCloseButton, renameSelectsExistingName,
commandPaletteSearchesAllSurfaces, fileDropDefaultBehavior,
titlebarControlsStyle, workspaceButtonFade, workspaceTitlebarVisibility,
systemWideHotkeyEnabled) providing a one-line summary of purpose plus brief note
on valid values or enum cases (or range for Bool/String) and any important side
effects (e.g., UI behavior, persistence, feature toggles); ensure each comment
is immediate above the corresponding public let and uses /// DocC format.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Keys/AutomationCatalogSection.swift`:
- Around line 5-64: Add Swift-DocC triple-slash /// documentation comments for
every public symbol in AutomationCatalogSection: document socketControlMode,
socketPassword, claudeCodeIntegration, claudeBinaryPath, ripgrepBinaryPath,
suppressSubagentNotifications, cursorIntegration, geminiIntegration, portBase,
portRange and the public init() with a short description of purpose and any
important default behavior/units (e.g., portBase/portRange semantics, default
values where relevant) so each exported symbol has a DocC comment immediately
above its declaration.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/BrowserCatalogSection.swift`:
- Around line 5-105: Add Swift-DocC triple-slash comments (///) to each public
DefaultsKey property in BrowserCatalogSection explaining what the setting
controls and noting the default behavior/value; specifically document
defaultSearchEngine, customSearchEngineName, customSearchEngineURLTemplate,
showSearchSuggestions, theme, discardHiddenWebViews,
hiddenWebViewDiscardDelaySeconds, openTerminalLinksInCmuxBrowser,
interceptTerminalOpenCommandInCmuxBrowser, hostsToOpenInEmbeddedBrowser,
urlsToAlwaysOpenExternally, insecureHttpHostsAllowedInEmbeddedBrowser,
showImportHintOnBlankTabs, reactGrabVersion, disabled, importHintVariant, and
importHintDismissed with a concise one-line description and optional mention of
the defaultValue for clarity.
- Line 107: The public empty initializer public init() on BrowserCatalogSection
is undocumented; add a Swift-DocC triple-slash comment (///) immediately above
the initializer documenting its purpose and usage (e.g., that it creates a
default/empty BrowserCatalogSection), so the public symbol
BrowserCatalogSection.init() complies with the package guideline requiring
documentation for all public symbols.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Keys/NotificationsCatalogSection.swift`:
- Around line 5-57: Add Swift-DocC triple-slash comments to every exported
symbol in NotificationsCatalogSection: document dockBadge, showInMenuBar,
unreadPaneRing, paneFlash, sound, customSoundFilePath, command, hooks, hooksMode
and the public init() with a short purpose/behavior sentence each; place a
concise /// summary above each DefaultsKey/JSONKey declaration and above init()
(mention default values or expected format where helpful, e.g. that sound
defaults to "default", customSoundFilePath is a file path string, hooks is a
[String: String] JSON map and hooksMode accepts values like "merge"), keeping
each comment one line and using DocC style.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalogSection.swift`:
- Around line 36-46: SettingCatalogSection.all currently uses
Mirror(reflecting:) and recursively flattens children on every access (hot
path); change this to compute the flattened [AnySettingKey] once and reuse it —
e.g., add a cached stored property (or lazy var) on SettingCatalogSection that
runs the Mirror-based reflection once (or populate it in the section
initializer) and have the all computed property return that cached array; ensure
you reference the existing reflection logic (Mirror + AnySettingKeyConvertible
-> asAnySettingKey and nested SettingCatalogSection recursion) when populating
the cache and consider making SettingsSearchIndex or the catalog's searchIndex
use that cached array so catalog.all is not recomputed per keystroke.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Keys/SidebarAppearanceCatalogSection.swift`:
- Around line 5-71: Add Swift-DocC triple-slash comments for each public symbol
in this file: document matchTerminalBackground, tintColorHex,
lightModeTintColorHex, darkModeTintColorHex, tintOpacity, blurOpacity,
cornerRadius, preset, material, blendMode, state and the public init() with
concise descriptions of what each key represents, the expected value type, and
note the defaultValue (and any userDefaultsKey) where relevant; place the
comments immediately above each DefaultsKey declaration and above init() so
every public symbol has a /// Swift-DocC comment.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift`:
- Around line 5-50: Add Swift-DocC triple-slash comments for each public
DefaultsKey/JSONKey in TerminalCatalogSection: prepend a /// summary to
showScrollBar, copyOnSelect, autoResumeAgentSessions, agentHibernationEnabled,
agentHibernationIdleSeconds, agentHibernationMaxLiveTerminals, textBoxMaxLines,
and resumeCommands describing the purpose of the key, any valid range or units
(e.g., seconds for agentHibernationIdleSeconds, min/max for textBoxMaxLines and
agentHibernationMaxLiveTerminals), and any important behavior (defaults, effects
on runtime like autoResumeAgentSessions behavior or whether resumeCommands is a
list of shell commands). Ensure each comment is a single-line Swift-DocC
triple-slash (///) immediately above the corresponding declaration.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONPath.swift`:
- Around line 23-35: The doc comment incorrectly says "Trapping in debug builds
on empty input" but the implementation uses precondition which can trap in
non-debug builds; update the documentation near the JSONPath.init(dottedPath:)
initializer (and any comment referencing "trapping in debug builds") to
accurately state that a precondition enforces non-empty, well-formed dotted
paths and may terminate the program on violation (i.e., traps when the input is
empty or contains empty components), ensuring the doc matches the use of
precondition.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift`:
- Around line 178-179: The user-facing label for the enum case focusRightSidebar
is inconsistent; update the string returned for case .focusRightSidebar in
ShortcutAction (the switch that currently returns "Focus Right Sidebar") to the
repo-standard label "Toggle right sidebar focus" so it matches the established
mapping for ⌘⇧E and the other right-sidebar labels (e.g.,
switchRightSidebarToFiles).
- Around line 110-117: The switch in the computed property ShortcutAction.title
(and other user-facing labels in the same file — see ranges ~155-233) currently
returns hardcoded English literals; replace each literal with the localized API
call (e.g., use String(localized: "App", defaultValue: "App") or your project's
localized helper) and ensure corresponding entries are added to the string
catalog/translation keys for each label (e.g., "App", "Workspace", "Navigation",
"Panes", "Browser & Find" and any labels in 155-233). Update all occurrences in
the ShortcutAction enum (and related properties/methods) to use the localized
initializer rather than raw string literals so UI consumers receive localized
text.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction`+Defaults.swift:
- Line 1: Remove the unused Foundation import from the top of
ShortcutAction+Defaults.swift: the extension only references ShortcutStroke and
other types that don't require Foundation, so delete the line "import
Foundation" to clean up the file and avoid unnecessary imports.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutStroke.swift`:
- Around line 12-33: Add Swift-DocC triple-slash documentation for the public
stored properties and the initializer of ShortcutStroke: document the purpose of
each property (key, command, shift, option, control, keyCode) and clarify
modifier defaults/semantics, and add a /// comment on the public init
(ShortcutStroke.init) describing parameters and default values (including that
modifiers default to false and keyCode is optional). Keep comments concise and
follow existing project comment style.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Values/SidebarBlendModeOption.swift`:
- Around line 3-6: Add DocC triple-slash comments for the public enum
SidebarBlendModeOption and each of its cases (behindWindow, withinWindow)
describing their visual effect and how they map to
NSVisualEffectView.BlendingMode (e.g., behindWindow: effect appears behind
window chrome; withinWindow: effect is confined to window content), so tools and
readers understand the blending behavior; place concise /// comments above the
enum declaration and above each case using the exact symbol names
SidebarBlendModeOption, behindWindow, and withinWindow.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/StoredShortcut.swift`:
- Around line 40-42: The current encodeForUserDefaults implementation swallows
encoding errors by returning an empty Data() which causes silent data loss;
update StoredShortcut.encodeForUserDefaults to mirror encodeForJSON's behavior
by returning NSNull() on failure instead of Data(), i.e. replace the fallback
Data() with NSNull() (or otherwise return a non-Data sentinel used by
decodeFromUserDefaults), ensuring encodeForUserDefaults, encodeForJSON, and
decodeFromUserDefaults use the same sentinel semantics.

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Values/WorkspacePresentationMode.swift`:
- Around line 3-5: The public enum WorkspacePresentationMode currently has a
top-level doc but its cases lack per-case Swift-DocC comments; add triple-slash
(///) comments directly above the cases `standard` and `minimal` describing what
each mode does (e.g., what UI changes or density differences the `standard` and
`minimal` modes produce) so the public symbol `WorkspacePresentationMode` and
its cases are fully documented for Swift-DocC and tooling consumers.

In `@Packages/CmuxSettings/Tests/CmuxSettingsTests/JSONConfigStoreTests.swift`:
- Around line 84-99: The withTimeout helper ignores the timeout task's nil
result causing the for-await loop to wait forever if work never completes;
update the for-await loop inside withTimeout (the function named withTimeout) to
handle the timeout branch: when you receive a nil result from the group, call
group.cancelAll() and immediately abort with a clear timeout failure (e.g.,
fatalError or return a default/throw if you change the signature), and when you
receive a non-nil result cancel remaining tasks and return it—this ensures the
timeout task actually causes the function to stop waiting.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountIdentity.swift`:
- Around line 24-29: Add a Swift-DocC triple-slash comment for the public
initializer AccountIdentity.init(id:displayName:email:avatarURL:) — provide a
one-line summary and add `- Parameter` callouts for `id`, `displayName`,
`email`, and `avatarURL` (noting that `avatarURL` is optional) so the public
init matches the file's documentation standard and package guidelines.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountTeamSummary.swift`:
- Around line 7-16: Add Swift-DocC documentation for the public struct and its
members: add a triple-slash comment above the AccountTeamSummary declaration
describing its purpose, and add /// comments for the public properties id,
displayName, slug and for the public init(id:displayName:slug:) describing each
parameter and behavior; ensure the comments follow the existing package style
(brief summary sentence and parameter descriptions) so all public symbols
(AccountTeamSummary, id, displayName, slug, init) are documented.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/Setting.swift`:
- Around line 74-91: Add a Swift-DocC triple-slash comment above the public func
update() describing its role in the DynamicProperty conformance and the
pending→resolved transition: explain that update() checks if resolved is
.pending and a runtime exists, then resolves the binding to either .defaults or
.json by looking up the key in runtime.catalog and creating the appropriate
DefaultsValueModel or JSONValueModel (mentioning runtime.userDefaultsStore,
runtime.jsonStore, and runtime.errorLog). Keep the comment concise, include
behavior, side-effects, and any important invariants (e.g., only runs when
resolved is .pending), and attach it directly to the update() declaration.
- Around line 70-72: Add a Swift-DocC triple-slash comment for the public
property wrapper projectedValue to satisfy documentation guidelines: document
what projectedValue returns (a Binding<Value>), how clients use it (e.g.,
`$appearance` in UI bindings), and any semantics (gets/sets underlying
wrappedValue). Place the comment immediately above the projectedValue
declaration in Setting.swift and mention the returned type Binding<Value> and
relation to wrappedValue so the public API is clearly described.
- Around line 53-68: Add a Swift-DocC triple-slash comment to the public var
wrappedValue describing its getter and setter semantics (that getter returns
fallbackDefault while resolved == .pending and returns model.current for
.defaults/.json), and explicitly document the current pending-write behavior and
how callers should expect it to behave; then change the silent-drop behavior on
set when resolved == .pending by introducing a buffered optional (e.g.,
pendingWrite: Value?) that stores the newValue in the setter instead of
returning, and ensure the code path that transitions resolved from .pending to
.defaults/.json applies pendingWrite via the same model.set(...) used in the
.defaults/.json branches (clear pendingWrite after applying); reference
wrappedValue, resolved, .pending, fallbackDefault, model.set and the transition
code that resolves the wrapper so reviewers can locate and apply the
buffered-write application.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsCard.swift`:
- Around line 24-40: The public API lacks Swift-DocC comments: add triple-slash
/// documentation above SettingsCard's public init(`@ViewBuilder` content: () ->
Content) describing what the initializer does and the content parameter (that it
accepts a view builder for the card contents), and add /// docs above public var
body: some View describing what the body renders (a rounded, stroked background
card containing the provided content) and any important behavior; ensure the
comments are concise, use sentence-form descriptions and document
parameters/returns where appropriate so both symbols meet package documentation
requirements.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsCardDivider.swift`:
- Line 8: Add a Swift-DocC triple-slash comment for the public initializer of
SettingsCardDivider: locate the public init() in SettingsCardDivider (the public
init() constructor) and add a concise `///` DocC comment above it that describes
what the initializer does and any important usage notes so the public symbol is
documented per package guidelines.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsConfigurationReview.swift`:
- Around line 12-15: Add Swift-DocC triple-slash documentation for each public
enum case on SettingsConfigurationReview: document case settingsFile([String])
(explain it persists settings to a file path list), case settingsOnly (explain
it persists only settings), case action (explain it represents action-only
persistence mode), and case debugOnly (explain it enables persistence for
debugging only); ensure each case uses /// comments immediately above the case
and succinctly describes the persistence behavior and any important parameters
or side effects.
- Around line 17-19: The public convenience constructor json(_:) is missing a
Swift-DocC triple-slash comment; add a /// doc comment above public static func
json(_ paths: String...) in SettingsConfigurationReview.swift that explains it
constructs a .settingsFile using a variadic list of path components (i.e.,
convenience for creating a settingsFile from one or more path strings), include
brief usage notes or expected behavior and mark it as documentation for the
public API.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsSectionHeader.swift`:
- Around line 14-24: Add Swift-DocC triple‑slash comments for the public symbols
in SettingsSectionHeader: place a `///` comment above `public init(_ title:
String)` describing the initializer and its `title` parameter, and a `///`
comment above `public var body: some View` describing what the view renders (the
section header text) and that it returns a SwiftUI view; ensure the comments are
immediately above the `init(_:)` and `body` declarations in the
`SettingsSectionHeader` type so the package API docs are satisfied.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsRuntime.swift`:
- Around line 22-36: Document the public initializer SettingsRuntime.init by
adding a Swift-DocC triple-slash (///) comment immediately above the initializer
that briefly describes the initializer's purpose and then uses /// - Parameters:
to document each parameter (catalog: SettingCatalog, userDefaultsStore:
UserDefaultsSettingsStore, jsonStore: JSONConfigStore, errorLog:
SettingsErrorLog, accountFlow: AccountFlow? = nil, hostActions:
SettingsHostActions? = nil), including that accountFlow and hostActions are
optional and default to nil; ensure the comment is placed on the public init
signature and follows the package's doc style conventions.
- Around line 15-20: The six public stored properties on SettingsRuntime
(catalog, userDefaultsStore, jsonStore, errorLog, accountFlow, hostActions) are
missing Swift-DocC documentation; add triple-slash /// comments above each
public property in SettingsRuntime.swift describing their purpose, what they
provide to consumers, and any important behavior or lifetime expectations (e.g.,
"catalog: SettingCatalog — the schema and metadata for available settings.",
"userDefaultsStore: UserDefaultsSettingsStore — persistent user overrides backed
by UserDefaults.", "jsonStore: JSONConfigStore — disk-backed JSON configuration
for defaults or templates.", "errorLog: SettingsErrorLog — error reporting sink
for settings operations.", "accountFlow: AccountFlow? — optional coordinator for
account-related UI/actions.", "hostActions: SettingsHostActions? — optional host
callbacks for external integrations."); ensure comments are brief, use
triple-slash DocC style, and keep wording consistent with package documentation
guidelines.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Errors/SettingsErrorAlertModifier.swift`:
- Around line 17-18: The alert title and action label in
SettingsErrorAlertModifier must be localized: replace the hardcoded strings
"Couldn't save setting" and "OK" with the localized API (e.g., String(localized:
, defaultValue: )) and add corresponding entries to the app’s strings catalog;
update the Alert construction in SettingsErrorAlertModifier (the alert title and
the primary/dismiss button labels) to use those localized values so all
user-facing text follows the String(localized:defaultValue:) guideline.
- Around line 34-35: Replace the user-facing Text that currently interpolates
internal values ("Text(\"\\(entry.keyID): \\(entry.message)\")") with a
sanitized, localized user-safe message (e.g., a generic error title + brief
recovery hint) and stop displaying entry.keyID and raw entry.message in the
alert body; instead send the detailed internals (entry.keyID, entry.message,
stack/metadata) to your logging facility (or a debug-only console) from the same
modifier or the surrounding handler so diagnostics remain available but not
shown to users. Ensure you reference the existing SettingsErrorAlertModifier
(and the local variable/struct name entry) when making the change so only the
sanitized string is presented to users and internals are logged separately.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry.swift`:
- Around line 34-39: Add a Swift-DocC triple-slash doc comment above the public
initializer CuratedSettingEntry.init(section:id:title:synonyms:) describing
purpose and using `- Parameter` callouts for `section`, `id`, `title`, and
`synonyms` (and `- Returns:` or `- Throws:` only if applicable); ensure the
comment is placed immediately above the `public init(...)` declaration in
CuratedSettingEntry so the public symbol in the Packages/ Swift package is
properly documented per coding guidelines.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSectionID.swift`:
- Around line 30-86: The title and searchKeywords properties return hardcoded
English literals; update each case in the title and searchKeywords switches to
use the localized API (e.g., String(localized: , defaultValue:)) or your
project's localization wrapper instead of plain literals (refer to the title and
searchKeywords vars in SettingsSectionID), and add corresponding keys/entries
for every supported locale in the string catalog (ensure keys/variants match the
identifiers or comments used in the localized calls so translators can provide
translations).

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsTextFieldRow.swift`:
- Around line 45-51: The onChange handler on SettingsTextFieldRow currently
assigns draft = newValue whenever model.current changes, which overwrites user
edits; add a guard that ignores external updates while the user is actively
editing (e.g., add a FocusState<Bool> or an `@State` isEditing flag tied to the
TextField's onEditingChanged/ focused binding) and only set draft = newValue
when isEditing is false (or when the field loses focus), so external
file-watcher changes are debounced/gated until the user finishes editing;
reference the onChange(of: model.current) closure, the draft state, and the
TextField focus/editing callbacks to implement this behavior.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift`:
- Around line 72-83: Add Swift-DocC triple-slash documentation to all newly
public symbols: document the makeNSView(context:) and updateNSView(_:context:)
methods and any public properties on RecorderHostButton (e.g., placeholder,
chordsEnabled, onStroke, onChord, onBareKeyRejected) using /// comments that
include the required callouts such as - Parameters: (list and describe each
parameter), - Returns: (for makeNSView), and any - Throws: if applicable; ensure
the docs are complete and follow the package policy for public API coverage so
the package builds DocC successfully.
- Around line 38-39: The default user-facing placeholder string in
ShortcutRecorderView (the placeholder parameter/default and the inline Text
usage) must be localized; replace the literal "Click and press a shortcut" used
as the default value for the placeholder parameter and any direct Text("Click
and press a shortcut") uses with the localized API (e.g.,
String(localized:defaultValue:) or equivalent) and ensure the placeholder
property and any view binding (in ShortcutRecorderView) reference that localized
string so both the parameter default and the displayed text are localized.
- Around line 225-229: The media-key (.systemDefined) path is unreachable
because the monitor closure and handleRecordingEvent assume
charactersIgnoringModifiers is present; modify the eventMonitor handler (the
NSEvent.addLocalMonitorForEvents closure) and handleRecordingEvent to
special-case events with type == .systemDefined (and subtype indicating media
keys), parsing their key code from the system-defined event payload
(data1/data2) instead of charactersIgnoringModifiers, and ensure the closure
does not early-return when charactersIgnoringModifiers is nil for system events;
update handleRecordingEvent to accept/process systemDefined-derived key codes so
media keys are recorded.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AccountSection.swift`:
- Around line 15-26: Add Swift-DocC triple-slash comments for the public API:
document the public initializer
AccountSection.init(defaultsStore:catalog:accountFlow:) with a concise
description of what the initializer does and brief parameter docs for
defaultsStore, catalog, and accountFlow, and add a /// comment above the public
var body describing what the returned View represents; ensure both comments are
placed immediately above the respective declarations and use Swift-DocC style
(///) so they appear in generated documentation.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift`:
- Around line 523-556: The picker options in systemSoundOptions use hardcoded
English labels for "Default", "Custom File...", and "None" so
notificationSoundRow renders untranslated text; change those three tuples in
systemSoundOptions to use localized strings via String(localized:
"settings.notifications.sound.default", defaultValue: "Default"),
String(localized: "settings.notifications.sound.customFile", defaultValue:
"Custom File..."), and String(localized: "settings.notifications.sound.none",
defaultValue: "None") (keep the macOS sound names unchanged), update the
xcstrings entries for these three keys, and leave the Picker rendering
(Text(option.label)) and model bindings intact.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BetaFeaturesSection.swift`:
- Around line 30-44: The inline creation of DefaultsValueModel in dockRow (let
model = DefaultsValueModel(...)) means SwiftUI won't observe its `@Observable`
updates; change it to a stored observed state so model.current updates drive the
UI: introduce a private `@StateObject` (or private `@ObservedObject` if injected)
property like dockModel: DefaultsValueModel (initialize via init or
.init(wrappedValue:) for `@StateObject`) and replace uses of model.current and
model.set(...) in dockRow/Toggle with dockModel.current and dockModel.set(...)
so the view subscribes to DefaultsValueModel's async updates.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserImportSection.swift`:
- Around line 22-24: BrowserImportSection currently returns EmptyView(), so the
.browserImport target renders nothing and the deeplink handling never runs;
replace the EmptyView() in the public var body of BrowserImportSection with a
view that performs the deeplink behavior (for example a lightweight placeholder
VStack or ZStack that calls your deeplink handler in onAppear) and/or triggers
navigation to the import UI; locate the BrowserImportSection struct and its body
property and implement the appropriate view that invokes the existing deeplink
handling function (or call the import handling logic directly) instead of
returning EmptyView().

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/GlobalHotkeySection.swift`:
- Line 13: Add a Swift-DocC triple-slash comment above the public struct
GlobalHotkeySection describing its purpose and public API: a brief one-line
summary of what the view represents, notes about its behavior or configuration,
and any important usage details for consumers (e.g., what inputs/bindings it
expects or side effects). Place the comment immediately above the declaration of
GlobalHotkeySection so the public symbol is documented per package guidelines;
keep it concise and developer-facing.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/ResetSection.swift`:
- Around line 39-44: The resetAll function currently calls await
defaultsStore.resetAll(catalog.all) then iterates await
key.resetInJSON(jsonStore) without handling throws, risking partial failures;
update resetAll to handle errors from key.resetInJSON by either (a) catching
errors per-key, logging the failure (include key.identifier or similar) and
continuing so UserDefaults and JSON stay best-effort consistent, or (b)
collecting per-key errors into an array and throwing a composite error after the
loop so the caller can roll back or surface the failure. Locate resetAll,
defaultsStore.resetAll and key.resetInJSON(jsonStore) and implement one of these
error-handling strategies (per-key do/catch with logging or aggregate-and-throw)
so partial JSON resets are not silently left unhandled.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift`:
- Around line 48-49: The Text(verbatim: "0") in TerminalSection.swift is a
hardcoded placeholder for the resume commands count; either wire it to the real
value from your JSON store (e.g., replace Text(verbatim: "0") with something
like Text("\(viewModel.resumeCommands.count)") or
Text("\(store.resumeCommands.count)") after exposing the count via
TerminalSection's view model/initializer and ensuring the JSON store is decoded,
or if you cannot yet implement the plumbing add a clear TODO comment above
Text(verbatim: "0") explaining it's a deliberate placeholder until the dynamic
count (from the JSON store) is wired up; locate the Text(verbatim: "0")
occurrence in TerminalSection and update accordingly.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Values/SocketControlMode`+Display.swift:
- Around line 12-14: The static property uiCases is currently internal and
should be public so UI-facing labels are accessible outside the package; update
the declaration of uiCases on SocketControlMode (the static var uiCases) to
include the public access modifier (e.g., public static var uiCases = ...) so
the property is exported as part of the public API.
- Around line 17-30: The computed property displayName on SocketControlMode is
currently internal; make it publicly accessible by adding the explicit public
access modifier to the property declaration (change "var displayName: String" to
"public var displayName: String") so the localized labels (e.g.,
"socketControl.off.name", "socketControl.automation.name") can be used outside
the CmuxSettingsUI module.
- Around line 33-46: The computed property description on the SocketControlMode
extension is currently internal; make it public so callers outside the
CmuxSettingsUI module can access the localized descriptions by changing the
declaration to public var description: String in SocketControlMode+Display (the
computed property named description within the SocketControlMode extension).

In `@Sources/cmuxApp.swift`:
- Around line 37-53: Defer construction of the SettingsRuntime and its
dependencies until after the early-launch/CLI exit checks: move the block that
instantiates SettingCatalog, CmuxConfigLocation(), UserDefaultsSettingsStore,
JSONConfigStore, SettingsErrorLog, HostAccountFlow, HostSettingsActions and
finally SettingsRuntime so it runs only after
CLIForwardingLaunchRouter.forwardToBundledCLIIfNeeded() returns and after the
untagged DEV guard has been evaluated; locate the current initialization of
SettingsRuntime and replace it with a lazy/deferred initialization or relocate
it below the early-exit checks to avoid side effects on CLI-style invocations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3ce081b1-6107-4228-a7fd-0f8411c48677

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa6aef and b40a76e.

📒 Files selected for processing (118)
  • CLAUDE.md
  • Packages/CmuxSettings/Package.swift
  • Packages/CmuxSettings/README.md
  • Packages/CmuxSettings/Sources/CmuxSettings/CmuxSettings.docc/CmuxSettings.md
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/Array+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/Bool+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/Data+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/Dictionary+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/Double+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/Int+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/RawRepresentable+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/String+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Codable/URL+SettingCodable.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/AccountCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/AnySettingKey.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/AutomationCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/BetaFeaturesCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/BrowserCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/DefaultsKey.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/IntegrationsCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/JSONKey.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/KeyboardShortcutsCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/NotificationsCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalog.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/SidebarAppearanceCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/SidebarCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/WorkspaceColorsCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/CmuxConfigLocation.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONCSanitizer.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONConfigFileWatcher.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONConfigStore.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONConfigStoreReadError.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONPath.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/UserDefaultsSettingsStore.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/AppIconMode.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/AppLanguage.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/AppearanceMode.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/BrowserSearchEngine.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/BrowserThemeMode.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/ConfirmQuitMode.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/FileDropDefaultBehavior.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/PIIDisplayMode.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutStroke.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/SidebarBlendModeOption.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/SidebarMaterialOption.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/SidebarPresetOption.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/SidebarStateOption.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/SocketControlMode.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/StoredShortcut.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/WorkspaceIndicatorStyle.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/WorkspacePlacement.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/WorkspacePresentationMode.swift
  • Packages/CmuxSettings/Tests/CmuxSettingsTests/JSONConfigStoreTests.swift
  • Packages/CmuxSettings/Tests/CmuxSettingsTests/SettingCatalogTests.swift
  • Packages/CmuxSettings/Tests/CmuxSettingsTests/SettingCodableTests.swift
  • Packages/CmuxSettings/Tests/CmuxSettingsTests/UserDefaultsSettingsStoreTests.swift
  • Packages/CmuxSettingsUI/Package.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountFlow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountIdentity.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountIdentityCard.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountTeamPicker.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Account/AccountTeamSummary.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/DefaultsValueModel.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/JSONValueModel.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/Setting.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsCard.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsCardDivider.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsCardNote.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsCardRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsConfigurationReview.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Chrome/SettingsSectionHeader.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsHostActions.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsRuntime.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Errors/SettingsErrorAlertModifier.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Errors/SettingsErrorLog.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSectionID.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSidebarEntryRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/AppIconPickerRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsDefaultsTextFieldRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsDoubleStepperRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsPickerRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsStepperRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsTextFieldRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/SettingsToggleRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ThemePickerRow.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ThemeWindowThumbnail.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AccountSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AutomationSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BetaFeaturesSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserImportSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/GlobalHotkeySection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/ResetSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SettingsJSONSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/WorkspaceColorsSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Values/SocketControlMode+Display.swift
  • Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsSearchIndexTests.swift
  • Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsSectionIDTests.swift
  • Sources/Auth/HostAccountFlow.swift
  • Sources/HostSettingsActions.swift
  • Sources/cmuxApp.swift
  • cmux.xcodeproj/project.pbxproj
  • cmux.xcworkspace/contents.xcworkspacedata

Comment thread CLAUDE.md
- **No global state in package code.** Every public type that needs `UserDefaults`, `FileManager`, an on-disk path, an environment variable, or a clock takes it via initializer parameter. Tests pass a `UserDefaults(suiteName:)` scoped to the test, a temp directory URL, a fixed `Date`, etc.
- **No reliance on `.shared` / `.standard`.** A public type that hardcodes `UserDefaults.standard` or `FileManager.default` inside its implementation cannot be tested without polluting the developer's actual settings. Inject these at the seam.
- **Public APIs return values, not side effects, where possible.** A function that mutates global UserDefaults and returns `Void` is harder to test than one that returns the changed value and lets the caller persist. Prefer pure transformations + thin imperative layers.
- **Asynchronous APIs surface their observation as `AsyncStream`.** Tests can iterate `AsyncStream` deterministically and assert the sequence of yielded values. Avoid `NotificationCenter`-only patterns where the test has to spin a runloop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Minor: "runloop" should be two words.

"run loop" is the conventional spelling in Apple documentation and Cocoa terminology.

📝 Suggested fix
-Avoid `NotificationCenter`-only patterns where the test has to spin a runloop.
+Avoid `NotificationCenter`-only patterns where the test has to spin a run loop.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Asynchronous APIs surface their observation as `AsyncStream`.** Tests can iterate `AsyncStream` deterministically and assert the sequence of yielded values. Avoid `NotificationCenter`-only patterns where the test has to spin a runloop.
- **Asynchronous APIs surface their observation as `AsyncStream`.** Tests can iterate `AsyncStream` deterministically and assert the sequence of yielded values. Avoid `NotificationCenter`-only patterns where the test has to spin a run loop.
🧰 Tools
🪛 LanguageTool

[grammar] ~302-~302: Ensure spelling is correct
Context: ...y patterns where the test has to spin a runloop. - Document the test pattern alongs...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

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

In `@CLAUDE.md` at line 302, Typo: change the single-word "runloop" to the
conventional two-word spelling "run loop" in the sentence mentioning AsyncStream
and NotificationCenter; update the occurrence near the phrase "Avoid
`NotificationCenter`-only patterns where the test has to spin a runloop" to read
"spin a run loop", referencing the terms `AsyncStream` and `NotificationCenter`
so it's easy to locate.

Comment on lines +4 to +21
public static func decodeFromUserDefaults(_ raw: Any?) -> Bool? {
if let number = raw as? NSNumber, CFGetTypeID(number) == CFBooleanGetTypeID() {
return number.boolValue
}
if let bool = raw as? Bool { return bool }
return nil
}

public func encodeForUserDefaults() -> Any { self }

public static func decodeFromJSON(_ raw: Any?) -> Bool? {
guard let number = raw as? NSNumber, CFGetTypeID(number) == CFBooleanGetTypeID() else {
return nil
}
return number.boolValue
}

public func encodeForJSON() -> Any { self }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document the newly added public conformance methods.

Please add method-level /// docs for the public decode/encode APIs.

As per coding guidelines: Packages/**/*.swift: “Every public symbol in a Swift package under Packages/ must be documented with a Swift-DocC triple-slash /// comment at the time of writing.”

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

In `@Packages/CmuxSettings/Sources/CmuxSettings/Codable/Bool`+SettingCodable.swift
around lines 4 - 21, Add Swift-DocC triple-slash documentation comments for the
newly added public methods so every public symbol is documented: add concise ///
comments above Bool+SettingCodable.decodeFromUserDefaults(_:),
Bool+SettingCodable.encodeForUserDefaults(),
Bool+SettingCodable.decodeFromJSON(_:), and Bool+SettingCodable.encodeForJSON()
describing purpose, parameters (e.g. the raw Any? input), return values (when it
returns a Bool or nil, or what type is encoded), and any important behavior
(e.g. CFBoolean detection for NSNumber), matching the package documentation
guideline for public symbols.

Comment on lines +9 to +15
public static func decodeFromUserDefaults(_ raw: Any?) -> Int? {
if let int = raw as? Int { return int }
if let number = raw as? NSNumber, CFGetTypeID(number) != CFBooleanGetTypeID() {
return number.intValue
}
return nil
}

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Swift, does casting an NSNumber that wraps a Bool (e.g. NSNumber(value: true)) with as? Int succeed and return 1?

💡 Result:

Yes. Casting an NSNumber that wraps a Bool (e.g. NSNumber(value: true), which is effectively an Objective-C BOOL/char value of 1) to Int using as? succeeds and produces 1. Rationale (grounded in sources): 1) Swift’s NSNumber↔numeric bridging is defined as an Optional cast (“Can I safely express the value stored in this opaque box called a NSNumber as the value I want?”). If it can be expressed, as? returns a non-nil value; otherwise it returns nil [1]. 2) The Foundation/Corelibs implementation for bridging NSNumber to Int uses number.intValue (and the conditional bridge checks Int(exactly: x) and returns false otherwise). For an NSNumber created from a BOOL value, the underlying integer value is 0 or 1, so intValue is 0/1, making the conditional bridge succeed for Int and therefore as? returns 1 for true [2]. 3) NSNumber documentation confirms NSNumber can be accessed as a BOOL and also exposes integer accessors like intValue/int32Value, i.e. the stored BOOL can be represented as an integer value when accessed as an integer type [3].

Citations:


Reject boolean NSNumber values before attempting the Int cast in decodeFromUserDefaults

Swift’s NSNumber↔Int casting succeeds for BOOL-backed NSNumbers (e.g. true becomes 1), so the raw as? Int branch can return 1/0 and bypass the CFBooleanGetTypeID() rejection.

🐛 Proposed fix to reject booleans before the Int cast
     public static func decodeFromUserDefaults(_ raw: Any?) -> Int? {
-        if let int = raw as? Int { return int }
-        if let number = raw as? NSNumber, CFGetTypeID(number) != CFBooleanGetTypeID() {
-            return number.intValue
-        }
-        return nil
+        if let number = raw as? NSNumber {
+            guard CFGetTypeID(number) != CFBooleanGetTypeID() else { return nil }
+            return number.intValue
+        }
+        if let int = raw as? Int { return int }
+        return nil
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static func decodeFromUserDefaults(_ raw: Any?) -> Int? {
if let int = raw as? Int { return int }
if let number = raw as? NSNumber, CFGetTypeID(number) != CFBooleanGetTypeID() {
return number.intValue
}
return nil
}
public static func decodeFromUserDefaults(_ raw: Any?) -> Int? {
if let number = raw as? NSNumber {
guard CFGetTypeID(number) != CFBooleanGetTypeID() else { return nil }
return number.intValue
}
if let int = raw as? Int { return int }
return nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Codable/Int`+SettingCodable.swift
around lines 9 - 15, The decodeFromUserDefaults implementation must reject
boolean-backed values before allowing an Int conversion: first check for Bool
(raw as? Bool) and for NSNumber whose CFGetTypeID equals CFBooleanGetTypeID()
and return nil for those cases; only after rejecting booleans, perform the Int
extraction (keep the existing decodeFromUserDefaults function name:
decodeFromUserDefaults(_:), use the Int cast and NSNumber.intValue branches) so
BOOL-backed NSNumbers cannot slip through as 1/0.

Comment on lines +19 to +29
public static func decodeFromUserDefaults(_ raw: Any?) -> Self? {
RawValue.decodeFromUserDefaults(raw).flatMap { Self(rawValue: $0) }
}

public func encodeForUserDefaults() -> Any { rawValue.encodeForUserDefaults() }

public static func decodeFromJSON(_ raw: Any?) -> Self? {
RawValue.decodeFromJSON(raw).flatMap { Self(rawValue: $0) }
}

public func encodeForJSON() -> Any { rawValue.encodeForJSON() }

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 | ⚡ Quick win

Document the public extension methods individually.

These four public APIs are missing symbol-level DocC comments (- Parameter, - Returns:). File-level prose doesn’t satisfy the per-symbol requirement.

As per coding guidelines, “Every public symbol in a Swift package under Packages/ must be documented with a Swift-DocC triple-slash /// comment at the time of writing.”

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

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Codable/RawRepresentable`+SettingCodable.swift
around lines 19 - 29, The four public methods decodeFromUserDefaults(_:),
encodeForUserDefaults(), decodeFromJSON(_:), and encodeForJSON() in the
RawRepresentable+SettingCodable extension lack per-symbol Swift‑DocC comments;
add triple‑slash /// comments immediately above each public declaration that
include a brief description plus - Parameter and - Returns: tags (for the two
taking a raw Any? parameter and for the return values) so each public symbol is
individually documented according to the package guidelines.

Comment on lines +5 to +9
public static func decodeFromUserDefaults(_ raw: Any?) -> String? { raw as? String }
public func encodeForUserDefaults() -> Any { self }

public static func decodeFromJSON(_ raw: Any?) -> String? { raw as? String }
public func encodeForJSON() -> Any { self }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add DocC for the public encode/decode methods.

Please add /// comments for each public symbol introduced in this conformance.

As per coding guidelines: Packages/**/*.swift: “Every public symbol in a Swift package under Packages/ must be documented with a Swift-DocC triple-slash /// comment at the time of writing.”

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

In
`@Packages/CmuxSettings/Sources/CmuxSettings/Codable/String`+SettingCodable.swift
around lines 5 - 9, Add Swift-DocC triple-slash comments for each public symbol
in the String conformance: document public static func
decodeFromUserDefaults(_:), public func encodeForUserDefaults(), public static
func decodeFromJSON(_:), and public func encodeForJSON() with a brief one-line
description of purpose, expected input/output and any important behavior (e.g.,
returns nil if cast fails), using /// comments immediately above each
declaration.

Comment on lines +22 to +24
public var body: some View {
EmptyView()
}

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 | ⚡ Quick win

BrowserImportSection currently renders nothing.

Line 22 returning EmptyView() makes the .browserImport target effectively blank, which breaks the stated deeplink behavior for this section.

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

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserImportSection.swift`
around lines 22 - 24, BrowserImportSection currently returns EmptyView(), so the
.browserImport target renders nothing and the deeplink handling never runs;
replace the EmptyView() in the public var body of BrowserImportSection with a
view that performs the deeplink behavior (for example a lightweight placeholder
VStack or ZStack that calls your deeplink handler in onAppear) and/or triggers
navigation to the import UI; locate the BrowserImportSection struct and its body
property and implement the appropriate view that invokes the existing deeplink
handling function (or call the import handling logic directly) instead of
returning EmptyView().

Comment on lines +12 to +14
static var uiCases: [SocketControlMode] {
[.off, .cmuxOnly, .automation, .password, .allowAll]
}

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 | ⚡ Quick win

Add explicit public modifier to uiCases.

The static property uiCases lacks an explicit public modifier, making it internal to the CmuxSettingsUI module. Based on the doc comment (lines 4–7) stating these are "UI-facing labels," this appears to be intended as public API but is currently inaccessible outside the package.

🔧 Proposed fix
-    static var uiCases: [SocketControlMode] {
+    public static var uiCases: [SocketControlMode] {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static var uiCases: [SocketControlMode] {
[.off, .cmuxOnly, .automation, .password, .allowAll]
}
public static var uiCases: [SocketControlMode] {
[.off, .cmuxOnly, .automation, .password, .allowAll]
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Values/SocketControlMode`+Display.swift
around lines 12 - 14, The static property uiCases is currently internal and
should be public so UI-facing labels are accessible outside the package; update
the declaration of uiCases on SocketControlMode (the static var uiCases) to
include the public access modifier (e.g., public static var uiCases = ...) so
the property is exported as part of the public API.

Comment on lines +17 to +30
var displayName: String {
switch self {
case .off:
return String(localized: "socketControl.off.name", defaultValue: "Off")
case .cmuxOnly:
return String(localized: "socketControl.cmuxOnly.name", defaultValue: "cmux processes only")
case .automation:
return String(localized: "socketControl.automation.name", defaultValue: "Automation mode")
case .password:
return String(localized: "socketControl.password.name", defaultValue: "Password mode")
case .allowAll:
return String(localized: "socketControl.allowAll.name", defaultValue: "Full open access")
}
}

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 | ⚡ Quick win

Add explicit public modifier to displayName.

The computed property displayName lacks an explicit public modifier, making it internal to the CmuxSettingsUI module. This property provides the localized short label for the Automation picker and should be publicly accessible.

🔧 Proposed fix
-    var displayName: String {
+    public var displayName: String {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var displayName: String {
switch self {
case .off:
return String(localized: "socketControl.off.name", defaultValue: "Off")
case .cmuxOnly:
return String(localized: "socketControl.cmuxOnly.name", defaultValue: "cmux processes only")
case .automation:
return String(localized: "socketControl.automation.name", defaultValue: "Automation mode")
case .password:
return String(localized: "socketControl.password.name", defaultValue: "Password mode")
case .allowAll:
return String(localized: "socketControl.allowAll.name", defaultValue: "Full open access")
}
}
/// The localized display name for this socket control mode.
/// - Returns: A user-facing string suitable for UI display.
public var displayName: String {
switch self {
case .off:
return String(localized: "socketControl.off.name", defaultValue: "Off")
case .cmuxOnly:
return String(localized: "socketControl.cmuxOnly.name", defaultValue: "cmux processes only")
case .automation:
return String(localized: "socketControl.automation.name", defaultValue: "Automation mode")
case .password:
return String(localized: "socketControl.password.name", defaultValue: "Password mode")
case .allowAll:
return String(localized: "socketControl.allowAll.name", defaultValue: "Full open access")
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Values/SocketControlMode`+Display.swift
around lines 17 - 30, The computed property displayName on SocketControlMode is
currently internal; make it publicly accessible by adding the explicit public
access modifier to the property declaration (change "var displayName: String" to
"public var displayName: String") so the localized labels (e.g.,
"socketControl.off.name", "socketControl.automation.name") can be used outside
the CmuxSettingsUI module.

Comment on lines +33 to +46
var description: String {
switch self {
case .off:
return String(localized: "socketControl.off.description", defaultValue: "Disable the local control socket.")
case .cmuxOnly:
return String(localized: "socketControl.cmuxOnly.description", defaultValue: "Only processes started inside cmux terminals can send commands.")
case .automation:
return String(localized: "socketControl.automation.description", defaultValue: "Allow external local automation clients from this macOS user (no ancestry check).")
case .password:
return String(localized: "socketControl.password.description", defaultValue: "Require socket authentication with a password stored in a local file.")
case .allowAll:
return String(localized: "socketControl.allowAll.description", defaultValue: "Allow any local process and user to connect with no auth. Unsafe.")
}
}

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 | ⚡ Quick win

Add explicit public modifier to description.

The computed property description lacks an explicit public modifier, making it internal to the CmuxSettingsUI module. This property provides the localized security/behavior description for each mode and should be publicly accessible.

🔧 Proposed fix
-    var description: String {
+    public var description: String {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Values/SocketControlMode`+Display.swift
around lines 33 - 46, The computed property description on the SocketControlMode
extension is currently internal; make it public so callers outside the
CmuxSettingsUI module can access the localized descriptions by changing the
declaration to public var description: String in SocketControlMode+Display (the
computed property named description within the SocketControlMode extension).

coderabbitai[bot]
coderabbitai Bot previously requested changes May 29, 2026

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

Review continued from previous batch...

## Adding a whole new section

1. Create `Sources/CmuxSettings/Keys/FooCatalogSection.swift`:
```swift

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Add blank lines around fenced code blocks.

Static analysis flagged missing blank lines before the code fences. This is a minor markdown formatting convention.

📝 Proposed fix
 ## Adding a whole new section

 1. Create `Sources/CmuxSettings/Keys/FooCatalogSection.swift`:
+
    ```swift
    public struct FooCatalogSection: SettingCatalogSection {
        public let bar = DefaultsKey<Bool>(
            id: "foo.bar",
            defaultValue: false,
            userDefaultsKey: "fooBar"
        )
        public init() {}
    }
    ```
+
 2. Add `public let foo = FooCatalogSection()` to `SettingCatalog`.

Also applies to: 155-155

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 146-146: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

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

In `@Packages/CmuxSettings/README.md` at line 146, Add blank lines before and
after fenced Swift code blocks in the README to satisfy markdown conventions:
ensure there is an empty line immediately above the opening ```swift fence and
an empty line immediately below the closing ``` fence for the example that
defines FooCatalogSection and the subsequent snippet; update the README so the
FooCatalogSection example (references: FooCatalogSection, SettingCatalogSection,
DefaultsKey) and the follow-up instruction to add `public let foo =
FooCatalogSection()` to SettingCatalog are each surrounded by blank lines around
their fenced code blocks.

Comment on lines +5 to +166
public let appearance = DefaultsKey<AppearanceMode>(
id: "app.appearance",
defaultValue: .system,
userDefaultsKey: "appearanceMode"
)

public let language = DefaultsKey<AppLanguage>(
id: "app.language",
defaultValue: .system,
userDefaultsKey: "appLanguage"
)

public let appIcon = DefaultsKey<AppIconMode>(
id: "app.appIcon",
defaultValue: .automatic,
userDefaultsKey: "appIconMode"
)

public let menuBarOnly = DefaultsKey<Bool>(
id: "app.menuBarOnly",
defaultValue: false,
userDefaultsKey: "menuBarOnly"
)

public let newWorkspacePlacement = DefaultsKey<WorkspacePlacement>(
id: "app.newWorkspacePlacement",
defaultValue: .top,
userDefaultsKey: "newWorkspacePlacement"
)

public let workspaceInheritWorkingDirectory = DefaultsKey<Bool>(
id: "app.workspaceInheritWorkingDirectory",
defaultValue: true,
userDefaultsKey: "workspaceInheritWorkingDirectory"
)

public let presentationMode = DefaultsKey<WorkspacePresentationMode>(
id: "app.minimalMode",
defaultValue: .standard,
userDefaultsKey: "workspacePresentationMode"
)

public let keepWorkspaceOpenWhenClosingLastSurface = DefaultsKey<Bool>(
id: "app.keepWorkspaceOpenWhenClosingLastSurface",
defaultValue: false,
userDefaultsKey: "closeWorkspaceOnLastSurfaceShortcut"
)

public let focusPaneOnFirstClick = DefaultsKey<Bool>(
id: "app.focusPaneOnFirstClick",
defaultValue: true,
userDefaultsKey: "paneFirstClickFocus.enabled"
)

public let preferredEditor = DefaultsKey<String>(
id: "app.preferredEditor",
defaultValue: "",
userDefaultsKey: "preferredEditorCommand"
)

public let openSupportedFilesInCmux = DefaultsKey<Bool>(
id: "app.openSupportedFilesInCmux",
defaultValue: true,
userDefaultsKey: "openSupportedFilesInCmux"
)

public let openMarkdownInCmuxViewer = DefaultsKey<Bool>(
id: "app.openMarkdownInCmuxViewer",
defaultValue: true,
userDefaultsKey: "openMarkdownInCmuxViewer"
)

public let iMessageMode = DefaultsKey<Bool>(
id: "app.iMessageMode",
defaultValue: false,
userDefaultsKey: "app.iMessageMode"
)

public let reorderOnNotification = DefaultsKey<Bool>(
id: "app.reorderOnNotification",
defaultValue: true,
userDefaultsKey: "workspaceAutoReorderOnNotification"
)

public let sendAnonymousTelemetry = DefaultsKey<Bool>(
id: "app.sendAnonymousTelemetry",
defaultValue: true,
userDefaultsKey: "sendAnonymousTelemetry"
)

public let confirmQuitMode = DefaultsKey<ConfirmQuitMode>(
id: "app.confirmQuit",
defaultValue: .always,
userDefaultsKey: "confirmQuit"
)

public let warnBeforeQuit = DefaultsKey<Bool>(
id: "app.warnBeforeQuit",
defaultValue: true,
userDefaultsKey: "warnBeforeQuitShortcut"
)

public let warnBeforeClosingTab = DefaultsKey<Bool>(
id: "app.warnBeforeClosingTab",
defaultValue: false,
userDefaultsKey: "warnBeforeClosingTabShortcut"
)

public let warnBeforeClosingTabXButton = DefaultsKey<Bool>(
id: "app.warnBeforeClosingTabXButton",
defaultValue: true,
userDefaultsKey: "warnBeforeClosingTabXButton"
)

public let hideTabCloseButton = DefaultsKey<Bool>(
id: "app.hideTabCloseButton",
defaultValue: false,
userDefaultsKey: "hideTabCloseButton"
)

public let renameSelectsExistingName = DefaultsKey<Bool>(
id: "app.renameSelectsExistingName",
defaultValue: true,
userDefaultsKey: "commandPalette.renameSelectAllOnFocus"
)

public let commandPaletteSearchesAllSurfaces = DefaultsKey<Bool>(
id: "app.commandPaletteSearchesAllSurfaces",
defaultValue: false,
userDefaultsKey: "commandPalette.switcherSearchAllSurfaces"
)

public let fileDropDefaultBehavior = DefaultsKey<FileDropDefaultBehavior>(
id: "app.fileDropDefaultBehavior",
defaultValue: .text,
userDefaultsKey: "fileDrop.defaultBehavior"
)

public let titlebarControlsStyle = DefaultsKey<String>(
id: "app.titlebarControlsStyle",
defaultValue: "classic",
userDefaultsKey: "titlebarControlsStyle"
)

public let workspaceButtonFade = DefaultsKey<String>(
id: "app.workspaceButtonFade",
defaultValue: "default",
userDefaultsKey: "workspaceButtonFade"
)

public let workspaceTitlebarVisibility = DefaultsKey<String>(
id: "app.workspaceTitlebarVisibility",
defaultValue: "visible",
userDefaultsKey: "workspaceTitlebarVisibility"
)

public let systemWideHotkeyEnabled = DefaultsKey<Bool>(
id: "app.systemWideHotkeyEnabled",
defaultValue: false,
userDefaultsKey: "systemWideHotkey.enabled"
)

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 | ⚡ Quick win

Document all public settings keys.

Every public key lacks DocC /// comments. As per coding guidelines, every public symbol in Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing.

Add a /// summary for each of the 24 keys describing purpose, valid values/range, and any important side effects.

📝 Example documentation
+    /// The app's color scheme preference.
+    ///
+    /// - `.system`: Follow macOS system appearance.
+    /// - `.light`: Always use light mode.
+    /// - `.dark`: Always use dark mode.
     public let appearance = DefaultsKey<AppearanceMode>(
         id: "app.appearance",
         defaultValue: .system,
         userDefaultsKey: "appearanceMode"
     )

+    /// Controls whether the app icon appears in the Dock.
+    ///
+    /// When `true`, the app runs as a menu-bar-only utility with no Dock
+    /// icon or Cmd+Tab entry. Requires app restart to take effect.
     public let menuBarOnly = DefaultsKey<Bool>(
         id: "app.menuBarOnly",
         defaultValue: false,
         userDefaultsKey: "menuBarOnly"
     )

As per coding guidelines, Packages/**/*.swift files require: "Every public symbol in a Swift package under Packages/ must be documented with a Swift-DocC triple-slash /// comment at the time of writing."

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

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift`
around lines 5 - 166, Add Swift-DocC triple-slash comments (///) above each
public DefaultsKey declaration (e.g., appearance, language, appIcon,
menuBarOnly, newWorkspacePlacement, workspaceInheritWorkingDirectory,
presentationMode, keepWorkspaceOpenWhenClosingLastSurface,
focusPaneOnFirstClick, preferredEditor, openSupportedFilesInCmux,
openMarkdownInCmuxViewer, iMessageMode, reorderOnNotification,
sendAnonymousTelemetry, confirmQuitMode, warnBeforeQuit, warnBeforeClosingTab,
warnBeforeClosingTabXButton, hideTabCloseButton, renameSelectsExistingName,
commandPaletteSearchesAllSurfaces, fileDropDefaultBehavior,
titlebarControlsStyle, workspaceButtonFade, workspaceTitlebarVisibility,
systemWideHotkeyEnabled) providing a one-line summary of purpose plus brief note
on valid values or enum cases (or range for Bool/String) and any important side
effects (e.g., UI behavior, persistence, feature toggles); ensure each comment
is immediate above the corresponding public let and uses /// DocC format.

Comment on lines +36 to +46
public var all: [AnySettingKey] {
Mirror(reflecting: self).children.flatMap { _, value -> [AnySettingKey] in
if let key = value as? AnySettingKeyConvertible {
return [key.asAnySettingKey]
}
if let section = value as? SettingCatalogSection {
return section.all
}
return []
}
}

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find consumers of `.all` on catalog/sections to assess call frequency.
rg -nP --type=swift -C3 '\.all\b' -g '!**/Tests/**'
rg -nP --type=swift -C3 '\bSettingsSearchIndex\b'

Repository: manaflow-ai/cmux

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the real call sites found earlier for `.all` usage.
# Focus on SettingsSearchIndex and any migration/reset code paths.
files=(
  "Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift"
  "Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/ResetSection.swift"
  "Packages/CmuxSettings/Sources/CmuxSettings/Stores/UserDefaultsSettingsStore.swift"
  "Sources/cmuxApp.swift"
  "Packages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalogSection.swift"
)

for f in "${files[@]}"; do
  if [ -f "$f" ]; then
    echo "==== $f ===="
    # Print a window around the `.all` usage areas.
    rg -n "\.all\b" "$f" -n || true
    # Print around the first few matches (keeps output manageable).
    # (Use `--context` only around matches, but rg already printed line numbers.)
    # We'll also show a small numbered excerpt around each match.
    while IFS=: read -r line _; do
      start=$((line-10)); end=$((line+30))
      start=$(( start<1 ? 1 : start ))
      sed -n "${start},${end}p" "$f" | nl -ba | sed "s/^/  /"
      echo
    done < <(rg -n "\.all\b" "$f" | head -n 6)
  fi
done

Repository: manaflow-ai/cmux

Length of output: 264


🏁 Script executed:

#!/bin/bash
set -euo pipefail

targets=(
  "Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift"
  "Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/ResetSection.swift"
  "Packages/CmuxSettings/Sources/CmuxSettings/Stores/UserDefaultsSettingsStore.swift"
  "Sources/cmuxApp.swift"
  "Packages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalogSection.swift"
)

for f in "${targets[@]}"; do
  echo "==== $f ===="
  if [ ! -f "$f" ]; then echo "(missing)"; continue; fi

  # Show all `.all` matches in the file with small context.
  rg -n --context 4 '\.all\b' "$f" | head -n 60
  echo
done

Repository: manaflow-ai/cmux

Length of output: 2693


🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift"
echo "==== head ===="
head -n 200 "$f" | nl -ba | sed -n '1,200p'

echo
echo "==== around catalog.all loop ===="
sed -n '60,130p' "$f" | nl -ba

echo
echo "==== locate entries(matching:) and related hot paths ===="
rg -n --context 2 'func entries\(matching:|static func entries\(matching:|entries\(matching:|onChange\(of:.*settingsSearchText' "$f"

Repository: manaflow-ai/cmux

Length of output: 136


🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift"

echo "==== around file top (first 220 lines) ===="
awk '{printf "%6d\t%s\n", NR, $0}' "$f" | sed -n '1,220p'

echo
echo "==== around catalog.all loop ===="
awk '{printf "%6d\t%s\n", NR, $0}' "$f" | sed -n '60,130p'

echo
echo "==== locate entries(matching:) in this file ===="
rg -n --type=swift 'entries\s*\(matching:' "$f"
echo
echo "==== grep for entries(matching:) definitions/usages across repo ===="
rg -n --type=swift 'func\s+entries\s*\(matching:' -g '!**/Tests/**'
rg -n --type=swift '\.entries\s*\(matching:' -g '!**/Tests/**'

Repository: manaflow-ai/cmux

Length of output: 10745


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --type=swift 'SettingsSearchIndex\s*\(' -g '!**/Tests/**'
rg -n --type=swift 'SettingsSearchIndex\.entries\s*\(matching:' -g '!**/Tests/**'
rg -n --type=swift 'SettingsSearchIndex\.match\s*\(' -g '!**/Tests/**'
rg -n --type=swift 'entries\s*\(matching:' Packages/CmuxSettingsUI/Sources/CmuxSettingsUI -g '!**/Tests/**'
rg -n --type=swift 'settingsSearchText' -g '!**/Tests/**'

Repository: manaflow-ai/cmux

Length of output: 420


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --type=swift 'entries\s*\(matching:' -g '!**/Tests/**'
rg -n --type=swift 'static func entries\s*\(matching:' -g '!**/Tests/**'
rg -n --type=swift 'SettingsSearchIndex\.entries\s*\(matching:' -g '!**/Tests/**'
rg -n --type=swift 'match\s*\(\s*draftState\.settingsSearchText' -g '!**/Tests/**'

Repository: manaflow-ai/cmux

Length of output: 292


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show call-site contexts
sed -n '1,140p' Sources/SettingsWindowScene.swift 2>/dev/null || true
sed -n '1,140p' Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift | nl -ba | sed -n '1,180p'

sed -n '9000,9085p' Sources/cmuxApp.swift | awk '{printf "%6d\t%s\n", NR+9000-1, $0}' | sed -n '1,160p'

# Find definition(s) of `entries(matching:)` where the parameter is likely `matching query:`
rg -n --type=swift --context 3 'static\s+func\s+entries\s*\(\s*matching\s+\w+\s*:' -g '!**/Tests/**'
rg -n --type=swift --context 3 'func\s+entries\s*\(\s*matching\s+\w+\s*:' -g '!**/Tests/**'

# Also search for any `entries(` static computed helpers on SettingsSearchIndex
rg -n --type=swift --context 3 'SettingsSearchIndex\.[a-zA-Z_]*entries' -g '!**/Tests/**'
rg -n --type=swift --context 3 'struct\s+SettingsSearchIndex|extension\s+SettingsSearchIndex' Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift

Repository: manaflow-ai/cmux

Length of output: 103


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "==== Call site: SettingsWindowScene.swift around SettingsSearchIndex usage ===="
f="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift"
awk 'NR>=50 && NR<=110 {printf "%6d\t%s\n", NR, $0}' "$f" 2>/dev/null || true

echo
echo "==== Call site: Sources/cmuxApp.swift around SettingsSearchIndex usage ===="
awk 'NR>=9015 && NR<=9065 {printf "%6d\t%s\n", NR, $0}' Sources/cmuxApp.swift 2>/dev/null || true

echo
echo "==== Definition search for entries(matching:) (no tests) ===="
rg -n --type=swift 'entries\s*\(\s*matching\s+[^)]*:' -g '!**/Tests/**'
echo
rg -n --type=swift 'static\s+func\s+entries\s*\(\s*matching\s+[^)]*:' -g '!**/Tests/**'
echo
rg -n --type=swift 'entries\s*\(matching:' -g '!**/Tests/**'
echo
rg -n --type=swift 'extension\s+SettingsSearchIndex' -g '!**/Tests/**'
echo
rg -n --type=swift 'SettingsSearchIndex\.(entries|match)\b' -g '!**/Tests/**'

Repository: manaflow-ai/cmux

Length of output: 6634


🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="Sources/SettingsNavigation.swift"
start=450
end=560
awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%6d\t%s\n", NR, $0}' "$f"

echo
echo "==== Search for any stored/cached index used by entries(matching:) ===="
rg -n --type=swift 'searchIndex|SettingsSearchIndex\(catalog:|private static let|static let.*entries|lazy' "$f"

Repository: manaflow-ai/cmux

Length of output: 7527


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# How often is the computed `searchIndex` var used?
f="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift"
rg -n --type=swift 'searchIndex\b' "$f"

# Is the SettingsSearchIndex from SettingsSearchIndex.swift actually used elsewhere?
rg -n --type=swift 'SettingsSearchIndex\.Entry|SettingsSearchIndex\.' -g '!**/Tests/**'
rg -n --type=swift '\.match\(' -g '!**/Tests/**'
rg -n --type=swift 'match\s*\(_' Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/SettingsSearchIndex.swift

Repository: manaflow-ai/cmux

Length of output: 13248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift"
awk 'NR>=60 && NR<=210 {printf "%6d\t%s\n", NR, $0}' "$f"
echo
echo "==== around ~240-290 ===="
awk 'NR>=230 && NR<=310 {printf "%6d\t%s\n", NR, $0}' "$f"

Repository: manaflow-ai/cmux

Length of output: 12656


Hot path: avoid recomputing SettingCatalogSection.all via Mirror during settings search

  • SettingCatalogSection.all builds a new Mirror(reflecting: self) and recursively flattens sections on every access (allocating intermediate arrays).
  • In the settings UI, SettingsWindowScene rebuilds searchIndex from SettingsSearchIndex(catalog:) via a computed searchIndex property, and the sidebar calls searchIndex.match(searchText) while searchText is updated by .searchable (per keystroke). SettingsSearchIndex.init iterates for key in catalog.all, so this reflection work runs on the search hot path.
  • Materialize/cache the flattened [AnySettingKey] once (at catalog construction / per section type) and reuse the cached array when building the search index (or cache SettingsSearchIndex itself so catalog.all isn’t recomputed on every render).
public var all: [AnySettingKey] {
    Mirror(reflecting: self).children.flatMap { _, value -> [AnySettingKey] in
        if let key = value as? AnySettingKeyConvertible {
            return [key.asAnySettingKey]
        }
        if let section = value as? SettingCatalogSection {
            return section.all
        }
        return []
    }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/SettingCatalogSection.swift`
around lines 36 - 46, SettingCatalogSection.all currently uses
Mirror(reflecting:) and recursively flattens children on every access (hot
path); change this to compute the flattened [AnySettingKey] once and reuse it —
e.g., add a cached stored property (or lazy var) on SettingCatalogSection that
runs the Mirror-based reflection once (or populate it in the section
initializer) and have the all computed property return that cached array; ensure
you reference the existing reflection logic (Mirror + AnySettingKeyConvertible
-> asAnySettingKey and nested SettingCatalogSection recursion) when populating
the cache and consider making SettingsSearchIndex or the catalog's searchIndex
use that cached array so catalog.all is not recomputed per keystroke.

Comment on lines +5 to +50
public let showScrollBar = DefaultsKey<Bool>(
id: "terminal.showScrollBar",
defaultValue: false,
userDefaultsKey: "terminal.showScrollBar"
)

public let copyOnSelect = DefaultsKey<Bool>(
id: "terminal.copyOnSelect",
defaultValue: false,
userDefaultsKey: "terminal.copyOnSelect"
)

public let autoResumeAgentSessions = DefaultsKey<Bool>(
id: "terminal.autoResumeAgentSessions",
defaultValue: true,
userDefaultsKey: "terminal.autoResumeAgentSessions"
)

public let agentHibernationEnabled = DefaultsKey<Bool>(
id: "terminal.agentHibernation.enabled",
defaultValue: false,
userDefaultsKey: "terminal.agentHibernation.enabled"
)

public let agentHibernationIdleSeconds = DefaultsKey<Double>(
id: "terminal.agentHibernation.idleSeconds",
defaultValue: 300,
userDefaultsKey: "terminal.agentHibernation.idleSeconds"
)

public let agentHibernationMaxLiveTerminals = DefaultsKey<Int>(
id: "terminal.agentHibernation.maxLiveTerminals",
defaultValue: 16,
userDefaultsKey: "terminal.agentHibernation.maxLiveTerminals"
)

public let textBoxMaxLines = DefaultsKey<Int>(
id: "terminal.textBoxMaxLines",
defaultValue: 6,
userDefaultsKey: "terminal.textBoxMaxLines"
)

public let resumeCommands = JSONKey<[String]>(
id: "terminal.resumeCommands",
defaultValue: []
)

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 | ⚡ Quick win

Document all public settings keys.

Every public key lacks DocC /// comments. As per coding guidelines, every public symbol in Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing.

Add a /// summary for each key describing its purpose, valid range (for numeric keys), and any important behavior.

📝 Example documentation
+    /// Controls the visibility of the terminal scrollbar.
+    ///
+    /// When `true`, the scrollbar is always visible. When `false`, it is
+    /// hidden during normal terminal use and only appears during scrollback.
     public let showScrollBar = DefaultsKey<Bool>(
         id: "terminal.showScrollBar",
         defaultValue: false,
         userDefaultsKey: "terminal.showScrollBar"
     )

+    /// The idle time in seconds before an agent terminal is hibernated.
+    ///
+    /// Only applies when ``agentHibernationEnabled`` is `true`. Valid range
+    /// is 60...3600 (1 minute to 1 hour). Defaults to 300 seconds (5 minutes).
     public let agentHibernationIdleSeconds = DefaultsKey<Double>(
         id: "terminal.agentHibernation.idleSeconds",
         defaultValue: 300,
         userDefaultsKey: "terminal.agentHibernation.idleSeconds"
     )

As per coding guidelines, Packages/**/*.swift files require: "Every public symbol in a Swift package under Packages/ must be documented with a Swift-DocC triple-slash /// comment at the time of writing."

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

In `@Packages/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift`
around lines 5 - 50, Add Swift-DocC triple-slash comments for each public
DefaultsKey/JSONKey in TerminalCatalogSection: prepend a /// summary to
showScrollBar, copyOnSelect, autoResumeAgentSessions, agentHibernationEnabled,
agentHibernationIdleSeconds, agentHibernationMaxLiveTerminals, textBoxMaxLines,
and resumeCommands describing the purpose of the key, any valid range or units
(e.g., seconds for agentHibernationIdleSeconds, min/max for textBoxMaxLines and
agentHibernationMaxLiveTerminals), and any important behavior (defaults, effects
on runtime like autoResumeAgentSessions behavior or whether resumeCommands is a
list of shell commands). Ensure each comment is a single-line Swift-DocC
triple-slash (///) immediately above the corresponding declaration.

Comment on lines +23 to +35
public init(dottedPath: String) {
precondition(!dottedPath.isEmpty, "JSONPath requires a non-empty dotted path")
// `split(separator:)` with default `omittingEmptySubsequences: true`
// would silently swallow consecutive dots; we use the non-omitting
// variant and then validate to catch malformed paths like
// "app..appearance" or ".leading" or "trailing." as programmer errors.
let segments = dottedPath.split(separator: ".", omittingEmptySubsequences: false).map(String.init)
precondition(
!segments.contains(""),
"JSONPath contains an empty component (leading/trailing dot or consecutive dots): \(dottedPath)"
)
self.components = segments
}

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 | 🟡 Minor | ⚡ Quick win

Clarify trap behavior in documentation.

Line 22 states "Trapping in debug builds on empty input" but precondition traps in all builds (debug and release). The implementation is correct for a programmer-error scenario, but the documentation should match.

📝 Proposed fix
     /// - Parameter dottedPath: A non-empty dotted identifier, e.g.
     ///   `"app.appearance"`. The string is split on `"."` once; subsequent
     ///   operations reuse the precomputed components. Trapping in debug
-    ///   builds on empty input — empty paths are a programmer error.
+    ///   builds and release builds on empty input or malformed paths
+    ///   (leading/trailing/consecutive dots) — these are programmer errors.
     public init(dottedPath: String) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONPath.swift` around
lines 23 - 35, The doc comment incorrectly says "Trapping in debug builds on
empty input" but the implementation uses precondition which can trap in
non-debug builds; update the documentation near the JSONPath.init(dottedPath:)
initializer (and any comment referencing "trapping in debug builds") to
accurately state that a precondition enforces non-empty, well-formed dotted
paths and may terminate the program on violation (i.e., traps when the input is
empty or contains empty components), ensuring the doc matches the use of
precondition.

Comment on lines +523 to +556
private static let systemSoundOptions: [(value: String, label: String)] = [
("default", "Default"),
("Basso", "Basso"),
("Blow", "Blow"),
("Bottle", "Bottle"),
("Frog", "Frog"),
("Funk", "Funk"),
("Glass", "Glass"),
("Hero", "Hero"),
("Morse", "Morse"),
("Ping", "Ping"),
("Pop", "Pop"),
("Purr", "Purr"),
("Sosumi", "Sosumi"),
("Submarine", "Submarine"),
("Tink", "Tink"),
(customSoundFileValue, "Custom File..."),
("none", "None"),
]

@ViewBuilder
private func notificationSoundRow(model: DefaultsValueModel<String>) -> some View {
let customFile = DefaultsValueModel(store: defaultsStore, key: catalog.notifications.customSoundFilePath)
SettingsCardRow(
configurationReview: .json("notifications.sound", "notifications.customSoundFilePath"),
String(localized: "settings.notifications.sound.title", defaultValue: "Notification Sound"),
subtitle: String(localized: "settings.notifications.sound.subtitle", defaultValue: "Sound played when a notification arrives."),
controlWidth: Self.notificationSoundControlWidth
) {
VStack(alignment: .trailing, spacing: 6) {
HStack(spacing: 6) {
Picker("", selection: Binding(get: { model.current }, set: { model.set($0) })) {
ForEach(Self.systemSoundOptions, id: \.value) { option in
Text(option.label).tag(option.value)

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP --type=swift -C3 'Custom File|systemSounds|No file selected' -g '!**/CmuxSettingsUI/**'

Repository: manaflow-ai/cmux

Length of output: 3030


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the legacy systemSounds definition and surrounding uses
rg -n --type=swift "NotificationSoundSettings\.systemSounds" .

# Show the definition of systemSounds in TerminalNotificationStore.swift
FILE="Sources/TerminalNotificationStore.swift"
if [ -f "$FILE" ]; then
  sed -n '80,140p' "$FILE" | cat -n
fi

# Check AppSection.swift for the systemSoundOptions definition (context for localization)
FILE2="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift"
if [ -f "$FILE2" ]; then
  sed -n '480,580p' "$FILE2" | cat -n
fi

# Find existing localized keys under settings.notifications.sound
rg -n --type=swift "localized: \"settings\.notifications\.sound" . || true
rg -n --type=swift "settings\.notifications\.sound\." . || true

# Search string catalogs / xcstrings for those sentinel phrases or keys
# (best-effort: look for xcstrings and any matches)
fd -e xcstrings -t f . | head -n 50 | cat >/tmp/xcstrings_list.txt || true
while read -r f; do
  rg -n --hidden --no-ignore-vcs -F "settings.notifications.sound" "$f" && true
done < /tmp/xcstrings_list.txt || true

rg -n --hidden --no-ignore-vcs -F "\"Default\"" . --glob='*.xcstrings' --glob='*.strings' --glob='*.plist' --glob='*.swift' || true
rg -n --hidden --no-ignore-vcs -F "Custom File..." . --glob='*.xcstrings' --glob='*.strings' --glob='*.plist' --glob='*.swift' || true
rg -n --hidden --no-ignore-vcs -F "\"None\"" . --glob='*.xcstrings' --glob='*.strings' --glob='*.plist' --glob='*.swift' || true

Repository: manaflow-ai/cmux

Length of output: 22254


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate where legacy systemSounds is used
rg -n --type=swift "NotificationSoundSettings\.systemSounds" .

# Show legacy systemSounds definition
FILE="Sources/TerminalNotificationStore.swift"
if [ -f "$FILE" ]; then
  echo "---- $FILE (systemSounds) ----"
  sed -n '80,140p' "$FILE" | cat -n
fi

# Show current AppSection.swift picker options area
FILE2="Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift"
if [ -f "$FILE2" ]; then
  echo "---- $FILE2 (notificationSoundRow options) ----"
  sed -n '500,570p' "$FILE2" | cat -n
fi

# Find existing localized keys for notifications.sound
rg -n --type=swift "localized: \"settings\.notifications\.sound" . || true
rg -n --type=swift "settings\.notifications\.sound\." . || true

# Search xcstrings for existing keys related to notifications.sound
fd -e xcstrings -t f . > /tmp/xcstrings_list.txt || true
if [ -s /tmp/xcstrings_list.txt ]; then
  echo "---- xcstrings matches for settings.notifications.sound ----"
  while read -r f; do
    rg -n --hidden --no-ignore-vcs -F "settings.notifications.sound" "$f" && true
  done < /tmp/xcstrings_list.txt
fi

# Search catalogs for the literal sentinel phrases
rg -n --hidden --no-ignore-vcs -F "Custom File..." . --glob='*.xcstrings' --glob='*.strings' --glob='*.plist' || true
rg -n --hidden --no-ignore-vcs -F "\"Default\"" . --glob='*.xcstrings' --glob='*.strings' --glob='*.plist' || true
rg -n --hidden --no-ignore-vcs -F "\"None\"" . --glob='*.xcstrings' --glob='*.strings' --glob='*.plist' || true

Repository: manaflow-ai/cmux

Length of output: 14967


Localize the notification sound picker’s sentinel labels (“Default”, “Custom File...”, “None”).

Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift builds the picker options from hardcoded labels and renders them via Text(option.label), so those three values stay English. The legacy NotificationSoundSettings.systemSounds in Sources/TerminalNotificationStore.swift uses the same hardcoded labels, and there are no existing settings.notifications.sound.* localized entries for these specific option labels (only title/subtitle and custom-file status/file-none). Replace those three labels with String(localized:defaultValue:) (adding the missing xcstrings entries) while leaving the macOS proper sound names (Basso, Blow, …) unchanged.

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

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift`
around lines 523 - 556, The picker options in systemSoundOptions use hardcoded
English labels for "Default", "Custom File...", and "None" so
notificationSoundRow renders untranslated text; change those three tuples in
systemSoundOptions to use localized strings via String(localized:
"settings.notifications.sound.default", defaultValue: "Default"),
String(localized: "settings.notifications.sound.customFile", defaultValue:
"Custom File..."), and String(localized: "settings.notifications.sound.none",
defaultValue: "None") (keep the macOS sound names unchanged), update the
xcstrings entries for these three keys, and leave the Picker rendering
(Text(option.label)) and model bindings intact.

Comment on lines +39 to +44
private func resetAll() async {
await defaultsStore.resetAll(catalog.all)
for key in catalog.all {
await key.resetInJSON(jsonStore)
}
}

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 | ⚡ Quick win

Handle errors in resetAll to prevent inconsistent state.

If key.resetInJSON(jsonStore) throws partway through the iteration (e.g., disk full, permission denied), some JSON keys will be reset while others remain. Since defaultsStore.resetAll already completed, UserDefaults keys are fully reset but JSON keys are only partially reset—an inconsistent state.

Wrap the JSON reset loop in error handling to log failures and continue, or propagate a composite error to the caller.

🛡️ Proposed fix
     private func resetAll() async {
         await defaultsStore.resetAll(catalog.all)
         for key in catalog.all {
-            await key.resetInJSON(jsonStore)
+            do {
+                try await key.resetInJSON(jsonStore)
+            } catch {
+                // Log but continue resetting remaining keys to maximize cleanup.
+                print("Failed to reset \(key.id) in JSON: \(error)")
+            }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/ResetSection.swift`
around lines 39 - 44, The resetAll function currently calls await
defaultsStore.resetAll(catalog.all) then iterates await
key.resetInJSON(jsonStore) without handling throws, risking partial failures;
update resetAll to handle errors from key.resetInJSON by either (a) catching
errors per-key, logging the failure (include key.identifier or similar) and
continuing so UserDefaults and JSON stay best-effort consistent, or (b)
collecting per-key errors into an array and throwing a composite error after the
loop so the caller can roll back or surface the failure. Locate resetAll,
defaultsStore.resetAll and key.resetInJSON(jsonStore) and implement one of these
error-handling strategies (per-key do/catch with logging or aggregate-and-throw)
so partial JSON resets are not silently left unhandled.

Comment on lines +48 to +49
Text(verbatim: "0")
.font(.caption.monospacedDigit())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Hardcoded placeholder for resume commands count.

The Text(verbatim: "0") displays a fixed zero for the resume commands count. Consider adding a comment explaining this is a placeholder until the dynamic count is wired from the JSON store, or implement the actual count.

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

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift`
around lines 48 - 49, The Text(verbatim: "0") in TerminalSection.swift is a
hardcoded placeholder for the resume commands count; either wire it to the real
value from your JSON store (e.g., replace Text(verbatim: "0") with something
like Text("\(viewModel.resumeCommands.count)") or
Text("\(store.resumeCommands.count)") after exposing the count via
TerminalSection's view model/initializer and ensuring the JSON store is decoded,
or if you cannot yet implement the plumbing add a clear TODO comment above
Text(verbatim: "0") explaining it's a deliberate placeholder until the dynamic
count (from the JSON store) is wired up; locate the Text(verbatim: "0")
occurrence in TerminalSection and update accordingly.

Comment thread Sources/cmuxApp.swift
Comment on lines +37 to +53
// Build the settings container once. All injected dependencies
// (the catalog, the two stores, the error log) live on this
// single struct; nothing in the package or app references a
// shared static.
let settingsCatalog = SettingCatalog()
let configFileURL = CmuxConfigLocation().userConfigFile
self.settingsRuntime = SettingsRuntime(
catalog: settingsCatalog,
userDefaultsStore: UserDefaultsSettingsStore(
defaults: .standard,
migrating: settingsCatalog.all
),
jsonStore: JSONConfigStore(fileURL: configFileURL),
errorLog: SettingsErrorLog(),
accountFlow: HostAccountFlow(),
hostActions: HostSettingsActions(configFileURL: configFileURL)
)

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 | 🟡 Minor | ⚡ Quick win

Defer settings-runtime construction until after the early launch exits.

SettingCatalog, UserDefaultsSettingsStore, JSONConfigStore, and the host shims are all built before CLIForwardingLaunchRouter.forwardToBundledCLIIfNeeded() and before the untagged DEV guard. That means CLI-style invocations and launches that immediately terminate still initialize the settings stack and any file/defaults side effects it brings along.

♻️ Proposed fix
-        // Build the settings container once. All injected dependencies
-        // (the catalog, the two stores, the error log) live on this
-        // single struct; nothing in the package or app references a
-        // shared static.
-        let settingsCatalog = SettingCatalog()
-        let configFileURL = CmuxConfigLocation().userConfigFile
-        self.settingsRuntime = SettingsRuntime(
-            catalog: settingsCatalog,
-            userDefaultsStore: UserDefaultsSettingsStore(
-                defaults: .standard,
-                migrating: settingsCatalog.all
-            ),
-            jsonStore: JSONConfigStore(fileURL: configFileURL),
-            errorLog: SettingsErrorLog(),
-            accountFlow: HostAccountFlow(),
-            hostActions: HostSettingsActions(configFileURL: configFileURL)
-        )
-
         // If invoked with CLI-style arguments (e.g. `cmux hooks setup`), exec the
         // bundled CLI at Contents/Resources/bin/cmux. The GUI binary and the CLI
         // share the name `cmux`, so if the GUI's Contents/MacOS leaks onto $PATH
@@
         if SocketControlSettings.shouldBlockUntaggedDebugLaunch() {
             StartupBreadcrumbLog.append("app.init.blockUntaggedDebugLaunch")
             Self.terminateForMissingLaunchTag()
         }
+
+        let settingsCatalog = SettingCatalog()
+        let configFileURL = CmuxConfigLocation().userConfigFile
+        self.settingsRuntime = SettingsRuntime(
+            catalog: settingsCatalog,
+            userDefaultsStore: UserDefaultsSettingsStore(
+                defaults: .standard,
+                migrating: settingsCatalog.all
+            ),
+            jsonStore: JSONConfigStore(fileURL: configFileURL),
+            errorLog: SettingsErrorLog(),
+            accountFlow: HostAccountFlow(),
+            hostActions: HostSettingsActions(configFileURL: configFileURL)
+        )

Also applies to: 55-70

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

In `@Sources/cmuxApp.swift` around lines 37 - 53, Defer construction of the
SettingsRuntime and its dependencies until after the early-launch/CLI exit
checks: move the block that instantiates SettingCatalog, CmuxConfigLocation(),
UserDefaultsSettingsStore, JSONConfigStore, SettingsErrorLog, HostAccountFlow,
HostSettingsActions and finally SettingsRuntime so it runs only after
CLIForwardingLaunchRouter.forwardToBundledCLIIfNeeded() returns and after the
untagged DEV guard has been evaluated; locate the current initialization of
SettingsRuntime and replace it with a lazy/deferred initialization or relocate
it below the early-exit checks to avoid side effects on CLI-style invocations.

… (Claude)

Architecture
- Value-models (DefaultsValueModel/JSONValueModel) now live in each view
  as explicit @State, built once in init; no inline construction, no cache.
- set/reset are sync façades (callable from Bindings) that fire the store
  write in a Task and never mutate `current`; the observation stream is the
  single source of truth. JSONConfigStore now self-notifies subscribers on
  write so JSON-backed controls update deterministically (atomic-rename-safe).
- errorLog and hostActions are non-optional throughout (runtime + every
  section); added NoopSettingsHostActions for previews/tests; removed all
  `if let hostActions` / `== nil` branches.

Correctness / parity
- keepWorkspaceOpen: catalog default true (legacy close-on-last-surface
  semantics) and the "Keep Workspace Open" toggle binds to the inverse.
- Encoding fixes to round-trip with legacy: titlebarControlsStyle Int,
  workspaceButtonFade -> key workspaceButtonsFadeMode default "disabled",
  workspaceTitlebarVisibility -> Bool key workspaceTitlebarVisible default true.
- GlobalHotkey recorder shows "None" when unbound (matches legacy).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously requested changes May 29, 2026

@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)
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift (1)

289-301: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Disable "Clear History" button during loading state.

The button is only disabled when historyCount == 0, but when historyCount is nil (the "loading" state per line 518), the comparison returns false and the button remains enabled. This creates a brief window where users can attempt to clear history before the store has loaded, potentially causing a no-op or race condition.

🔧 Proposed fix
                 .buttonStyle(.bordered)
                 .controlSize(.small)
-                .disabled(historyCount == 0)
+                .disabled(historyCount == nil || historyCount == 0)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift`
around lines 289 - 301, The "Clear History" button is enabled while historyCount
is nil (loading), which allows a premature tap; update the Button's disabled
condition in the SettingsCardRow so it is disabled when historyCount is nil OR
equals 0 (i.e., treat nil as a loading/disabled state) — locate the use of
hostActions.browserHistoryEntryCount() / historyCount and change the
.disabled(...) predicate to guard against nil (for example by checking
historyCount == nil || historyCount == 0) so confirmClearHistory cannot be
triggered while the store is loading.
♻️ Duplicate comments (7)
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AccountSection.swift (1)

13-28: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add DocC comments for public init and body.

The public initializer and body property still lack /// DocC comments.

As per coding guidelines: "Every public symbol in new Swift packages under Packages/ must be documented with Swift-DocC triple-slash comment at time of writing."

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

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AccountSection.swift`
around lines 13 - 28, Add Swift-DocC triple‑slash comments for the public
initializer and the public body property in AccountSection so both public
symbols are documented; specifically, add a /// comment above the public
init(...) describing the purpose and parameters (defaultsStore, catalog,
accountFlow) and a /// comment above the public var body: some View describing
what the view renders (the Account section and its identity row driven by
accountFlow) so the public init and body symbols in AccountSection are
documented per package guidelines.
Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift (3)

110-117: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize user-facing shortcut group titles.

These group title strings are user-visible but hardcoded in English; they should use String(localized:defaultValue:) with matching string catalog entries.

As per coding guidelines: "user-facing Swift text must use String(localized:defaultValue:) or equivalent localized API with matching translated string-catalog entry."

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

In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift`
around lines 110 - 117, The title computed property currently returns hard-coded
English strings; update it to return localized strings using
String(localized:defaultValue:) for each case (refer to the title var on the
ShortcutAction enum) and add matching entries to the string catalog for "App",
"Workspace", "Navigation", "Panes", and "Browser & Find" so translations are
available; ensure each case (e.g., .app, .workspace, .navigation, .panes,
.browser) uses the localized initializer with an appropriate defaultValue and
key/lookup that matches the catalog entries.

178-178: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use repo-standard label for focusRightSidebar.

The label should be "Toggle right sidebar focus" (lowercase) to match the established repo-wide keyboard shortcut naming convention.

Based on learnings: "Use these mappings consistently: ⌘⇧E → focusRightSidebar with the user-facing label 'Toggle right sidebar focus'."

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

In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift` at
line 178, Update the user-facing label for the ShortcutAction enum case
.focusRightSidebar so it matches the repo convention: replace the current string
"Toggle Right Sidebar Focus" with "Toggle right sidebar focus" in
ShortcutAction.swift (the return value for case .focusRightSidebar).

155-233: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize user-facing display names.

All displayName strings are user-visible but hardcoded in English; they should use String(localized:defaultValue:) with matching string catalog entries for every supported locale.

As per coding guidelines: "user-facing Swift text must use String(localized:defaultValue:) or equivalent localized API with matching translated string-catalog entry."

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

In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift`
around lines 155 - 233, The displayName computed property in enum ShortcutAction
returns hardcoded English strings; replace each literal with String(localized:
"ShortcutAction.<case>.displayName", defaultValue: "<current literal>") (or your
project's localized API) so every case (e.g., .openSettings,
.reloadConfiguration, .goToWorkspace, .selectSurfaceByNumber, etc.) uses the
localizer; add corresponding keys to the string catalog
(ShortcutAction.<case>.displayName) for all supported locales and ensure the
displayName switch covers every enum case using those localized keys.
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift (2)

79-100: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add required DocC documentation to public package APIs.

Public symbols makeNSView(context:), updateNSView(_:context:) (lines 79-100) and public properties on RecorderHostButton (placeholder, chordsEnabled, onStroke, onChord, onBareKeyRejected, lines 121-125) still lack Swift-DocC triple-slash comments with required callouts.

📝 Example documentation
/// Creates the underlying `RecorderHostButton` view.
///
/// - Parameter context: The representable context provided by SwiftUI.
/// - Returns: A configured `RecorderHostButton` instance.
public func makeNSView(context: Context) -> RecorderHostButton {

/// Updates the `RecorderHostButton` with the latest SwiftUI state.
///
/// - Parameters:
///   - nsView: The button instance to update.
///   - context: The representable context provided by SwiftUI.
public func updateNSView(_ nsView: RecorderHostButton, context: Context) {

/// The placeholder text displayed when no shortcut is bound.
public var placeholder: String = ""

As per coding guidelines: Every public symbol in new Swift packages under Packages/ must be documented with Swift-DocC triple-slash comment at time of writing, including - Parameter, - Returns:, - Throws: callouts on functions.

Also applies to: 121-125

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

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift`
around lines 79 - 100, Public API symbols are missing Swift-DocC triple-slash
comments; add documentation comments for makeNSView(context:),
updateNSView(_:context:) and the public RecorderHostButton properties
(placeholder, chordsEnabled, onStroke, onChord, onBareKeyRejected). For the
functions include descriptive summary plus the required callouts (- Parameter
for context and nsView, - Returns: for makeNSView, and any - Throws: if
applicable); for the properties add a brief single-line summary describing their
purpose/behavior. Ensure the comments use /// triple-slash DocC format
immediately above each declaration so the package passes the public-symbol
documentation requirement.

241-265: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Media-key recording path is unreachable.

The event monitor on line 243 subscribes to .systemDefined events (media keys), and lines 237-240 document that media keys should be recordable. However, line 265's guard requires charactersIgnoringModifiers to be non-empty, which causes all .systemDefined events to return early without recording. Media keys (Play/Pause, Volume, Brightness, etc.) cannot currently be bound.

🔧 Proposed fix outline

Before line 265, add special handling for .systemDefined events:

private func handleRecordingEvent(_ event: NSEvent) {
    if event.keyCode == 53 /* Escape */ {
        pendingFirst = nil
        stopRecording()
        return
    }

    let keyToken: String
    if event.type == .systemDefined {
        // Extract media key from systemDefined event payload
        guard let mediaToken = Self.extractMediaKeyToken(from: event) else { return }
        keyToken = mediaToken
    } else {
        guard let chars = event.charactersIgnoringModifiers, !chars.isEmpty else { return }
        keyToken = chars.lowercased()
    }
    
    // Continue with hasModifier check and stroke construction using keyToken

Implement extractMediaKeyToken(from:) to parse the media key code from event.data1/event.data2 and return tokens like "media.playPause", "media.volumeUp", etc.

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

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift`
around lines 241 - 265, The media-key path is never reached because
handleRecordingEvent requires charactersIgnoringModifiers; update
handleRecordingEvent (called from installEventMonitor) to special-case
event.type == .systemDefined: parse the media key from the system-defined
payload (event.data1/data2) into a canonical token (e.g. "media.playPause",
"media.volumeUp", etc.) and use that token instead of
charactersIgnoringModifiers; add a helper static method
extractMediaKeyToken(from:) to encapsulate the bit-twiddling and return nil for
unknown keys, and proceed with the existing modifier checks and stroke
construction using the media token when present.
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift (1)

556-574: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize the notification sound picker's sentinel labels.

The three sentinel option labels on lines 557, 572, 573 ("Default", "Custom File...", "None") are hardcoded English strings rendered via Text(option.label) on line 589. These user-facing labels must be localized using String(localized:defaultValue:) while keeping the macOS sound names (Basso, Blow, etc.) unchanged.

🌐 Proposed fix
     private static let systemSoundOptions: [(value: String, label: String)] = [
-        ("default", "Default"),
+        ("default", String(localized: "settings.notifications.sound.default", defaultValue: "Default")),
         ("Basso", "Basso"),
         ...
-        (customSoundFileValue, "Custom File..."),
-        ("none", "None"),
+        (customSoundFileValue, String(localized: "settings.notifications.sound.customFile", defaultValue: "Custom File...")),
+        ("none", String(localized: "settings.notifications.sound.none", defaultValue: "None")),
     ]

As per coding guidelines: user-facing Swift text must use String(localized:defaultValue:) and app string catalogs must include translated entries for every supported locale.

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

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift`
around lines 556 - 574, The systemSoundOptions array currently contains
hardcoded user-facing labels for sentinel values; update the tuples for the
sentinel entries ("default" -> label "Default", customSoundFileValue -> label
"Custom File...", "none" -> label "None") to use localized labels by replacing
the plain strings with String(localized:defaultValue:) calls while leaving the
macOS sound name labels (e.g., "Basso", "Blow", etc.) unchanged; ensure the code
that renders Text(option.label) (where option comes from systemSoundOptions)
continues to receive a localized String and add corresponding keys/entries to
the app string catalog for each sentinel label.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SettingsJSONSection.swift`:
- Around line 8-15: Add Swift-DocC triple‑slash comments to the public struct
SettingsJSONSection and its public initializer init(jsonStore:hostActions:);
document the struct’s purpose as the UI section that displays/edit JSON settings
and explain each initializer parameter (jsonStore: the JSONConfigStore providing
the config data, hostActions: the SettingsHostActions used to perform
host-driven operations) so both public symbols are properly documented per
package guidelines.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/WorkspaceColorsSection.swift`:
- Line 13: WorkspaceColorsSection currently stores an unused property errorLog;
either remove the errorLog parameter/property from WorkspaceColorsSection and
its initializer if it's not needed, or wire it into the view's palette
operations by calling errorLog.log(...) (or the appropriate SettingsErrorLog
API) wherever palette create/update/delete operations occur (e.g., in the
add/remove/rename palette handlers or the viewModel methods invoked by the UI)
so errors produced by WorkspaceColorsSection are recorded; update the
initializer and usages accordingly to keep signatures consistent.

---

Outside diff comments:
In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift`:
- Around line 289-301: The "Clear History" button is enabled while historyCount
is nil (loading), which allows a premature tap; update the Button's disabled
condition in the SettingsCardRow so it is disabled when historyCount is nil OR
equals 0 (i.e., treat nil as a loading/disabled state) — locate the use of
hostActions.browserHistoryEntryCount() / historyCount and change the
.disabled(...) predicate to guard against nil (for example by checking
historyCount == nil || historyCount == 0) so confirmClearHistory cannot be
triggered while the store is loading.

---

Duplicate comments:
In `@Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift`:
- Around line 110-117: The title computed property currently returns hard-coded
English strings; update it to return localized strings using
String(localized:defaultValue:) for each case (refer to the title var on the
ShortcutAction enum) and add matching entries to the string catalog for "App",
"Workspace", "Navigation", "Panes", and "Browser & Find" so translations are
available; ensure each case (e.g., .app, .workspace, .navigation, .panes,
.browser) uses the localized initializer with an appropriate defaultValue and
key/lookup that matches the catalog entries.
- Line 178: Update the user-facing label for the ShortcutAction enum case
.focusRightSidebar so it matches the repo convention: replace the current string
"Toggle Right Sidebar Focus" with "Toggle right sidebar focus" in
ShortcutAction.swift (the return value for case .focusRightSidebar).
- Around line 155-233: The displayName computed property in enum ShortcutAction
returns hardcoded English strings; replace each literal with String(localized:
"ShortcutAction.<case>.displayName", defaultValue: "<current literal>") (or your
project's localized API) so every case (e.g., .openSettings,
.reloadConfiguration, .goToWorkspace, .selectSurfaceByNumber, etc.) uses the
localizer; add corresponding keys to the string catalog
(ShortcutAction.<case>.displayName) for all supported locales and ensure the
displayName switch covers every enum case using those localized keys.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift`:
- Around line 79-100: Public API symbols are missing Swift-DocC triple-slash
comments; add documentation comments for makeNSView(context:),
updateNSView(_:context:) and the public RecorderHostButton properties
(placeholder, chordsEnabled, onStroke, onChord, onBareKeyRejected). For the
functions include descriptive summary plus the required callouts (- Parameter
for context and nsView, - Returns: for makeNSView, and any - Throws: if
applicable); for the properties add a brief single-line summary describing their
purpose/behavior. Ensure the comments use /// triple-slash DocC format
immediately above each declaration so the package passes the public-symbol
documentation requirement.
- Around line 241-265: The media-key path is never reached because
handleRecordingEvent requires charactersIgnoringModifiers; update
handleRecordingEvent (called from installEventMonitor) to special-case
event.type == .systemDefined: parse the media key from the system-defined
payload (event.data1/data2) into a canonical token (e.g. "media.playPause",
"media.volumeUp", etc.) and use that token instead of
charactersIgnoringModifiers; add a helper static method
extractMediaKeyToken(from:) to encapsulate the bit-twiddling and return nil for
unknown keys, and proceed with the existing modifier checks and stroke
construction using the media token when present.

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AccountSection.swift`:
- Around line 13-28: Add Swift-DocC triple‑slash comments for the public
initializer and the public body property in AccountSection so both public
symbols are documented; specifically, add a /// comment above the public
init(...) describing the purpose and parameters (defaultsStore, catalog,
accountFlow) and a /// comment above the public var body: some View describing
what the view renders (the Account section and its identity row driven by
accountFlow) so the public init and body symbols in AccountSection are
documented per package guidelines.

In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift`:
- Around line 556-574: The systemSoundOptions array currently contains hardcoded
user-facing labels for sentinel values; update the tuples for the sentinel
entries ("default" -> label "Default", customSoundFileValue -> label "Custom
File...", "none" -> label "None") to use localized labels by replacing the plain
strings with String(localized:defaultValue:) calls while leaving the macOS sound
name labels (e.g., "Basso", "Blow", etc.) unchanged; ensure the code that
renders Text(option.label) (where option comes from systemSoundOptions)
continues to receive a localized String and add corresponding keys/entries to
the app string catalog for each sentinel label.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8c97b01a-7e9f-43ab-8a92-f4c6112c402a

📥 Commits

Reviewing files that changed from the base of the PR and between 48d980b and 91d7c7b.

📒 Files selected for processing (29)
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/AppCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/AutomationCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/BrowserCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/NotificationsCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/SidebarAppearanceCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/SidebarCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/JSONConfigStore.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Stores/UserDefaultsSettingsStore.swift
  • Packages/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/DefaultsValueModel.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/JSONValueModel.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsHostActions.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsRuntime.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Rows/ShortcutRecorderView.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Scene/SettingsWindowScene.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AccountSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AutomationSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BetaFeaturesSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserImportSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/BrowserSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/GlobalHotkeySection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SettingsJSONSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/WorkspaceColorsSection.swift
  • Sources/HostSettingsActions.swift

Comment on lines +8 to +15
public struct SettingsJSONSection: View {
private let jsonStore: JSONConfigStore
private let hostActions: SettingsHostActions

public init(jsonStore: JSONConfigStore, hostActions: SettingsHostActions) {
self.jsonStore = jsonStore
self.hostActions = hostActions
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document the public struct and initializer.

SettingsJSONSection and its public initializer lack Swift-DocC comments. The struct-level comment should describe the section's purpose, and the initializer should document its parameters.

📝 Suggested documentation
+/// A settings section that displays the cmux.json configuration file
+/// location and provides actions to open the file and view documentation.
+///
+/// The section shows a "User config file" row with the file path and an
+/// "Open" button, plus a "Documentation" row linking to the cmux.json docs.
 `@MainActor`
 public struct SettingsJSONSection: View {
     private let jsonStore: JSONConfigStore
     private let hostActions: SettingsHostActions

+    /// Creates a new settings JSON section.
+    ///
+    /// - Parameters:
+    ///   - jsonStore: The JSON configuration store providing the config file URL.
+    ///   - hostActions: The host actions delegate for opening the config file externally.
     public init(jsonStore: JSONConfigStore, hostActions: SettingsHostActions) {

As per coding guidelines: "Every public symbol in new Swift packages under Packages/ must be documented with Swift-DocC triple-slash comment at the time of writing."

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

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SettingsJSONSection.swift`
around lines 8 - 15, Add Swift-DocC triple‑slash comments to the public struct
SettingsJSONSection and its public initializer init(jsonStore:hostActions:);
document the struct’s purpose as the UI section that displays/edit JSON settings
and explain each initializer parameter (jsonStore: the JSONConfigStore providing
the config data, hostActions: the SettingsHostActions used to perform
host-driven operations) so both public symbols are properly documented per
package guidelines.

public struct WorkspaceColorsSection: View {
private let jsonStore: JSONConfigStore
private let catalog: SettingCatalog
private let errorLog: SettingsErrorLog

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

errorLog parameter is accepted but never used.

The errorLog is stored as a property and passed to the initializer but is never referenced in the view body. Either remove it if unused, or wire it to error-handling logic if errors from palette operations should be logged.

Also applies to: 48-48, 52-52

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

In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/WorkspaceColorsSection.swift`
at line 13, WorkspaceColorsSection currently stores an unused property errorLog;
either remove the errorLog parameter/property from WorkspaceColorsSection and
its initializer if it's not needed, or wire it into the view's palette
operations by calling errorLog.log(...) (or the appropriate SettingsErrorLog
API) wherever palette create/update/delete operations occur (e.g., in the
add/remove/rename palette handlers or the viewModel methods invoked by the UI)
so errors produced by WorkspaceColorsSection are recorded; update the
initializer and usages accordingly to keep signatures consistent.

austinywang added a commit that referenced this pull request Jun 1, 2026
The #4975 Settings rewrite switched the Settings scene from SwiftUI's
`Settings { }` (which gets the modern unified-compact Settings chrome for
free on macOS 14+) to a generic `Window(...)` scene, which defaults to the
legacy titled-window chrome.

Path A (chosen over Path B): keep the `Window(...)` scene so the existing
multi-instance / window-ID deep-link hooks (`openWindow(id: SettingsWindowPresenter.windowID)`)
keep working, and apply the chrome explicitly:

- Add `.windowToolbarStyle(.unifiedCompact)` to the SwiftUI Window scene.
- Extend `SettingsWindowPresenter.configure(window:)` to insert
  `.fullSizeContentView`, set `titlebarAppearsTransparent`/`titleVisibility`,
  attach an `NSToolbar`, and set `toolbarStyle = .unifiedCompact` on the
  AppKit window.

Fixes #5071.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview – cmux — 47d2e510 Deployed May 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