Repository navigation
Extract cmux settings into SPM packages - #4921
azooz2003-bit wants to merge 4 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR introduces two new local Swift packages—CmuxSettings and CmuxSettingsUI—to modularize settings I/O and UI. CmuxSettings provides a file-based store with async file-watching and a JSON-path catalog. CmuxSettingsUI implements searchable settings navigation with keyword-normalized filtering, visual highlighting, and lazy-load infrastructure. The main app refactors to use the catalog as the source of truth for settings paths and re-exports UI types from the new packages. ChangesCmuxSettings and CmuxSettingsUI Packages
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (4 errors, 2 warnings)
✅ Passed checks (12 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR extracts settings model and UI into two zero-dependency SwiftPM packages (
Confidence Score: 4/5Safe to merge for existing settings behavior; the file watcher issues flagged in prior threads are the main correctness risks if consumers of the new CmuxSettingsStore AsyncStream API rely on it in production. The app-side compatibility shims and the KeyboardShortcutSettingsFileStore (which uses the pre-existing ShortcutSettingsFileWatcher with correct restart logic) are unchanged in behavior. The new CmuxSettingsStore and its CmuxSettingsFileWatcher have confirmed defects — silent stream death after atomic writes and a nil stream on fresh install — that have been flagged in earlier review rounds but not yet addressed. The settings search double-allocation is a quality issue in the new package code but does not affect correctness. Packages/CmuxSettings/Sources/CmuxSettings/CmuxSettingsStore.swift (CmuxSettingsFileWatcher restart and missing-file fallback), Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/CmuxSettingsRootView.swift and SettingsSearchIndex.swift (double sidebarEntries evaluation). Important Files Changed
Sequence DiagramsequenceDiagram
participant App as cmuxApp
participant RootView as CmuxSettingsRootView
participant Index as SettingsSearchIndex
participant Store as CmuxSettingsStore
participant Watcher as CmuxSettingsFileWatcher
participant FS as FileSystem
App->>Store: primaryChanges() → AsyncStream
Store->>Watcher: init(url:onChange:)
Watcher->>FS: open(O_EVTONLY)
FS-->>Watcher: fd
Watcher->>Watcher: DispatchSource.resume()
FS-->>Watcher: kqueue event (write/delete/rename)
Watcher->>Store: onChange()
Store->>App: yield(CmuxSettingsFileSnapshot)
App->>RootView: body render
RootView->>Index: entries(matching: searchText)
Index->>Index: allEntries() → settingEntries() [120+ allocs]
Index-->>RootView: [SettingsSearchEntry]
RootView->>Index: sidebarEntries.isEmpty check (second call)
Index->>Index: allEntries() → settingEntries() [120+ allocs again]
Index-->>RootView: [SettingsSearchEntry]
Reviews (4): Last reviewed commit: "Clean up settings UI package boundaries" | Re-trigger Greptile |
| private let onChange: @Sendable () -> Void | ||
| private let lock = NSLock() | ||
| private var isCancelled = false | ||
|
|
||
| init(url: URL, onChange: @escaping @Sendable () -> Void) { | ||
| self.queue = DispatchQueue(label: "com.cmux.settings.file-watcher.\(UUID().uuidString)") | ||
| self.onChange = onChange | ||
|
|
||
| let descriptor = open(url.path, O_EVTONLY) | ||
| guard descriptor >= 0 else { | ||
| fileDescriptor = -1 |
There was a problem hiding this comment.
Stream silently goes dead after atomic write or file deletion
CmuxSettingsFileWatcher's event handler calls onChange() unconditionally on every event but never restarts the watcher. When writePrimaryContents writes atomically (temp file + rename), the .rename event fires once and the DispatchSource continues watching the now-stale file descriptor of the original inode — all subsequent writes are missed. The same happens after an external editor's atomic save or a plain .delete. ShortcutSettingsFileWatcher (the pre-existing watcher) avoids this by calling self.start() inside the handler on .delete/.rename events to re-attach to the new inode. Without equivalent restart logic here, any consumer of primaryChanges() or fileChanges(at:) will silently stop receiving events after the first atomic write.
| fileDescriptor: descriptor, | ||
| eventMask: [.write, .delete, .rename, .revoke], | ||
| queue: queue | ||
| ) | ||
| self.source = source | ||
| source.setEventHandler { [weak self] in | ||
| self?.onChange() | ||
| } | ||
| source.setCancelHandler { | ||
| close(descriptor) | ||
| } | ||
| source.resume() | ||
| } |
There was a problem hiding this comment.
Stream delivers zero events when watched file does not yet exist
If the cmux.json file does not exist when primaryChanges() is first observed (e.g., on a fresh install before bootstrapPrimaryTemplateIfNeeded runs), open(url.path, O_EVTONLY) returns -1, source is nil, and no DispatchSource is ever installed. The stream will never deliver an event, even if the file is created moments later. The pre-existing ShortcutSettingsFileWatcher handles this case by falling back to watching the parent directory and calling start() once the file appears. CmuxSettingsFileWatcher has no such directory fallback.
| public static var all: [CmuxSettingsJSONPathModel] { | ||
| CmuxSettingsCatalog.supportedJSONPathDescriptors | ||
| .sorted { $0.path < $1.path } | ||
| .map(CmuxSettingsJSONPathModel.init(descriptor:)) | ||
| } |
There was a problem hiding this comment.
CmuxSettingsJSONPathList.all is a static var that re-sorts all ~100 descriptors on every call. The source array is a static let, so the result is always the same — computing and sorting it once as a static let eliminates redundant O(n log n) work on every access, including from SwiftUI view bodies that call it through init.
| public static var all: [CmuxSettingsJSONPathModel] { | |
| CmuxSettingsCatalog.supportedJSONPathDescriptors | |
| .sorted { $0.path < $1.path } | |
| .map(CmuxSettingsJSONPathModel.init(descriptor:)) | |
| } | |
| public static let all: [CmuxSettingsJSONPathModel] = | |
| CmuxSettingsCatalog.supportedJSONPathDescriptors | |
| .sorted { $0.path < $1.path } | |
| .map(CmuxSettingsJSONPathModel.init(descriptor:)) |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| static var supportedSettingsJSONPaths: Set<String> { | ||
| CmuxSettingsCatalog.supportedJSONPaths | ||
| } |
There was a problem hiding this comment.
supportedSettingsJSONPaths was a static let constant before this PR; changing it to a static var computed property signals mutability where there is none and forces an indirect call on each access. Since CmuxSettingsCatalog.supportedJSONPaths is itself a static let, the forwarded value is always identical — assigning it directly as a static let preserves the constant-access semantics.
| static var supportedSettingsJSONPaths: Set<String> { | |
| CmuxSettingsCatalog.supportedJSONPaths | |
| } | |
| static let supportedSettingsJSONPaths: Set<String> = CmuxSettingsCatalog.supportedJSONPaths |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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/CmuxSettings/Sources/CmuxSettings/CmuxSettingsCatalog.swift`:
- Around line 3-46: Add Swift-DocC triple-slash comments to every public symbol
in this file: document the CmuxSettingsJSONPathDescriptor type (describe its
purpose), its stored properties path and section, and its public
init(path:section:) (explain behavior when section is nil); document the
CmuxSettingsCatalog enum and each public member: currentSchemaVersion,
schemaURLString, defaultPrimaryURL(_:), defaultLegacyURL(_:),
defaultApplicationSupportLegacyURL(applicationSupportDirectoryURL:releaseBundleIdentifier:),
supportedJSONPathDescriptors, and supportedJSONPaths — each comment should
briefly explain what the symbol represents, its parameters/return value and any
important behavior or defaults so the package meets the DocC requirement.
In `@Packages/CmuxSettings/Sources/CmuxSettings/CmuxSettingsStore.swift`:
- Around line 4-76: Add Swift-DocC triple-slash documentation to every public
symbol in this file: CmuxSettingsFileSnapshot (describe purpose and fields url,
contents), CmuxSettingsStoreError (document the unreadableData case and when it
is thrown), CmuxSettingsStore (summary of the actor’s responsibility and
threading/Sendable behavior) and each public method:
init(primaryURL:fallbackURLs:fileManager:), readActiveSnapshot(),
readPrimarySnapshot(), writePrimaryContents(_:), primaryChanges(),
fileChanges(at:). For each method include a brief summary, parameters, return
value, and documented errors/behavior (e.g., when
CmuxSettingsStoreError.unreadableData is thrown, that fileChanges returns an
AsyncStream of CmuxSettingsFileSnapshot, and that primaryChanges is
nonisolated). Keep comments concise and use triple-slash (///) Swift-DocC style
directly above each public declaration.
- Around line 57-64: The fileChanges watcher currently ignores missing or
deleted files and fails to notify subscribers on create/rename because
CmuxSettingsFileWatcher aborts if open(url.path, O_EVTONLY) fails and the
closure drops events when String(contentsOf:) fails; update
CmuxSettingsStore.fileChanges to watch the parent directory (not just the file)
via CmuxSettingsFileWatcher, emit an initial CmuxSettingsFileSnapshot reflecting
the current readSnapshot (empty contents when missing), and in the watch
callback attempt to read the file and on failure (file not found) yield a
CmuxSettingsFileSnapshot(url: url, contents: "") so create/rename/delete events
produce an empty/cleared snapshot; also make the watcher re-arm or retry opening
when the initial open fails so post-subscription creates trigger updates (keep
continuation.onTermination to call watcher.cancel()).
In
`@Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsCatalogTests.swift`:
- Around line 1-19: Replace XCTest usage with Swift Testing: change `import
XCTest` to `import Testing`, mark the test container `CmuxSettingsCatalogTests`
with `@Suite` and annotate each test function
(`testSupportedJSONPathsContainCurrentSettingsSurface` and
`testDefaultPrimaryURLUsesCmuxJSON`) with `@Test`; replace
`XCTAssertTrue(CmuxSettingsCatalog.supportedJSONPaths.contains(...))` with `try
`#expect`(CmuxSettingsCatalog.supportedJSONPaths.contains(...)).toBeTrue()` and
replace `XCTAssertEqual(CmuxSettingsCatalog.defaultPrimaryURL(homeDirectoryURL:
home).path, "...")` with `try
`#expect`(CmuxSettingsCatalog.defaultPrimaryURL(homeDirectoryURL:
home).path).toEqual("...")`; apply the same changes for the other file
`CmuxSettingsStoreTests.swift` (import, `@Suite/`@Test annotations, and replace
XCTAssert* with `try `#expect``/`try `#require`` as appropriate).
In `@Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsStoreTests.swift`:
- Around line 1-33: Tests currently use XCTest APIs; replace XCTest with the
project's Swift Testing style by changing import XCTest to import Testing,
convert the CmuxSettingsStoreTests XCTestCase subclass into a `@Suite` (or
top-level struct/class per project convention) and convert each test method
(testWriteAndReadPrimarySnapshot,
testActiveSnapshotFallsBackWhenPrimaryIsMissing) into `@Test` functions that use
`#expect`(...) assertions instead of XCTAssertEqual; ensure async/throws
signatures are preserved and that CmuxSettingsStore, writePrimaryContents,
readPrimarySnapshot, and readActiveSnapshot are invoked the same way, and update
assertion calls to `#expect`(snapshot.url).toBe(primaryURL) and
`#expect`(snapshot.contents).toBe(...) (or equivalent `#expect` syntax used in the
codebase).
In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/CmuxSettingsJSONPathModel.swift`:
- Around line 5-46: Add Swift-DocC triple‑slash comments for every public symbol
in this file: document CmuxSettingsJSONPathModel (describe the model), its
public properties id, path, section and the initializer init(descriptor:
CmuxSettingsJSONPathDescriptor); document CmuxSettingsJSONPathList and its
static members all and contains(_:); and document CmuxSettingsJSONPathListView
plus its public init(paths:) and body. For each comment include a brief summary
sentence and where appropriate a Parameters/Returns note that references the
parameter names (e.g., descriptor, path, paths). Ensure all comments use ///
Swift-DocC style immediately above the symbol declarations.
In
`@Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift`:
- Around line 1-16: Replace the XCTest-based test with Swift Testing: remove
"import XCTest" and instead "import Testing"; convert the final class
CmuxSettingsJSONPathModelTests : XCTestCase into a `@Suite` (e.g., annotate a
struct or enum with `@Suite`) and change the test method
testPathModelsAreSortedAndSectioned into a `@Test` function; replace
XCTAssertEqual assertions with the Swift Testing expectation macro (`#expect`)
comparing values (use
`#expect`(paths.map(\.path)).toEqual(paths.map(\.path).sorted()) and
`#expect`(CmuxSettingsJSONPathModel(descriptor: .init(path:
"terminal.showScrollBar")).section).toEqual("terminal") or the equivalent
`#expect`(...) form); keep references to CmuxSettingsJSONPathList.all and
CmuxSettingsJSONPathModel(descriptor: ...) so the same logic is preserved.
🪄 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: e8c09a25-d9f0-4e08-9dd9-d2a2b52c4b98
📒 Files selected for processing (11)
Packages/CmuxSettings/Package.swiftPackages/CmuxSettings/Sources/CmuxSettings/CmuxSettingsCatalog.swiftPackages/CmuxSettings/Sources/CmuxSettings/CmuxSettingsStore.swiftPackages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsCatalogTests.swiftPackages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsStoreTests.swiftPackages/CmuxSettingsUI/Package.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/CmuxSettingsJSONPathModel.swiftPackages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/KeyboardShortcutSettingsFileStore.swiftcmux.xcodeproj/project.pbxproj
| public struct CmuxSettingsJSONPathDescriptor: Equatable, Hashable, Sendable { | ||
| public let path: String | ||
| public let section: String | ||
|
|
||
| public init(path: String, section: String? = nil) { | ||
| self.path = path | ||
| self.section = section ?? path.split(separator: ".", maxSplits: 1).first.map(String.init) ?? path | ||
| } | ||
| } | ||
|
|
||
| public enum CmuxSettingsCatalog { | ||
| public static let currentSchemaVersion = 1 | ||
|
|
||
| public static let schemaURLString = | ||
| "https://raw.githubusercontent.com/manaflow-ai/cmux/main/web/data/cmux.schema.json" | ||
|
|
||
| public static func defaultPrimaryURL(homeDirectoryURL: URL = FileManager.default.homeDirectoryForCurrentUser) -> URL { | ||
| homeDirectoryURL | ||
| .appendingPathComponent(".config", isDirectory: true) | ||
| .appendingPathComponent("cmux", isDirectory: true) | ||
| .appendingPathComponent("cmux.json", isDirectory: false) | ||
| } | ||
|
|
||
| public static func defaultLegacyURL(homeDirectoryURL: URL = FileManager.default.homeDirectoryForCurrentUser) -> URL { | ||
| homeDirectoryURL | ||
| .appendingPathComponent(".config", isDirectory: true) | ||
| .appendingPathComponent("cmux", isDirectory: true) | ||
| .appendingPathComponent("settings.json", isDirectory: false) | ||
| } | ||
|
|
||
| public static func defaultApplicationSupportLegacyURL( | ||
| applicationSupportDirectoryURL: URL, | ||
| releaseBundleIdentifier: String = "com.cmuxterm.app" | ||
| ) -> URL { | ||
| applicationSupportDirectoryURL | ||
| .appendingPathComponent(releaseBundleIdentifier, isDirectory: true) | ||
| .appendingPathComponent("settings.json", isDirectory: false) | ||
| } | ||
|
|
||
| public static let supportedJSONPathDescriptors: [CmuxSettingsJSONPathDescriptor] = | ||
| supportedJSONPathValues.map { CmuxSettingsJSONPathDescriptor(path: $0) } | ||
|
|
||
| public static let supportedJSONPaths: Set<String> = Set(supportedJSONPathValues) | ||
|
|
There was a problem hiding this comment.
Add DocC comments for all public API in this package file.
This new package source exports public types/members without /// documentation, which violates the package API documentation requirement.
As per coding guidelines: Packages/**/Sources/**/*.swift: “Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment…”.
🤖 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/CmuxSettingsCatalog.swift` around
lines 3 - 46, Add Swift-DocC triple-slash comments to every public symbol in
this file: document the CmuxSettingsJSONPathDescriptor type (describe its
purpose), its stored properties path and section, and its public
init(path:section:) (explain behavior when section is nil); document the
CmuxSettingsCatalog enum and each public member: currentSchemaVersion,
schemaURLString, defaultPrimaryURL(_:), defaultLegacyURL(_:),
defaultApplicationSupportLegacyURL(applicationSupportDirectoryURL:releaseBundleIdentifier:),
supportedJSONPathDescriptors, and supportedJSONPaths — each comment should
briefly explain what the symbol represents, its parameters/return value and any
important behavior or defaults so the package meets the DocC requirement.
| public struct CmuxSettingsFileSnapshot: Equatable, Sendable { | ||
| public var url: URL | ||
| public var contents: String | ||
|
|
||
| public init(url: URL, contents: String) { | ||
| self.url = url | ||
| self.contents = contents | ||
| } | ||
| } | ||
|
|
||
| public enum CmuxSettingsStoreError: Error, Equatable { | ||
| case unreadableData(URL) | ||
| } | ||
|
|
||
| public actor CmuxSettingsStore { | ||
| public let primaryURL: URL | ||
| public let fallbackURLs: [URL] | ||
|
|
||
| private let fileManager: FileManager | ||
|
|
||
| public init( | ||
| primaryURL: URL = CmuxSettingsCatalog.defaultPrimaryURL(), | ||
| fallbackURLs: [URL] = [CmuxSettingsCatalog.defaultLegacyURL()], | ||
| fileManager: FileManager = .default | ||
| ) { | ||
| self.primaryURL = primaryURL | ||
| self.fallbackURLs = fallbackURLs | ||
| self.fileManager = fileManager | ||
| } | ||
|
|
||
| public func readActiveSnapshot() throws -> CmuxSettingsFileSnapshot { | ||
| let activeURL = ([primaryURL] + fallbackURLs).first { url in | ||
| fileManager.fileExists(atPath: url.path) | ||
| } ?? primaryURL | ||
| return try readSnapshot(at: activeURL) | ||
| } | ||
|
|
||
| public func readPrimarySnapshot() throws -> CmuxSettingsFileSnapshot { | ||
| try readSnapshot(at: primaryURL) | ||
| } | ||
|
|
||
| public func writePrimaryContents(_ contents: String) throws { | ||
| try fileManager.createDirectory( | ||
| at: primaryURL.deletingLastPathComponent(), | ||
| withIntermediateDirectories: true | ||
| ) | ||
| try contents.write(to: primaryURL, atomically: true, encoding: .utf8) | ||
| } | ||
|
|
||
| public nonisolated func primaryChanges() -> AsyncStream<CmuxSettingsFileSnapshot> { | ||
| fileChanges(at: primaryURL) | ||
| } | ||
|
|
||
| public nonisolated func fileChanges(at url: URL) -> AsyncStream<CmuxSettingsFileSnapshot> { | ||
| AsyncStream { continuation in | ||
| let watcher = CmuxSettingsFileWatcher(url: url) { | ||
| guard let contents = try? String(contentsOf: url, encoding: .utf8) else { return } | ||
| continuation.yield(CmuxSettingsFileSnapshot(url: url, contents: contents)) | ||
| } | ||
| continuation.onTermination = { _ in watcher.cancel() } | ||
| } | ||
| } | ||
|
|
||
| private func readSnapshot(at url: URL) throws -> CmuxSettingsFileSnapshot { | ||
| guard fileManager.fileExists(atPath: url.path) else { | ||
| return CmuxSettingsFileSnapshot(url: url, contents: "") | ||
| } | ||
| guard let contents = try? String(contentsOf: url, encoding: .utf8) else { | ||
| throw CmuxSettingsStoreError.unreadableData(url) | ||
| } | ||
| return CmuxSettingsFileSnapshot(url: url, contents: contents) | ||
| } | ||
| } |
There was a problem hiding this comment.
Document the public store API with Swift-DocC comments.
All exported symbols in this new package source file need /// docs (summary + callouts where applicable).
As per coding guidelines: Packages/**/Sources/**/*.swift: “Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment…”.
🤖 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/CmuxSettingsStore.swift` around
lines 4 - 76, Add Swift-DocC triple-slash documentation to every public symbol
in this file: CmuxSettingsFileSnapshot (describe purpose and fields url,
contents), CmuxSettingsStoreError (document the unreadableData case and when it
is thrown), CmuxSettingsStore (summary of the actor’s responsibility and
threading/Sendable behavior) and each public method:
init(primaryURL:fallbackURLs:fileManager:), readActiveSnapshot(),
readPrimarySnapshot(), writePrimaryContents(_:), primaryChanges(),
fileChanges(at:). For each method include a brief summary, parameters, return
value, and documented errors/behavior (e.g., when
CmuxSettingsStoreError.unreadableData is thrown, that fileChanges returns an
AsyncStream of CmuxSettingsFileSnapshot, and that primaryChanges is
nonisolated). Keep comments concise and use triple-slash (///) Swift-DocC style
directly above each public declaration.
| public nonisolated func fileChanges(at url: URL) -> AsyncStream<CmuxSettingsFileSnapshot> { | ||
| AsyncStream { continuation in | ||
| let watcher = CmuxSettingsFileWatcher(url: url) { | ||
| guard let contents = try? String(contentsOf: url, encoding: .utf8) else { return } | ||
| continuation.yield(CmuxSettingsFileSnapshot(url: url, contents: contents)) | ||
| } | ||
| continuation.onTermination = { _ in watcher.cancel() } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the watcher implementation and the store method
rg -n "class CmuxSettingsFileWatcher|struct CmuxSettingsFileWatcher|CmuxSettingsFileWatcher" -S Packages/CmuxSettings/Sources/CmuxSettings || true
rg -n "nonisolated func fileChanges\\(at url: URL\\)" -S Packages/CmuxSettings/Sources/CmuxSettings || true
# Show the cited regions
sed -n '1,140p' Packages/CmuxSettings/Sources/CmuxSettings/CmuxSettingsStore.swift | cat -n
# If the watcher is in another file, print that too
WATCHER_FILE="$(rg -l "CmuxSettingsFileWatcher" Packages/CmuxSettings/Sources/CmuxSettings | head -n 20)"
echo "Watcher files:"
echo "$WATCHER_FILE"
for f in $WATCHER_FILE; do
echo "---- $f ----"
# Avoid huge output: print first ~220 lines or until match region
sed -n '1,220p' "$f" | cat -n
done
# Also inspect around lines mentioned "Also applies to: 90-111"
# (These are within the same file; we already printed to 140 with line numbers.)Repository: manaflow-ai/cmux
Length of output: 10504
Fix CmuxSettingsStore fileChanges watcher to emit cleared/created states (missing-at-subscription + delete/rename)
CmuxSettingsFileWatcherreturns immediately whenopen(url.path, O_EVTONLY)fails, so create/recreate after subscription produces no updates.- For
.delete/.renameevents, the handler runs but thetry? String(contentsOf:)+guarddrops the event, so consumers never receive the “missing/cleared” snapshot (unlikereadSnapshot, which returnscontents: ""when absent). - Update the watcher to observe the parent directory and (re)arm or retry reading on create/rename/delete, yielding an empty snapshot when the file is missing.
Suggested direction
- let descriptor = open(url.path, O_EVTONLY)
+ // Watch parent directory when file may not exist yet, and (re)arm file-specific watch
+ // after create/rename. Emit an empty snapshot when file is missing/deleted.🤖 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/CmuxSettingsStore.swift` around
lines 57 - 64, The fileChanges watcher currently ignores missing or deleted
files and fails to notify subscribers on create/rename because
CmuxSettingsFileWatcher aborts if open(url.path, O_EVTONLY) fails and the
closure drops events when String(contentsOf:) fails; update
CmuxSettingsStore.fileChanges to watch the parent directory (not just the file)
via CmuxSettingsFileWatcher, emit an initial CmuxSettingsFileSnapshot reflecting
the current readSnapshot (empty contents when missing), and in the watch
callback attempt to read the file and on failure (file not found) yield a
CmuxSettingsFileSnapshot(url: url, contents: "") so create/rename/delete events
produce an empty/cleared snapshot; also make the watcher re-arm or retry opening
when the initial open fails so post-subscription creates trigger updates (keep
continuation.onTermination to call watcher.cancel()).
| import XCTest | ||
| @testable import CmuxSettings | ||
|
|
||
| final class CmuxSettingsCatalogTests: XCTestCase { | ||
| func testSupportedJSONPathsContainCurrentSettingsSurface() { | ||
| XCTAssertTrue(CmuxSettingsCatalog.supportedJSONPaths.contains("app.appearance")) | ||
| XCTAssertTrue(CmuxSettingsCatalog.supportedJSONPaths.contains("terminal.autoResumeAgentSessions")) | ||
| XCTAssertTrue(CmuxSettingsCatalog.supportedJSONPaths.contains("browser.defaultSearchEngine")) | ||
| XCTAssertTrue(CmuxSettingsCatalog.supportedJSONPaths.contains("shortcuts.bindings")) | ||
| } | ||
|
|
||
| func testDefaultPrimaryURLUsesCmuxJSON() { | ||
| let home = URL(fileURLWithPath: "/tmp/cmux-settings-home", isDirectory: true) | ||
| XCTAssertEqual( | ||
| CmuxSettingsCatalog.defaultPrimaryURL(homeDirectoryURL: home).path, | ||
| "/tmp/cmux-settings-home/.config/cmux/cmux.json" | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsCatalogTests.swift"
echo "=== File exists? ==="
ls -la "$FILE" || true
echo "=== First 120 lines of file ==="
sed -n '1,120p' "$FILE" | cat -n
echo "=== Search for XCTest usage in Packages/CmuxSettings/Tests ==="
rg -n --hidden --glob 'Packages/CmuxSettings/Tests/**/*.swift' 'import XCTest|XCTestCase|XCTAssert' || true
echo "=== Search for Swift Testing usage in Packages/CmuxSettings/Tests ==="
rg -n --hidden --glob 'Packages/CmuxSettings/Tests/**/*.swift' 'import Testing|`@Suite`|`@Test`|`#expect`|`#require`' || true
echo "=== Locate review-bot rules for Swift Testing guidance ==="
ls -la .github/review-bot-rules || true
rg -n --hidden 'Swift Testing framework|import Testing|`@Suite`|`@Test`|`#expect`|`#require`' .github/review-bot-rules || trueRepository: manaflow-ai/cmux
Length of output: 4406
Migrate package tests from XCTest to Swift Testing
Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsCatalogTests.swift still uses import XCTest / XCTestCase and XCTAssert* APIs; switch it to Swift Testing (import Testing, @Suite, @Test, #expect, try #require``). The same issue exists in Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsStoreTests.swift.
🧰 Tools
🪛 SwiftLint (0.63.2)
[Warning] 4-4: Classes should have an explicit deinit method
(required_deinit)
🤖 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/Tests/CmuxSettingsTests/CmuxSettingsCatalogTests.swift`
around lines 1 - 19, Replace XCTest usage with Swift Testing: change `import
XCTest` to `import Testing`, mark the test container `CmuxSettingsCatalogTests`
with `@Suite` and annotate each test function
(`testSupportedJSONPathsContainCurrentSettingsSurface` and
`testDefaultPrimaryURLUsesCmuxJSON`) with `@Test`; replace
`XCTAssertTrue(CmuxSettingsCatalog.supportedJSONPaths.contains(...))` with `try
`#expect`(CmuxSettingsCatalog.supportedJSONPaths.contains(...)).toBeTrue()` and
replace `XCTAssertEqual(CmuxSettingsCatalog.defaultPrimaryURL(homeDirectoryURL:
home).path, "...")` with `try
`#expect`(CmuxSettingsCatalog.defaultPrimaryURL(homeDirectoryURL:
home).path).toEqual("...")`; apply the same changes for the other file
`CmuxSettingsStoreTests.swift` (import, `@Suite/`@Test annotations, and replace
XCTAssert* with `try `#expect``/`try `#require`` as appropriate).
| import XCTest | ||
| @testable import CmuxSettings | ||
|
|
||
| final class CmuxSettingsStoreTests: XCTestCase { | ||
| func testWriteAndReadPrimarySnapshot() async throws { | ||
| let directory = FileManager.default.temporaryDirectory | ||
| .appendingPathComponent("cmux-settings-\(UUID().uuidString)", isDirectory: true) | ||
| let primaryURL = directory.appendingPathComponent("cmux.json", isDirectory: false) | ||
| let store = CmuxSettingsStore(primaryURL: primaryURL, fallbackURLs: []) | ||
|
|
||
| try await store.writePrimaryContents("{\"schemaVersion\":1}") | ||
| let snapshot = try await store.readPrimarySnapshot() | ||
|
|
||
| XCTAssertEqual(snapshot.url, primaryURL) | ||
| XCTAssertEqual(snapshot.contents, "{\"schemaVersion\":1}") | ||
| } | ||
|
|
||
| func testActiveSnapshotFallsBackWhenPrimaryIsMissing() async throws { | ||
| let directory = FileManager.default.temporaryDirectory | ||
| .appendingPathComponent("cmux-settings-\(UUID().uuidString)", isDirectory: true) | ||
| try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) | ||
|
|
||
| let primaryURL = directory.appendingPathComponent("cmux.json", isDirectory: false) | ||
| let fallbackURL = directory.appendingPathComponent("settings.json", isDirectory: false) | ||
| try "{\"legacy\":true}".write(to: fallbackURL, atomically: true, encoding: .utf8) | ||
|
|
||
| let store = CmuxSettingsStore(primaryURL: primaryURL, fallbackURLs: [fallbackURL]) | ||
| let snapshot = try await store.readActiveSnapshot() | ||
|
|
||
| XCTAssertEqual(snapshot.url, fallbackURL) | ||
| XCTAssertEqual(snapshot.contents, "{\"legacy\":true}") | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsStoreTests.swift"
echo "=== File: $FILE ==="
if [ -f "$FILE" ]; then
wc -l "$FILE"
sed -n '1,200p' "$FILE" | cat -n
else
echo "Missing: $FILE"
exit 1
fi
echo
echo "=== Search for Swift Testing usage in Packages/CmuxSettings/Tests ==="
rg -n --hidden --no-ignore-vcs "import Testing|`#expect`\\(|`@Test`|`@Suite`" Packages/CmuxSettings/Tests || true
echo
echo "=== Search for XCTest usage in Packages/CmuxSettings/Tests ==="
rg -n --hidden --no-ignore-vcs "import XCTest|XCTestCase|XCTAssert" Packages/CmuxSettings/Tests || true
echo
echo "=== Search for Swift Testing usage across repo tests (quick check) ==="
rg -n --hidden --no-ignore-vcs "import Testing" Packages/**/Tests/**/*.swift | head -n 50 || trueRepository: manaflow-ai/cmux
Length of output: 5164
Replace XCTest with Swift Testing in CmuxSettings package tests
Packages/CmuxSettings/Tests/CmuxSettingsTests/CmuxSettingsStoreTests.swift uses import XCTest, subclasses XCTestCase, and calls XCTAssertEqual. Update it to Swift Testing (import Testing, @Test/@Suite, and #expect(...)).
🧰 Tools
🪛 SwiftLint (0.63.2)
[Warning] 4-4: Classes should have an explicit deinit method
(required_deinit)
🤖 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/Tests/CmuxSettingsTests/CmuxSettingsStoreTests.swift`
around lines 1 - 33, Tests currently use XCTest APIs; replace XCTest with the
project's Swift Testing style by changing import XCTest to import Testing,
convert the CmuxSettingsStoreTests XCTestCase subclass into a `@Suite` (or
top-level struct/class per project convention) and convert each test method
(testWriteAndReadPrimarySnapshot,
testActiveSnapshotFallsBackWhenPrimaryIsMissing) into `@Test` functions that use
`#expect`(...) assertions instead of XCTAssertEqual; ensure async/throws
signatures are preserved and that CmuxSettingsStore, writePrimaryContents,
readPrimarySnapshot, and readActiveSnapshot are invoked the same way, and update
assertion calls to `#expect`(snapshot.url).toBe(primaryURL) and
`#expect`(snapshot.contents).toBe(...) (or equivalent `#expect` syntax used in the
codebase).
| public struct CmuxSettingsJSONPathModel: Identifiable, Equatable, Sendable { | ||
| public var id: String { path } | ||
| public let path: String | ||
| public let section: String | ||
|
|
||
| public init(descriptor: CmuxSettingsJSONPathDescriptor) { | ||
| path = descriptor.path | ||
| section = descriptor.section | ||
| } | ||
| } | ||
|
|
||
| public enum CmuxSettingsJSONPathList { | ||
| public static var all: [CmuxSettingsJSONPathModel] { | ||
| CmuxSettingsCatalog.supportedJSONPathDescriptors | ||
| .sorted { $0.path < $1.path } | ||
| .map(CmuxSettingsJSONPathModel.init(descriptor:)) | ||
| } | ||
|
|
||
| public static func contains(_ path: String) -> Bool { | ||
| CmuxSettingsCatalog.supportedJSONPaths.contains(path) | ||
| } | ||
| } | ||
|
|
||
| public struct CmuxSettingsJSONPathListView: View { | ||
| private let paths: [CmuxSettingsJSONPathModel] | ||
|
|
||
| public init(paths: [CmuxSettingsJSONPathModel] = CmuxSettingsJSONPathList.all) { | ||
| self.paths = paths | ||
| } | ||
|
|
||
| public var body: some View { | ||
| List(paths) { path in | ||
| VStack(alignment: .leading, spacing: 2) { | ||
| Text(verbatim: path.path) | ||
| .font(.system(.body, design: .monospaced)) | ||
| Text(verbatim: path.section) | ||
| .font(.caption) | ||
| .foregroundStyle(.secondary) | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Add Swift-DocC comments to all public symbols in this package source.
The new public model/list/view API is currently undocumented.
As per coding guidelines: Packages/**/Sources/**/*.swift: “Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment…”.
🤖 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/CmuxSettingsJSONPathModel.swift`
around lines 5 - 46, Add Swift-DocC triple‑slash comments for every public
symbol in this file: document CmuxSettingsJSONPathModel (describe the model),
its public properties id, path, section and the initializer init(descriptor:
CmuxSettingsJSONPathDescriptor); document CmuxSettingsJSONPathList and its
static members all and contains(_:); and document CmuxSettingsJSONPathListView
plus its public init(paths:) and body. For each comment include a brief summary
sentence and where appropriate a Parameters/Returns note that references the
parameter names (e.g., descriptor, path, paths). Ensure all comments use ///
Swift-DocC style immediately above the symbol declarations.
| import XCTest | ||
| @testable import CmuxSettingsUI | ||
|
|
||
| final class CmuxSettingsJSONPathModelTests: XCTestCase { | ||
| func testPathModelsAreSortedAndSectioned() { | ||
| let paths = CmuxSettingsJSONPathList.all | ||
|
|
||
| XCTAssertEqual(paths.map(\.path), paths.map(\.path).sorted()) | ||
| XCTAssertEqual( | ||
| CmuxSettingsJSONPathModel( | ||
| descriptor: .init(path: "terminal.showScrollBar") | ||
| ).section, | ||
| "terminal" | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift"
echo "== Exists? =="
ls -la "$FILE" || exit 1
echo
echo "== Lines (1-200) =="
nl -ba "$FILE" | sed -n '1,200p'Repository: manaflow-ai/cmux
Length of output: 291
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift"
echo "== Content (1-200) =="
cat -n "$FILE" | sed -n '1,200p'Repository: manaflow-ai/cmux
Length of output: 660
Convert this package test to Swift Testing (remove XCTest)
Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift imports XCTest, subclasses XCTestCase, and uses XCTAssertEqual. Switch to import Testing with @Suite/@Test and replace assertions with #expect(...).
🧰 Tools
🪛 SwiftLint (0.63.2)
[Warning] 4-4: Classes should have an explicit deinit method
(required_deinit)
🤖 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/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift`
around lines 1 - 16, Replace the XCTest-based test with Swift Testing: remove
"import XCTest" and instead "import Testing"; convert the final class
CmuxSettingsJSONPathModelTests : XCTestCase into a `@Suite` (e.g., annotate a
struct or enum with `@Suite`) and change the test method
testPathModelsAreSortedAndSectioned into a `@Test` function; replace
XCTAssertEqual assertions with the Swift Testing expectation macro (`#expect`)
comparing values (use
`#expect`(paths.map(\.path)).toEqual(paths.map(\.path).sorted()) and
`#expect`(CmuxSettingsJSONPathModel(descriptor: .init(path:
"terminal.showScrollBar")).section).toEqual("terminal") or the equivalent
`#expect`(...) form); keep references to CmuxSettingsJSONPathList.all and
CmuxSettingsJSONPathModel(descriptor: ...) so the same logic is preserved.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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/CmuxSettingsRootView.swift`:
- Around line 3-22: Add Swift-DocC triple-slash comments to all public symbols
in this file: document the CmuxSettingsRootView type with a brief summary and
usage notes, and add /// comments for public members and init parameters
(mention columnVisibility, searchText, minimumSize,
keyboardShortcutActionAliases, consumePendingNavigationTarget, and detail) so
each parameter is described; also document stored SceneStorage properties
selectedSectionRaw and selectedSidebarEntryID if they are intended to be part of
the public API (or mark them fileprivate/private if not) to satisfy the package
guideline requiring every public symbol have a /// summary and any relevant
callouts.
In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsCardNote.swift`:
- Around line 3-17: Add Swift-DocC triple-slash comments for the public API of
SettingsCardNote: document the public struct SettingsCardNote with a short
description, document the public property text (what it contains), document the
public init(_ text: String) with a brief "Parameters" note describing the text
argument, and add a short comment for the public var body describing the view
output; ensure all comments use /// style and are placed immediately above the
respective declarations.
In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsLazyLoadHelpers.swift`:
- Around line 3-5: Add Swift-DocC triple-slash documentation for every public
symbol added in SettingsLazyLoadHelpers.swift — e.g., document the public enum
SettingsScrollCoordinateSpace and its static let name, and likewise add ///
comments for all other public helpers shown in the file (the public types,
functions, and properties between the indicated ranges such as the symbols
around lines 7–10, 12–21, 23–38, and 40–43). For each symbol include a brief
one-line summary and, where helpful, a short parameter/return description for
public functions so the docs satisfy the package guideline that every public API
under Packages/**/Sources/** is documented at creation.
In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigation.swift`:
- Around line 3-559: Add Swift-DocC triple-slash comments to every public symbol
introduced in this file: summarize each type and its purpose for
SettingsNavigationTarget, SettingsNavigationRequest (document
post(_:anchorID:highlight:)), SettingsNavigationDestination,
SettingsSearchHighlightState, the EnvironmentValues extension
(settingsSearchHighlightState), the View extension helpers
(settingsSearchAnchor(_:), settingsSearchAnchors(_:)), SettingsSearchEntry, and
the SettingsSearchIndex API (document public constants and functions such as
defaultSelectionID, entries(matching:), entry(withID:), sectionEntry(for:),
sectionID(_:), settingID(_:idSuffix:), anchorID(forSettingsPath:), and
normalized(_:)), providing a one-line summary for types, and for
functions/methods include parameter and return descriptions (and any
error/behavior notes) using /// - Parameters: and /// - Returns: where
applicable so the public API is fully DocC-documented.
In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchAliases.swift`:
- Around line 1-41: Add Swift-DocC triple‑slash comments for every public symbol
in this file: document the enum SettingsSearchAliasIndex, the nonisolated static
var keyboardShortcutActionAliasesProvider, the static method
sectionAliases(for:) and the static method aliases(target:idSuffix:). For each
comment include a one‑sentence summary of purpose, parameters (for
sectionAliases(for:) and aliases(target:idSuffix:)), and the return value;
ensure visibility and behavior notes (e.g.,
keyboardShortcutActionAliasesProvider is a replaceable provider) are included so
the public API is fully documented per package guidelines.
🪄 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: d6c776cf-ea77-44f5-9421-0ae1873e0ac5
📒 Files selected for processing (11)
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/CmuxSettingsRootView.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsCardNote.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsLazyLoadHelpers.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigation.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchAliases.swiftSources/SettingsCardNote.swiftSources/SettingsLazyLoadHelpers.swiftSources/SettingsNavigation.swiftSources/SettingsSearchAliases.swiftSources/cmuxApp.swiftcmux.xcodeproj/project.pbxproj
| public struct CmuxSettingsRootView<Detail: View>: View { | ||
| @Binding private var columnVisibility: NavigationSplitViewVisibility | ||
| @Binding private var searchText: String | ||
|
|
||
| private let minimumSize: CGSize | ||
| private let keyboardShortcutActionAliases: () -> String | ||
| private let consumePendingNavigationTarget: () -> SettingsNavigationTarget? | ||
| private let detail: () -> Detail | ||
|
|
||
| @SceneStorage("selectedSettingsSection") private var selectedSectionRaw = SettingsNavigationTarget.account.rawValue | ||
| @SceneStorage("selectedSettingsSidebarEntry") private var selectedSidebarEntryID = SettingsSearchIndex.defaultSelectionID | ||
|
|
||
| public init( | ||
| columnVisibility: Binding<NavigationSplitViewVisibility>, | ||
| searchText: Binding<String>, | ||
| minimumSize: CGSize, | ||
| keyboardShortcutActionAliases: @escaping () -> String = { "" }, | ||
| consumePendingNavigationTarget: @escaping () -> SettingsNavigationTarget? = { nil }, | ||
| @ViewBuilder detail: @escaping () -> Detail | ||
| ) { |
There was a problem hiding this comment.
Add Swift-DocC comments for public API in this new package file.
CmuxSettingsRootView and its public members are missing required /// documentation blocks (summary, and callouts where applicable).
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing.
Also applies to: 51-51
🤖 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/CmuxSettingsRootView.swift`
around lines 3 - 22, Add Swift-DocC triple-slash comments to all public symbols
in this file: document the CmuxSettingsRootView type with a brief summary and
usage notes, and add /// comments for public members and init parameters
(mention columnVisibility, searchText, minimumSize,
keyboardShortcutActionAliases, consumePendingNavigationTarget, and detail) so
each parameter is described; also document stored SceneStorage properties
selectedSectionRaw and selectedSidebarEntryID if they are intended to be part of
the public API (or mark them fileprivate/private if not) to satisfy the package
guideline requiring every public symbol have a /// summary and any relevant
callouts.
| public struct SettingsCardNote: View { | ||
| public let text: String | ||
|
|
||
| public init(_ text: String) { | ||
| self.text = text | ||
| } | ||
|
|
||
| public var body: some View { | ||
| Text(text) | ||
| .font(.caption) | ||
| .foregroundColor(.secondary) | ||
| .padding(.horizontal, 14) | ||
| .padding(.vertical, 8) | ||
| .frame(maxWidth: .infinity, alignment: .leading) | ||
| } |
There was a problem hiding this comment.
Public view API needs DocC coverage.
SettingsCardNote public symbols are missing required /// documentation.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsCardNote.swift` around
lines 3 - 17, Add Swift-DocC triple-slash comments for the public API of
SettingsCardNote: document the public struct SettingsCardNote with a short
description, document the public property text (what it contains), document the
public init(_ text: String) with a brief "Parameters" note describing the text
argument, and add a short comment for the public var body describing the view
output; ensure all comments use /// style and are placed immediately above the
respective declarations.
| public enum SettingsScrollCoordinateSpace { | ||
| public static let name = "SettingsScrollCoordinateSpace" | ||
| } |
There was a problem hiding this comment.
Document all public lazy-load helper symbols with Swift-DocC.
This file adds several public APIs without required /// docs.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing.
Also applies to: 7-10, 12-21, 23-38, 40-43
🤖 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/SettingsLazyLoadHelpers.swift`
around lines 3 - 5, Add Swift-DocC triple-slash documentation for every public
symbol added in SettingsLazyLoadHelpers.swift — e.g., document the public enum
SettingsScrollCoordinateSpace and its static let name, and likewise add ///
comments for all other public helpers shown in the file (the public types,
functions, and properties between the indicated ranges such as the symbols
around lines 7–10, 12–21, 23–38, and 40–43). For each symbol include a brief
one-line summary and, where helpful, a short parameter/return description for
public functions so the docs satisfy the package guideline that every public API
under Packages/**/Sources/** is documented at creation.
| public enum SettingsNavigationTarget: String, CaseIterable, Identifiable, Sendable { | ||
| case account | ||
| case app | ||
| case terminal | ||
| case sidebarAppearance | ||
| case betaFeatures | ||
| case automation | ||
| case browser | ||
| case browserImport | ||
| case globalHotkey | ||
| case keyboardShortcuts | ||
| case workspaceColors | ||
| case settingsJSON | ||
| case reset | ||
|
|
||
| public var id: Self { self } | ||
|
|
||
| public var title: String { | ||
| switch self { | ||
| case .account: | ||
| return String(localized: "settings.section.account", defaultValue: "Account") | ||
| case .app: | ||
| return String(localized: "settings.section.app", defaultValue: "App") | ||
| case .terminal: | ||
| return String(localized: "settings.section.terminal", defaultValue: "Terminal") | ||
| case .workspaceColors: | ||
| return String(localized: "settings.section.workspaceColors", defaultValue: "Workspace Colors") | ||
| case .sidebarAppearance: | ||
| return String(localized: "settings.section.sidebarAppearance", defaultValue: "Sidebar") | ||
| case .betaFeatures: | ||
| return String(localized: "settings.section.betaFeatures", defaultValue: "Beta Features") | ||
| case .automation: | ||
| return String(localized: "settings.section.automation", defaultValue: "Automation") | ||
| case .browser: | ||
| return String(localized: "settings.section.browser", defaultValue: "Browser") | ||
| case .browserImport: | ||
| return String(localized: "settings.browser.import", defaultValue: "Import Browser Data") | ||
| case .globalHotkey: | ||
| return String(localized: "settings.section.globalHotkey", defaultValue: "Global Hotkey") | ||
| case .keyboardShortcuts: | ||
| return String(localized: "settings.section.keyboardShortcuts", defaultValue: "Keyboard Shortcuts") | ||
| case .settingsJSON: | ||
| return String(localized: "settings.section.settingsJSON", defaultValue: "cmux.json") | ||
| case .reset: | ||
| return String(localized: "settings.section.reset", defaultValue: "Reset") | ||
| } | ||
| } | ||
|
|
||
| public var symbolName: String { | ||
| switch self { | ||
| case .account: | ||
| return "person.crop.circle" | ||
| case .app: | ||
| return "gearshape" | ||
| case .terminal: | ||
| return "terminal" | ||
| case .workspaceColors: | ||
| return "paintpalette" | ||
| case .sidebarAppearance: | ||
| return "sidebar.left" | ||
| case .betaFeatures: | ||
| return "exclamationmark.triangle" | ||
| case .automation: | ||
| return "wand.and.sparkles" | ||
| case .browser: | ||
| return "globe" | ||
| case .browserImport: | ||
| return "square.and.arrow.down" | ||
| case .globalHotkey: | ||
| return "keyboard.badge.ellipsis" | ||
| case .keyboardShortcuts: | ||
| return "keyboard" | ||
| case .settingsJSON: | ||
| return "doc.text" | ||
| case .reset: | ||
| return "arrow.counterclockwise" | ||
| } | ||
| } | ||
|
|
||
| public var searchText: String { | ||
| switch self { | ||
| case .account: | ||
| return "\(title) sign in team sync" | ||
| case .app: | ||
| return "\(title) appearance language workspace notifications menu bar telemetry" | ||
| case .terminal: | ||
| return "\(title) scrollbar auto resume restore reopen relaunch quit sessions agents claude codex opencode rovodev hibernation idle suspend commands approvals prefixes toggle" | ||
| case .workspaceColors: | ||
| return "\(title) palette tabs" | ||
| case .sidebarAppearance: | ||
| return "\(title) sidebar details branches badges material terminal background" | ||
| case .betaFeatures: | ||
| return "\(title) beta experimental unstable feed dock right sidebar" | ||
| case .automation: | ||
| return "\(title) socket integrations hooks ports claude cursor gemini" | ||
| case .browser: | ||
| return "\(title) search engine links history theme" | ||
| case .browserImport: | ||
| return "\(title) browser import data bookmarks history cookies" | ||
| case .globalHotkey: | ||
| return "\(title) system wide shortcut" | ||
| case .keyboardShortcuts: | ||
| return "\(title) keybindings commands chords" | ||
| case .settingsJSON: | ||
| return "\(title) config file preferences editor documentation schema jsonc reload" | ||
| case .reset: | ||
| return "\(title) defaults" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public enum SettingsNavigationRequest { | ||
| public static let notificationName = Notification.Name("cmux.settings.navigate") | ||
| private static let targetKey = "target" | ||
| private static let anchorKey = "anchor" | ||
| private static let highlightKey = "highlight" | ||
|
|
||
| public static func post(_ target: SettingsNavigationTarget, anchorID: String? = nil, highlight: Bool = false) { | ||
| NotificationCenter.default.post( | ||
| name: notificationName, | ||
| object: nil, | ||
| userInfo: [ | ||
| targetKey: target.rawValue, | ||
| anchorKey: anchorID ?? SettingsSearchIndex.sectionID(for: target), | ||
| highlightKey: highlight | ||
| ] | ||
| ) | ||
| } | ||
|
|
||
| public static func target(from notification: Notification) -> SettingsNavigationTarget? { | ||
| destination(from: notification)?.target | ||
| } | ||
|
|
||
| public static func destination(from notification: Notification) -> SettingsNavigationDestination? { | ||
| guard | ||
| let rawValue = notification.userInfo?[targetKey] as? String, | ||
| let target = SettingsNavigationTarget(rawValue: rawValue) | ||
| else { | ||
| return nil | ||
| } | ||
| let anchorID = notification.userInfo?[anchorKey] as? String | ||
| let shouldHighlight = notification.userInfo?[highlightKey] as? Bool ?? false | ||
| return SettingsNavigationDestination( | ||
| target: target, | ||
| anchorID: anchorID ?? SettingsSearchIndex.sectionID(for: target), | ||
| shouldHighlight: shouldHighlight | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| public struct SettingsNavigationDestination: Sendable { | ||
| public let target: SettingsNavigationTarget | ||
| public let anchorID: String | ||
| public let shouldHighlight: Bool | ||
|
|
||
| public init(target: SettingsNavigationTarget, anchorID: String, shouldHighlight: Bool) { | ||
| self.target = target | ||
| self.anchorID = anchorID | ||
| self.shouldHighlight = shouldHighlight | ||
| } | ||
| } | ||
|
|
||
| public struct SettingsSearchHighlightState: Equatable, Sendable { | ||
| public let anchorID: String? | ||
| public let token: Int | ||
| public let startedAt: Date? | ||
|
|
||
| public init(anchorID: String?, token: Int, startedAt: Date?) { | ||
| self.anchorID = anchorID | ||
| self.token = token | ||
| self.startedAt = startedAt | ||
| } | ||
| } | ||
|
|
||
| private struct SettingsSearchHighlightStateKey: EnvironmentKey { | ||
| static let defaultValue = SettingsSearchHighlightState(anchorID: nil, token: 0, startedAt: nil) | ||
| } | ||
|
|
||
| public extension EnvironmentValues { | ||
| var settingsSearchHighlightState: SettingsSearchHighlightState { | ||
| get { self[SettingsSearchHighlightStateKey.self] } | ||
| set { self[SettingsSearchHighlightStateKey.self] = newValue } | ||
| } | ||
| } | ||
|
|
||
| public extension View { | ||
| @ViewBuilder | ||
| func settingsSearchAnchor(_ anchorID: String?) -> some View { | ||
| if let anchorID { | ||
| settingsSearchAnchors([anchorID]) | ||
| } else { | ||
| self | ||
| } | ||
| } | ||
|
|
||
| @ViewBuilder | ||
| func settingsSearchAnchors(_ anchorIDs: [String]) -> some View { | ||
| let filteredAnchorIDs = anchorIDs.filter { !$0.isEmpty } | ||
| if let primaryAnchorID = filteredAnchorIDs.first { | ||
| self | ||
| .id(primaryAnchorID) | ||
| .modifier(SettingsSearchHighlightModifier(anchorIDs: filteredAnchorIDs)) | ||
| } else { | ||
| self | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private struct SettingsSearchHighlightModifier: ViewModifier { | ||
| @Environment(\.settingsSearchHighlightState) private var highlightState | ||
| let anchorIDs: [String] | ||
|
|
||
| private func matches(_ state: SettingsSearchHighlightState) -> Bool { | ||
| guard let anchorID = state.anchorID else { return false } | ||
| return anchorIDs.contains(anchorID) | ||
| } | ||
|
|
||
| func body(content: Content) -> some View { | ||
| content | ||
| .background { | ||
| if matches(highlightState) { | ||
| TimelineView(.animation) { context in | ||
| let opacity = highlightOpacity(at: context.date, for: highlightState) | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .fill(Color.accentColor.opacity(opacity * 0.24)) | ||
| .overlay( | ||
| RoundedRectangle(cornerRadius: 8, style: .continuous) | ||
| .stroke(Color.accentColor.opacity(opacity), lineWidth: 2.5) | ||
| ) | ||
| .shadow(color: Color.accentColor.opacity(opacity * 0.24), radius: 8, x: 0, y: 0) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private func highlightOpacity(at date: Date, for state: SettingsSearchHighlightState) -> Double { | ||
| guard matches(state), let startedAt = state.startedAt else { return 0 } | ||
| let elapsed = date.timeIntervalSince(startedAt) | ||
| if elapsed < 0.14 { | ||
| return max(0, min(1, elapsed / 0.14)) | ||
| } | ||
| if elapsed < 5 { | ||
| return 1 | ||
| } | ||
| if elapsed < 5.9 { | ||
| return max(0, 1 - ((elapsed - 5) / 0.9)) | ||
| } | ||
| return 0 | ||
| } | ||
| } | ||
|
|
||
| public enum SettingsSearchEntryKind: Sendable { | ||
| case section | ||
| case setting | ||
| } | ||
|
|
||
| public struct SettingsSearchEntry: Identifiable, Sendable { | ||
| public let id: String | ||
| public let kind: SettingsSearchEntryKind | ||
| public let target: SettingsNavigationTarget | ||
| public let title: String | ||
| public let subtitle: String? | ||
| public let symbolName: String | ||
| public let normalizedSearchText: String | ||
|
|
||
| public init( | ||
| id: String, | ||
| kind: SettingsSearchEntryKind, | ||
| target: SettingsNavigationTarget, | ||
| title: String, | ||
| subtitle: String?, | ||
| symbolName: String, | ||
| searchText: String | ||
| ) { | ||
| self.id = id | ||
| self.kind = kind | ||
| self.target = target | ||
| self.title = title | ||
| self.subtitle = subtitle | ||
| self.symbolName = symbolName | ||
| normalizedSearchText = SettingsSearchIndex.normalized("\(title) \(subtitle ?? "") \(searchText)") | ||
| } | ||
| } | ||
|
|
||
| public enum SettingsSearchIndex { | ||
| public static let defaultSelectionID = sectionID(for: .account) | ||
|
|
||
| private static let sectionEntries: [SettingsSearchEntry] = SettingsNavigationTarget.allCases.map { target in | ||
| SettingsSearchEntry( | ||
| id: sectionID(for: target), | ||
| kind: .section, | ||
| target: target, | ||
| title: target.title, | ||
| subtitle: nil, | ||
| symbolName: target.symbolName, | ||
| searchText: "\(target.rawValue) \(target.searchText) \(SettingsSearchAliasIndex.sectionAliases(for: target))" | ||
| ) | ||
| } | ||
|
|
||
| private static let settingEntries: [SettingsSearchEntry] = [ | ||
| setting(.account, "account", String(localized: "settings.section.account", defaultValue: "Account"), "sign in login team sync user profile"), | ||
| setting(.app, "language", String(localized: "settings.app.language", defaultValue: "Language"), "locale translation japanese english restart"), | ||
| setting(.app, "appearance", String(localized: "settings.app.appearance", defaultValue: "Appearance"), "theme light dark system"), | ||
| setting(.app, "app-icon", String(localized: "settings.app.appIcon", defaultValue: "App Icon"), "dock icon alternate"), | ||
| setting(.app, "new-workspace-placement", String(localized: "settings.app.newWorkspacePlacement", defaultValue: "New Workspace Placement"), "workspace order position"), | ||
| setting(.app, "workspace-inherit-working-directory", String(localized: "settings.app.workspaceInheritWorkingDirectory", defaultValue: "Inherit Workspace Working Directory"), "workspace cwd directory current ghostty working-directory"), | ||
| setting(.app, "minimal-mode", String(localized: "settings.app.minimalMode", defaultValue: "Minimal Mode"), "presentation compact chrome"), | ||
| setting(.app, "keep-workspace-open", String(localized: "settings.app.closeWorkspaceOnLastSurfaceShortcut", defaultValue: "Keep Workspace Open When Closing Last Surface"), "close last surface shortcut"), | ||
| setting(.app, "focus-pane-first-click", String(localized: "settings.app.paneFirstClickFocus", defaultValue: "Focus Pane on First Click"), "mouse click focus"), | ||
| setting(.app, "file-drops", String(localized: "settings.app.fileDrop.defaultBehavior", defaultValue: "File Drops"), "drag drop files finder path text terminal editor split preview shift"), | ||
| setting(.app, "preferred-editor", String(localized: "settings.app.preferredEditor", defaultValue: "Open Files With"), "editor code zed subl cmd click file"), | ||
| setting(.app, "supported-file-previews", String(localized: "settings.app.openSupportedFilesInCmux", defaultValue: "Open Supported Files in cmux"), "cmd click file preview pdf image audio video quick look editor"), | ||
| setting(.app, "terminal-config", String(localized: "settings.app.configWindow", defaultValue: "Terminal Config"), "ghostty config merged preview"), | ||
| setting(.app, "markdown-viewer", String(localized: "settings.app.openMarkdownInCmuxViewer", defaultValue: "Open Markdown in cmux Viewer"), "md markdown viewer"), | ||
| setting(.app, "imessage-mode", String(localized: "settings.app.iMessageMode", defaultValue: "iMessage Mode"), "message messages imessage chat prompt prompts submitted message send agent workspace reorder move top"), | ||
| setting(.app, "reorder-notification", String(localized: "settings.app.reorderOnNotification", defaultValue: "Reorder on Notification"), "workspace notification order"), | ||
| setting(.app, "dock-badge", String(localized: "settings.app.dockBadge", defaultValue: "Dock Badge"), "unread count app icon"), | ||
| setting(.app, "menu-bar-only", String(localized: "settings.app.menuBarOnly", defaultValue: "Menu Bar Only"), "dock icon cmd tab"), | ||
| setting(.app, "show-menu-bar", String(localized: "settings.app.showInMenuBar", defaultValue: "Show in Menu Bar"), "menu extra status item"), | ||
| setting(.app, "unread-pane-ring", String(localized: "settings.notifications.paneRing.title", defaultValue: "Unread Pane Ring"), "notification blue ring pane"), | ||
| setting(.app, "pane-flash", String(localized: "settings.notifications.paneFlash.title", defaultValue: "Pane Flash"), "notification flash highlight"), | ||
| setting(.app, "desktop-notifications", String(localized: "settings.notifications.desktop", defaultValue: "Desktop Notifications"), "permission alerts test notification"), | ||
| setting(.app, "notification-sound", String(localized: "settings.notifications.sound.title", defaultValue: "Notification Sound"), "custom sound alert audio"), | ||
| setting(.app, "notification-command", String(localized: "settings.notifications.command", defaultValue: "Notification Command"), "shell command environment variables"), | ||
| setting(.app, "telemetry", String(localized: "settings.app.telemetry", defaultValue: "Send anonymous telemetry"), "analytics crash usage"), | ||
| setting(.app, "warn-before-quit", String(localized: "settings.app.warnBeforeQuit", defaultValue: "Warn Before Quit"), "cmd q confirmation confirmQuit"), | ||
| setting(.app, "warn-before-closing-tab", String(localized: "settings.app.warnBeforeClosingTab", defaultValue: "Warn Before Closing Tab"), "cmd w close tab confirmation"), | ||
| setting( | ||
| .app, | ||
| "warn-before-closing-tab-x-button", | ||
| String(localized: "settings.app.warnBeforeClosingTabXButton", defaultValue: "Warn Before Tab Close Button"), | ||
| "x button close tab confirmation" | ||
| ), | ||
| setting( | ||
| .app, | ||
| "hide-tab-close-button", | ||
| String(localized: "settings.app.hideTabCloseButton", defaultValue: "Hide Tab Close Button"), | ||
| "hide x button close tab" | ||
| ), | ||
| setting(.app, "rename-selects-name", String(localized: "settings.app.renameSelectsName", defaultValue: "Rename Selects Existing Name"), "command palette rename text selection"), | ||
| setting(.app, "palette-search-all", String(localized: "settings.app.commandPaletteSearchAllSurfaces", defaultValue: "Command Palette Searches All Surfaces"), "cmd p search terminal browser markdown"), | ||
| setting(.terminal, "scrollbar", String(localized: "settings.terminal.scrollBar", defaultValue: "Show Terminal Scroll Bar"), "terminal shell scrollback"), | ||
| setting(.terminal, "textbox-max-lines", String(localized: "settings.terminal.textBoxMaxLines", defaultValue: "TextBox Max Lines"), "terminal textbox text box rich input prompt max height lines grow scroll"), | ||
| setting(.terminal, "copy-on-select", String(localized: "settings.terminal.copyOnSelect", defaultValue: "Copy on Selection"), "terminal.copyOnSelect clipboard selection mouse double click triple click"), | ||
| setting(.terminal, "agent-auto-resume", String(localized: "settings.terminal.agentAutoResume", defaultValue: "Resume Agent Sessions on Reopen"), "terminal.autoResumeAgentSessions auto resume restore reopen relaunch quit sessions agents claude code codex opencode rovo dev rovodev toggle"), | ||
| setting(.terminal, "agent-hibernation", String(localized: "settings.terminal.agentHibernation", defaultValue: "Agent Hibernation"), "terminal.agentHibernation idle hibernate suspend background agents claude code codex opencode live terminals"), | ||
| setting(.terminal, "resume-commands", String(localized: "settings.terminal.resumeCommands", defaultValue: "Resume Commands"), "surface resume command approvals prefixes auto restore prompt manual tmux hibernation"), | ||
| setting(.sidebarAppearance, "match-terminal", String(localized: "settings.sidebarAppearance.matchTerminalBackground", defaultValue: "Match Terminal Background"), "sidebar material transparency"), | ||
| setting(.sidebarAppearance, "hide-sidebar-details", String(localized: "settings.app.hideAllSidebarDetails", defaultValue: "Hide All Sidebar Details"), "workspace sidebar compact"), | ||
| setting(.sidebarAppearance, "wrap-workspace-titles", String(localized: "settings.app.wrapWorkspaceTitles", defaultValue: "Wrap Workspace Titles in Sidebar"), "workspace title wrap multiline pr pull request"), | ||
| setting(.sidebarAppearance, "show-workspace-description", String(localized: "settings.app.showWorkspaceDescription", defaultValue: "Show Workspace Description in Sidebar"), "workspace description notes markdown"), | ||
| setting(.sidebarAppearance, "sidebar-branch-layout", String(localized: "settings.app.sidebarBranchLayout", defaultValue: "Sidebar Branch Layout"), "branch directory vertical inline"), | ||
| setting(.sidebarAppearance, "stack-branch-directory", String(localized: "settings.app.stackBranchDirectory", defaultValue: "Stack Branch and Directory"), "branch directory cwd path stack two rows separate lines"), | ||
| setting(.sidebarAppearance, "path-last-segment-only", String(localized: "settings.app.pathLastSegmentOnly", defaultValue: "Truncate Path From Start"), "cwd path directory truncate last segment basename viewport"), | ||
| setting(.sidebarAppearance, "show-notification-message", String(localized: "settings.app.showNotificationMessage", defaultValue: "Show Notification Message in Sidebar"), "workspace latest notification"), | ||
| setting(.sidebarAppearance, "show-branch-directory", String(localized: "settings.app.showBranchDirectory", defaultValue: "Show Branch + Directory in Sidebar"), "git cwd path"), | ||
| setting(.sidebarAppearance, "show-pull-requests", String(localized: "settings.app.showPullRequests", defaultValue: "Show Pull Requests in Sidebar"), "review pr mr link"), | ||
| setting(.sidebarAppearance, "watch-git-status", String(localized: "settings.app.watchGitStatus", defaultValue: "Watch Git Status in Sidebar"), "git status branch watcher index lock"), | ||
| setting(.sidebarAppearance, "make-pr-clickable", String(localized: "settings.app.makeSidebarPullRequestClickable", defaultValue: "Make Sidebar PR Clickable"), "pull requests pull request pr mr review clickable links select workspace row"), | ||
| setting(.sidebarAppearance, "open-pr-links", String(localized: "settings.app.openSidebarPRLinks", defaultValue: "Open Sidebar PR Links in cmux Browser"), "pull request link browser"), | ||
| setting(.sidebarAppearance, "open-port-links", String(localized: "settings.app.openSidebarPortLinks", defaultValue: "Open Sidebar Port Links in cmux Browser"), "port link browser"), | ||
| setting(.sidebarAppearance, "show-ssh", String(localized: "settings.app.showSSH", defaultValue: "Show SSH in Sidebar"), "remote target"), | ||
| setting(.sidebarAppearance, "show-ports", String(localized: "settings.app.showPorts", defaultValue: "Show Listening Ports in Sidebar"), "localhost port"), | ||
| setting(.sidebarAppearance, "show-log", String(localized: "settings.app.showLog", defaultValue: "Show Latest Log in Sidebar"), "status message"), | ||
| setting(.sidebarAppearance, "show-progress", String(localized: "settings.app.showProgress", defaultValue: "Show Progress in Sidebar"), "progress bar"), | ||
| setting(.sidebarAppearance, "show-metadata", String(localized: "settings.app.showMetadata", defaultValue: "Show Custom Metadata in Sidebar"), "report meta status block"), | ||
| setting(.betaFeatures, "dock", String(localized: "settings.betaFeatures.dock", defaultValue: "Dock"), "dock right sidebar terminal controls tui"), | ||
| setting(.automation, "socket-mode", String(localized: "settings.automation.socketMode", defaultValue: "Socket Control Mode"), "unix socket api access password auth"), | ||
| setting(.automation, "socket-password", String(localized: "settings.automation.socketPassword", defaultValue: "Socket Password"), "socket auth credential"), | ||
| setting(.automation, "claude-code", String(localized: "settings.automation.claudeCode", defaultValue: "Claude Code Integration"), "agent hooks notifications"), | ||
| setting(.automation, "claude-path", String(localized: "settings.automation.claudeCode.customPath", defaultValue: "Claude Binary Path"), "custom claude executable"), | ||
| setting(.automation, "ripgrep-path", String(localized: "settings.automation.ripgrep.customPath", defaultValue: "Ripgrep Binary Path"), "custom ripgrep rg executable find search nix"), | ||
| setting(.automation, "subagent-notifications", String(localized: "settings.automation.suppressSubagentNotifications", defaultValue: "Suppress Subagent Notifications"), "nested child agent codex claude hooks notifications"), | ||
| setting(.automation, "cursor", String(localized: "settings.automation.cursor", defaultValue: "Cursor Integration"), "agent hooks notifications"), | ||
| setting(.automation, "gemini", String(localized: "settings.automation.gemini", defaultValue: "Gemini CLI Integration"), "agent hooks notifications"), | ||
| setting(.automation, "port-base", String(localized: "settings.automation.portBase", defaultValue: "Port Base"), "CMUX_PORT start"), | ||
| setting(.automation, "port-range", String(localized: "settings.automation.portRange", defaultValue: "Port Range Size"), "CMUX_PORT_END workspace ports"), | ||
| setting(.browser, "search-engine", String(localized: "settings.browser.searchEngine", defaultValue: "Default Search Engine"), "address bar query google duckduckgo bing kagi brave startpage perplexity exa yahoo ecosia qwant mojeek wikipedia github baidu yandex custom search provider"), | ||
| setting(.browser, "enable-browser", String(localized: "settings.browser.enabled", defaultValue: "Enable cmux Browser"), "webview tabs links"), | ||
| setting(.browser, "search-suggestions", String(localized: "settings.browser.searchSuggestions", defaultValue: "Show Search Suggestions"), "browser address bar suggestions"), | ||
| setting(.browser, "theme", String(localized: "settings.browser.theme", defaultValue: "Browser Theme"), "web appearance light dark system"), | ||
| setting(.browser, "hidden-webview-discard", String(localized: "settings.browser.hiddenWebViewDiscard", defaultValue: "Discard Hidden Browser WebViews"), "memory hidden tabs webview discard unload"), | ||
| setting(.browser, "hidden-webview-discard-delay", String(localized: "settings.browser.hiddenWebViewDiscardDelay", defaultValue: "Hidden WebView Discard Delay"), "memory hidden tabs delay seconds discard"), | ||
| setting(.browser, "terminal-links", String(localized: "settings.browser.openTerminalLinks", defaultValue: "Open Terminal Links in cmux Browser"), "click links browser"), | ||
| setting(.browser, "intercept-open", String(localized: "settings.browser.interceptOpen", defaultValue: "Intercept open http(s) in Terminal"), "open command urls"), | ||
| setting(.browser, "host-whitelist", String(localized: "settings.browser.hostWhitelist", defaultValue: "Hosts to Open in Embedded Browser"), "hosts wildcard terminal links"), | ||
| setting(.browser, "external-patterns", String(localized: "settings.browser.externalPatterns", defaultValue: "URLs to Always Open Externally"), "regex url rules default browser"), | ||
| setting(.browser, "http-allowlist", String(localized: "settings.browser.httpAllowlist", defaultValue: "HTTP Hosts Allowed in Embedded Browser"), "localhost non https warning"), | ||
| setting(.browserImport, "import-data", String(localized: "settings.browser.import", defaultValue: "Import Browser Data"), "bookmarks history cookies profiles"), | ||
| setting(.browserImport, "import-hint", String(localized: "settings.browser.import.hint.show", defaultValue: "Show import hint on blank browser tabs"), "blank tab browser import"), | ||
| setting(.browser, "react-grab", String(localized: "settings.browser.reactGrabVersion", defaultValue: "React Grab Version"), "npm react grab toolbar"), | ||
| setting(.browser, "history", String(localized: "settings.browser.history", defaultValue: "Browsing History"), "clear visited suggestions"), | ||
| setting(.globalHotkey, "enable-hotkey", String(localized: "settings.globalHotkey.enable", defaultValue: "Enable System-Wide Hotkey"), "global shortcut show hide windows"), | ||
| setting(.globalHotkey, "shortcut", String(localized: "settings.section.globalHotkey", defaultValue: "Global Hotkey"), "keyboard recorder command option control"), | ||
| setting(.keyboardShortcuts, "shortcut-chords", String(localized: "settings.shortcuts.chords", defaultValue: "Shortcut Chords"), "tmux multi step keybindings"), | ||
| setting(.keyboardShortcuts, "reset-defaults", String(localized: "settings.shortcuts.resetDefaults", defaultValue: "Reset Default Shortcuts"), "restore built in builtin defaults keybindings hotkeys chords commands"), | ||
| setting(.keyboardShortcuts, "shortcuts", String(localized: "settings.section.keyboardShortcuts", defaultValue: "Keyboard Shortcuts"), "keybindings commands"), | ||
| setting(.workspaceColors, "indicator", String(localized: "settings.workspaceColors.indicator", defaultValue: "Workspace Color Indicator"), "tab color indicator"), | ||
| setting(.workspaceColors, "selection", String(localized: "settings.workspaceColors.selectionColor", defaultValue: "Selection Highlight"), "selected workspace background"), | ||
| setting(.workspaceColors, "badge", String(localized: "settings.workspaceColors.notificationBadgeColor", defaultValue: "Notification Badge"), "unread notification color"), | ||
| setting(.workspaceColors, "palette", String(localized: "settings.workspaceColors.resetPalette", defaultValue: "Reset Palette"), "named colors palette"), | ||
| setting(.settingsJSON, "open-file", String(localized: "settings.settingsJSON.openFile", defaultValue: "Open cmux.json"), "config json file editor dotfiles"), | ||
| setting(.settingsJSON, "documentation", String(localized: "settings.settingsJSON.documentation", defaultValue: "Documentation"), "cmux json schema reference docs"), | ||
| setting(.reset, "reset-all", String(localized: "settings.reset.resetAll", defaultValue: "Reset All Settings"), "restore defaults") | ||
| ] | ||
|
|
||
| private static let allEntries = sectionEntries + settingEntries | ||
|
|
||
| private static let entriesByID: [String: SettingsSearchEntry] = Dictionary( | ||
| uniqueKeysWithValues: allEntries.map { ($0.id, $0) } | ||
| ) | ||
|
|
||
| private static let settingsPathAnchorIDs: [String: String] = [ | ||
| "rightSidebar.beta.dock.enabled": settingID(for: .betaFeatures, idSuffix: "dock"), | ||
| "app.language": settingID(for: .app, idSuffix: "language"), | ||
| "app.appearance": settingID(for: .app, idSuffix: "appearance"), | ||
| "app.appIcon": settingID(for: .app, idSuffix: "app-icon"), | ||
| "app.newWorkspacePlacement": settingID(for: .app, idSuffix: "new-workspace-placement"), | ||
| "app.workspaceInheritWorkingDirectory": settingID(for: .app, idSuffix: "workspace-inherit-working-directory"), | ||
| "app.minimalMode": settingID(for: .app, idSuffix: "minimal-mode"), | ||
| "app.keepWorkspaceOpenWhenClosingLastSurface": settingID(for: .app, idSuffix: "keep-workspace-open"), | ||
| "app.focusPaneOnFirstClick": settingID(for: .app, idSuffix: "focus-pane-first-click"), | ||
| "fileDrop.defaultBehavior": settingID(for: .app, idSuffix: "file-drops"), | ||
| "app.fileDropDefaultBehavior": settingID(for: .app, idSuffix: "file-drops"), | ||
| "app.preferredEditor": settingID(for: .app, idSuffix: "preferred-editor"), | ||
| "app.openSupportedFilesInCmux": settingID(for: .app, idSuffix: "supported-file-previews"), | ||
| "app.openMarkdownInCmuxViewer": settingID(for: .app, idSuffix: "markdown-viewer"), | ||
| "app.iMessageMode": settingID(for: .app, idSuffix: "imessage-mode"), | ||
| "app.reorderOnNotification": settingID(for: .app, idSuffix: "reorder-notification"), | ||
| "notifications.dockBadge": settingID(for: .app, idSuffix: "dock-badge"), | ||
| "app.menuBarOnly": settingID(for: .app, idSuffix: "menu-bar-only"), | ||
| "notifications.showInMenuBar": settingID(for: .app, idSuffix: "show-menu-bar"), | ||
| "notifications.unreadPaneRing": settingID(for: .app, idSuffix: "unread-pane-ring"), | ||
| "notifications.paneFlash": settingID(for: .app, idSuffix: "pane-flash"), | ||
| "notifications.sound": settingID(for: .app, idSuffix: "notification-sound"), | ||
| "notifications.customSoundFilePath": settingID(for: .app, idSuffix: "notification-sound"), | ||
| "notifications.command": settingID(for: .app, idSuffix: "notification-command"), | ||
| "app.sendAnonymousTelemetry": settingID(for: .app, idSuffix: "telemetry"), | ||
| "app.confirmQuit": settingID(for: .app, idSuffix: "warn-before-quit"), | ||
| "app.warnBeforeQuit": settingID(for: .app, idSuffix: "warn-before-quit"), | ||
| "app.warnBeforeClosingTab": settingID(for: .app, idSuffix: "warn-before-closing-tab"), | ||
| "app.warnBeforeClosingTabXButton": settingID(for: .app, idSuffix: "warn-before-closing-tab-x-button"), | ||
| "app.hideTabCloseButton": settingID(for: .app, idSuffix: "hide-tab-close-button"), | ||
| "app.renameSelectsExistingName": settingID(for: .app, idSuffix: "rename-selects-name"), | ||
| "app.commandPaletteSearchesAllSurfaces": settingID(for: .app, idSuffix: "palette-search-all"), | ||
| "sidebar.hideAllDetails": settingID(for: .sidebarAppearance, idSuffix: "hide-sidebar-details"), | ||
| "sidebar.wrapWorkspaceTitles": settingID(for: .sidebarAppearance, idSuffix: "wrap-workspace-titles"), | ||
| "sidebar.showWorkspaceDescription": settingID(for: .sidebarAppearance, idSuffix: "show-workspace-description"), | ||
| "sidebar.branchLayout": settingID(for: .sidebarAppearance, idSuffix: "sidebar-branch-layout"), | ||
| "sidebar.stackBranchDirectory": settingID(for: .sidebarAppearance, idSuffix: "stack-branch-directory"), | ||
| "sidebar.pathLastSegmentOnly": settingID(for: .sidebarAppearance, idSuffix: "path-last-segment-only"), | ||
| "sidebar.showNotificationMessage": settingID(for: .sidebarAppearance, idSuffix: "show-notification-message"), | ||
| "sidebar.showBranchDirectory": settingID(for: .sidebarAppearance, idSuffix: "show-branch-directory"), | ||
| "sidebar.showPullRequests": settingID(for: .sidebarAppearance, idSuffix: "show-pull-requests"), | ||
| "sidebar.watchGitStatus": settingID(for: .sidebarAppearance, idSuffix: "watch-git-status"), | ||
| "sidebar.makePullRequestsClickable": settingID(for: .sidebarAppearance, idSuffix: "make-pr-clickable"), | ||
| "sidebar.openPullRequestLinksInCmuxBrowser": settingID(for: .sidebarAppearance, idSuffix: "open-pr-links"), | ||
| "sidebar.openPortLinksInCmuxBrowser": settingID(for: .sidebarAppearance, idSuffix: "open-port-links"), | ||
| "sidebar.showSSH": settingID(for: .sidebarAppearance, idSuffix: "show-ssh"), | ||
| "sidebar.showPorts": settingID(for: .sidebarAppearance, idSuffix: "show-ports"), | ||
| "sidebar.showLog": settingID(for: .sidebarAppearance, idSuffix: "show-log"), | ||
| "sidebar.showProgress": settingID(for: .sidebarAppearance, idSuffix: "show-progress"), | ||
| "sidebar.showCustomMetadata": settingID(for: .sidebarAppearance, idSuffix: "show-metadata"), | ||
| "terminal.showScrollBar": settingID(for: .terminal, idSuffix: "scrollbar"), | ||
| "terminal.textBoxMaxLines": settingID(for: .terminal, idSuffix: "textbox-max-lines"), | ||
| "terminal.copyOnSelect": settingID(for: .terminal, idSuffix: "copy-on-select"), | ||
| "terminal.autoResumeAgentSessions": settingID(for: .terminal, idSuffix: "agent-auto-resume"), | ||
| "terminal.agentHibernation.enabled": settingID(for: .terminal, idSuffix: "agent-hibernation"), | ||
| "terminal.agentHibernation.idleSeconds": settingID(for: .terminal, idSuffix: "agent-hibernation"), | ||
| "terminal.agentHibernation.maxLiveTerminals": settingID(for: .terminal, idSuffix: "agent-hibernation"), | ||
| "workspaceColors.indicatorStyle": settingID(for: .workspaceColors, idSuffix: "indicator"), | ||
| "workspaceColors.selectionColor": settingID(for: .workspaceColors, idSuffix: "selection"), | ||
| "workspaceColors.notificationBadgeColor": settingID(for: .workspaceColors, idSuffix: "badge"), | ||
| "sidebarAppearance.matchTerminalBackground": settingID(for: .sidebarAppearance, idSuffix: "match-terminal"), | ||
| "automation.socketControlMode": settingID(for: .automation, idSuffix: "socket-mode"), | ||
| "automation.socketPassword": settingID(for: .automation, idSuffix: "socket-password"), | ||
| "automation.claudeCodeIntegration": settingID(for: .automation, idSuffix: "claude-code"), | ||
| "automation.claudeBinaryPath": settingID(for: .automation, idSuffix: "claude-path"), | ||
| "automation.ripgrepBinaryPath": settingID(for: .automation, idSuffix: "ripgrep-path"), | ||
| "automation.suppressSubagentNotifications": settingID(for: .automation, idSuffix: "subagent-notifications"), | ||
| "automation.cursorIntegration": settingID(for: .automation, idSuffix: "cursor"), | ||
| "automation.geminiIntegration": settingID(for: .automation, idSuffix: "gemini"), | ||
| "automation.portBase": settingID(for: .automation, idSuffix: "port-base"), | ||
| "automation.portRange": settingID(for: .automation, idSuffix: "port-range"), | ||
| "browser.enabled": settingID(for: .browser, idSuffix: "enable-browser"), | ||
| "browser.defaultSearchEngine": settingID(for: .browser, idSuffix: "search-engine"), | ||
| "browser.customSearchEngineName": settingID(for: .browser, idSuffix: "search-engine"), | ||
| "browser.customSearchEngineURLTemplate": settingID(for: .browser, idSuffix: "search-engine"), | ||
| "browser.showSearchSuggestions": settingID(for: .browser, idSuffix: "search-suggestions"), | ||
| "browser.theme": settingID(for: .browser, idSuffix: "theme"), | ||
| "browser.discardHiddenWebViews": settingID(for: .browser, idSuffix: "hidden-webview-discard"), | ||
| "browser.hiddenWebViewDiscardDelaySeconds": settingID(for: .browser, idSuffix: "hidden-webview-discard-delay"), | ||
| "browser.openTerminalLinksInCmuxBrowser": settingID(for: .browser, idSuffix: "terminal-links"), | ||
| "browser.interceptTerminalOpenCommandInCmuxBrowser": settingID(for: .browser, idSuffix: "intercept-open"), | ||
| "browser.hostsToOpenInEmbeddedBrowser": settingID(for: .browser, idSuffix: "host-whitelist"), | ||
| "browser.urlsToAlwaysOpenExternally": settingID(for: .browser, idSuffix: "external-patterns"), | ||
| "browser.insecureHttpHostsAllowedInEmbeddedBrowser": settingID(for: .browser, idSuffix: "http-allowlist"), | ||
| "browser.showImportHintOnBlankTabs": settingID(for: .browserImport, idSuffix: "import-hint"), | ||
| "browser.reactGrabVersion": settingID(for: .browser, idSuffix: "react-grab"), | ||
| "shortcuts.bindings": settingID(for: .keyboardShortcuts, idSuffix: "shortcuts") | ||
| ] | ||
|
|
||
| public static func entries(matching query: String) -> [SettingsSearchEntry] { | ||
| let tokens = normalizedTokens(for: query) | ||
| guard !tokens.isEmpty else { return sectionEntries } | ||
| return allEntries.filter { entry in | ||
| tokens.allSatisfy { token in entry.normalizedSearchText.contains(token) } | ||
| } | ||
| } | ||
|
|
||
| public static func entry(withID id: String) -> SettingsSearchEntry? { | ||
| entriesByID[id] | ||
| } | ||
|
|
||
| public static func sectionEntry(for target: SettingsNavigationTarget) -> SettingsSearchEntry { | ||
| entriesByID[sectionID(for: target)] ?? sectionEntries[0] | ||
| } | ||
|
|
||
| public static func sectionID(for target: SettingsNavigationTarget) -> String { "section:\(target.rawValue)" } | ||
| public static func settingID(for target: SettingsNavigationTarget, idSuffix: String) -> String { "setting:\(target.rawValue):\(idSuffix)" } | ||
|
|
||
| public static func anchorID(forSettingsPath path: String) -> String? { | ||
| settingsPathAnchorIDs[path] | ||
| } | ||
|
|
||
| public static func normalized(_ text: String) -> String { | ||
| text.folding(options: [.caseInsensitive, .diacriticInsensitive], locale: .current) | ||
| } | ||
|
|
||
| private static func setting( | ||
| _ target: SettingsNavigationTarget, | ||
| _ idSuffix: String, | ||
| _ title: String, | ||
| _ searchText: String | ||
| ) -> SettingsSearchEntry { | ||
| SettingsSearchEntry( | ||
| id: settingID(for: target, idSuffix: idSuffix), | ||
| kind: .setting, | ||
| target: target, | ||
| title: title, | ||
| subtitle: target.title, | ||
| symbolName: target.symbolName, | ||
| searchText: "\(target.rawValue) \(idSuffix) \(target.searchText) \(searchText) \(SettingsSearchAliasIndex.aliases(target: target, idSuffix: idSuffix))" | ||
| ) | ||
| } | ||
|
|
||
| private static func normalizedTokens(for query: String) -> [String] { | ||
| normalized(query) | ||
| .split { character in | ||
| character.unicodeScalars.allSatisfy { scalar in | ||
| CharacterSet.whitespacesAndNewlines.contains(scalar) | ||
| || CharacterSet.punctuationCharacters.contains(scalar) | ||
| } | ||
| } | ||
| .map(String.init) | ||
| } | ||
| } |
There was a problem hiding this comment.
This new public navigation/search API surface must be DocC-documented.
The file exposes many public symbols without required triple-slash docs. Please add API docs (summary + parameter/returns/throws where relevant) before merge.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigation.swift`
around lines 3 - 559, Add Swift-DocC triple-slash comments to every public
symbol introduced in this file: summarize each type and its purpose for
SettingsNavigationTarget, SettingsNavigationRequest (document
post(_:anchorID:highlight:)), SettingsNavigationDestination,
SettingsSearchHighlightState, the EnvironmentValues extension
(settingsSearchHighlightState), the View extension helpers
(settingsSearchAnchor(_:), settingsSearchAnchors(_:)), SettingsSearchEntry, and
the SettingsSearchIndex API (document public constants and functions such as
defaultSelectionID, entries(matching:), entry(withID:), sectionEntry(for:),
sectionID(_:), settingID(_:idSuffix:), anchorID(forSettingsPath:), and
normalized(_:)), providing a one-line summary for types, and for
functions/methods include parameter and return descriptions (and any
error/behavior notes) using /// - Parameters: and /// - Returns: where
applicable so the public API is fully DocC-documented.
| public enum SettingsSearchAliasIndex { | ||
| public nonisolated(unsafe) static var keyboardShortcutActionAliasesProvider: () -> String = { "" } | ||
|
|
||
| public static func sectionAliases(for target: SettingsNavigationTarget) -> String { | ||
| switch target { | ||
| case .account: | ||
| return localized("settings.search.alias.section.account", defaultValue: "auth authentication login logout sign in sign out email user profile team") | ||
| case .app: | ||
| return localized("settings.search.alias.section.app", defaultValue: "general preferences prefs behavior chrome dock menubar menu bar status notifications telemetry") | ||
| case .terminal: | ||
| return localized("settings.search.alias.section.terminal", defaultValue: "shell scrollback scrollbar scroll bar ghostty tty pty") | ||
| case .sidebarAppearance: | ||
| return localized("settings.search.alias.section.sidebarAppearance", defaultValue: "sidebar left rail navigation details branches badges material terminal background") | ||
| case .betaFeatures: | ||
| return localized("settings.search.alias.section.betaFeatures", defaultValue: "beta experimental unstable preview dock right sidebar") | ||
| case .automation: | ||
| return localized("settings.search.alias.section.automation", defaultValue: "api cli control socket mcp agents hooks ports") | ||
| case .browser: | ||
| return localized("settings.search.alias.section.browser", defaultValue: "web webview address bar omnibar links urls embedded default browser") | ||
| case .browserImport: | ||
| return localized("settings.search.alias.section.browserImport", defaultValue: "chrome safari firefox brave edge arc bookmarks history cookies profiles") | ||
| case .globalHotkey: | ||
| return localized("settings.search.alias.section.globalHotkey", defaultValue: "system shortcut global keyboard show hide bring forward") | ||
| case .keyboardShortcuts: | ||
| return localized("settings.search.alias.section.keyboardShortcuts", defaultValue: "keybinds key bindings hotkeys chords accelerators commands") | ||
| case .workspaceColors: | ||
| return localized("settings.search.alias.section.workspaceColors", defaultValue: "tab colors palette accent badge selected highlight") | ||
| case .settingsJSON: | ||
| return localized("settings.search.alias.section.settingsJSON", defaultValue: "configuration config file json jsonc dotfile ~/.config schema docs") | ||
| case .reset: | ||
| return localized("settings.search.alias.section.reset", defaultValue: "factory defaults restore clear preferences") | ||
| } | ||
| } | ||
|
|
||
| public static func aliases(target: SettingsNavigationTarget, idSuffix: String) -> String { | ||
| let aliases = settingAliases["\(target.rawValue):\(idSuffix)"] ?? "" | ||
| if target == .keyboardShortcuts, idSuffix == "shortcuts" { | ||
| return "\(aliases) \(keyboardShortcutActionAliases)" | ||
| } | ||
| return aliases | ||
| } |
There was a problem hiding this comment.
Add required Swift-DocC comments to exported alias-index API.
Public declarations in this new package file are undocumented.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchAliases.swift`
around lines 1 - 41, Add Swift-DocC triple‑slash comments for every public
symbol in this file: document the enum SettingsSearchAliasIndex, the nonisolated
static var keyboardShortcutActionAliasesProvider, the static method
sectionAliases(for:) and the static method aliases(target:idSuffix:). For each
comment include a one‑sentence summary of purpose, parameters (for
sectionAliases(for:) and aliases(target:idSuffix:)), and the return value;
ensure visibility and behavior notes (e.g.,
keyboardShortcutActionAliasesProvider is a replaceable provider) are included so
the public API is fully documented per package guidelines.
| private var sidebarEntries: [SettingsSearchEntry] { | ||
| SettingsSearchAliasIndex.keyboardShortcutActionAliasesProvider = keyboardShortcutActionAliases | ||
| return SettingsSearchIndex.entries(matching: searchText) | ||
| } |
There was a problem hiding this comment.
Global state mutation inside SwiftUI
body
sidebarEntries is a computed property called directly from body (at the sidebarEntries.isEmpty guard), yet its first statement writes to a shared global: SettingsSearchAliasIndex.keyboardShortcutActionAliasesProvider = keyboardShortcutActionAliases. SwiftUI can call body speculatively during diffing and animation passes, causing this assignment to fire repeatedly and unpredictably. Any reader of keyboardShortcutActionAliasesProvider from a non-main-actor context (e.g., a background search or the static let settingEntries lazy initialiser) races against these writes.
| @@ -0,0 +1,149 @@ | |||
| public enum SettingsSearchAliasIndex { | |||
| public nonisolated(unsafe) static var keyboardShortcutActionAliasesProvider: () -> String = { "" } | |||
There was a problem hiding this comment.
nonisolated(unsafe) shared mutable closure without actor protection
keyboardShortcutActionAliasesProvider is assigned from the SwiftUI render path (main actor, via sidebarEntries) and read from aliases(target:idSuffix:), which is also called during static let settingEntries lazy initialisation. The nonisolated(unsafe) annotation opts out of all Swift Concurrency data-race protection, meaning a concurrent reader (e.g., if settingEntries triggers from a non-main context) and a body-time writer can race. A @MainActor-isolated stored property or an actor-owned injection point would give the same capability without the unsafe bypass.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchIndex.swift (1)
2-3:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPublic
SettingsSearchIndexAPI needs Swift-DocC documentation.Line 2 and Lines 230-257 add public API (
defaultSelectionID, query/lookup/id/normalize helpers) without required///documentation blocks.As per coding guidelines,
Packages/**/Sources/**/*.swift: Everypublicsymbol in any new Swift package underPackages/must be documented with a Swift-DocC triple-slash comment at the time of writing.Also applies to: 230-257
🤖 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/SettingsSearchIndex.swift` around lines 2 - 3, Add Swift-DocC triple-slash documentation for every public symbol introduced in this file: the SettingsSearchIndex enum itself and all public members such as defaultSelectionID and the public helper functions referenced in the comment (e.g., sectionID(for:), query/..., lookup/..., id/..., normalize/...). For each public property/function add a concise /// summary; for functions include /// - Parameters: and /// - Returns: entries as appropriate and a short example or discussion if behavior is non-obvious, ensuring every public declaration has a triple-slash comment per the Packages/**/Sources/** guideline.
♻️ Duplicate comments (1)
Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift (1)
17-47:⚠️ Potential issue | 🟠 Major | ⚡ Quick winStop adding XCTest-based tests in package test targets; migrate this file to Swift Testing.
Line 17 onward adds more
XCTestassertions/helpers inPackages/**/Tests, which violates the package-test standard and compounds existing non-compliance. Convert this file toimport Testing,@Suite,@Test, and#expect(...)/try#require(...).#!/bin/bash set -euo pipefail # Verify package tests are using Swift Testing (and not XCTest) rg -n --type=swift 'import XCTest|:\s*XCTestCase\b|XCTAssert' Packages -g '**/Tests/**/*.swift'As per coding guidelines:
Packages/**/Tests/**/*.swift: "Use Swift Testing framework for all unit and integration tests in packages. Annotate tests with@Test, group with@Suite, assert with#expect(...)andtry#require(...). Useimport Testing, notimport XCTest."🤖 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/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift` around lines 17 - 47, Replace the XCTest-based test with Swift Testing: remove "import XCTest" and add "import Testing", convert the testKeyboardShortcutAliasesAreInjectedPerSearch() XCTestCase-style test into a top-level `@Suite` with an `@Test` annotated function, replace XCTAssertFalse/ XCTAssertTrue assertions with `#expect`(...) (and use try `#require`(...) if any throwing setup is needed), and keep the helper searchResultIDs(query:keyboardShortcutActionAliases:) (make it top-level or internal so the test can call it) while still invoking SettingsSearchIndex.entries(matching:keyboardShortcutActionAliases:) and mapping .id for assertions; ensure the test uses `#expect`(searchResultIDs(...).contains(...)).toBeFalse/True semantics per the Swift Testing matcher style.
🤖 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/SettingsNavigationRequest.swift`:
- Around line 3-52: Add Swift-DocC triple-slash documentation for all public
symbols introduced here: document SettingsNavigationRequest.notificationName,
the SettingsNavigationRequest.post(_:anchorID:highlight:) method,
SettingsNavigationRequest.target(from:),
SettingsNavigationRequest.destination(from:), and the
SettingsNavigationDestination type including its public properties (target,
anchorID, shouldHighlight) and init. For each symbol add a concise ///
description explaining its purpose, parameters and return value (where
applicable) and any default behavior (e.g., how anchorID is derived and what
highlight means) so the public API meets the package documentation guideline.
In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigationTarget.swift`:
- Around line 3-112: Add Swift-DocC triple-slash comments for the public API
introduced here: document the public enum SettingsNavigationTarget with a brief
overview of its purpose, and add /// comments for each public member: the id
property, the title computed property, the symbolName computed property, and the
searchText computed property; ensure each comment is a single-line DocC summary
(optionally follow with a short note if needed) so all public symbols in this
file satisfy the package documentation guideline.
In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchEntry.swift`:
- Around line 3-34: Add Swift-DocC triple-slash comments for all public API in
this file: add a /// comment explaining the purpose of the
SettingsSearchEntryKind enum and each case, add a /// comment for the
SettingsSearchEntry struct describing the model, and add /// comments for the
public initializer (documenting parameters) and each public property (id, kind,
target, title, subtitle, symbolName, normalizedSearchText); ensure comments use
Swift-DocC style (///) and include brief descriptions for the initializer
parameters and the meaning of normalizedSearchText so the public API is fully
documented.
In
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchHighlight.swift`:
- Around line 3-47: Add Swift-DocC triple-slash comments to each public symbol
so they are documented: document the SettingsSearchHighlightState struct
(describe what anchorID, token, and startedAt represent and their semantics),
the EnvironmentValues.settingsSearchHighlightState property (explain it stores
the current highlight state in the environment), and the View extensions
settingsSearchAnchor(_:) and settingsSearchAnchors(_:) (describe their behavior,
parameters, and effect on the view, e.g., assigning id and applying
SettingsSearchHighlightModifier). Ensure comments use /// and mention
lifetime/usage notes where relevant to meet package documentation guidelines.
---
Outside diff comments:
In `@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchIndex.swift`:
- Around line 2-3: Add Swift-DocC triple-slash documentation for every public
symbol introduced in this file: the SettingsSearchIndex enum itself and all
public members such as defaultSelectionID and the public helper functions
referenced in the comment (e.g., sectionID(for:), query/..., lookup/..., id/...,
normalize/...). For each public property/function add a concise /// summary; for
functions include /// - Parameters: and /// - Returns: entries as appropriate
and a short example or discussion if behavior is non-obvious, ensuring every
public declaration has a triple-slash comment per the Packages/**/Sources/**
guideline.
---
Duplicate comments:
In
`@Packages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift`:
- Around line 17-47: Replace the XCTest-based test with Swift Testing: remove
"import XCTest" and add "import Testing", convert the
testKeyboardShortcutAliasesAreInjectedPerSearch() XCTestCase-style test into a
top-level `@Suite` with an `@Test` annotated function, replace XCTAssertFalse/
XCTAssertTrue assertions with `#expect`(...) (and use try `#require`(...) if any
throwing setup is needed), and keep the helper
searchResultIDs(query:keyboardShortcutActionAliases:) (make it top-level or
internal so the test can call it) while still invoking
SettingsSearchIndex.entries(matching:keyboardShortcutActionAliases:) and mapping
.id for assertions; ensure the test uses
`#expect`(searchResultIDs(...).contains(...)).toBeFalse/True semantics per the
Swift Testing matcher style.
🪄 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: 32bba8f0-362d-45c7-8bd3-fda3329e21e7
📒 Files selected for processing (8)
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/CmuxSettingsRootView.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigationRequest.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigationTarget.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchAliases.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchEntry.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchHighlight.swiftPackages/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchIndex.swiftPackages/CmuxSettingsUI/Tests/CmuxSettingsUITests/CmuxSettingsJSONPathModelTests.swift
| public enum SettingsNavigationRequest { | ||
| public static let notificationName = Notification.Name("cmux.settings.navigate") | ||
| private static let targetKey = "target" | ||
| private static let anchorKey = "anchor" | ||
| private static let highlightKey = "highlight" | ||
|
|
||
| public static func post(_ target: SettingsNavigationTarget, anchorID: String? = nil, highlight: Bool = false) { | ||
| NotificationCenter.default.post( | ||
| name: notificationName, | ||
| object: nil, | ||
| userInfo: [ | ||
| targetKey: target.rawValue, | ||
| anchorKey: anchorID ?? SettingsSearchIndex.sectionID(for: target), | ||
| highlightKey: highlight | ||
| ] | ||
| ) | ||
| } | ||
|
|
||
| public static func target(from notification: Notification) -> SettingsNavigationTarget? { | ||
| destination(from: notification)?.target | ||
| } | ||
|
|
||
| public static func destination(from notification: Notification) -> SettingsNavigationDestination? { | ||
| guard | ||
| let rawValue = notification.userInfo?[targetKey] as? String, | ||
| let target = SettingsNavigationTarget(rawValue: rawValue) | ||
| else { | ||
| return nil | ||
| } | ||
| let anchorID = notification.userInfo?[anchorKey] as? String | ||
| let shouldHighlight = notification.userInfo?[highlightKey] as? Bool ?? false | ||
| return SettingsNavigationDestination( | ||
| target: target, | ||
| anchorID: anchorID ?? SettingsSearchIndex.sectionID(for: target), | ||
| shouldHighlight: shouldHighlight | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| public struct SettingsNavigationDestination: Sendable { | ||
| public let target: SettingsNavigationTarget | ||
| public let anchorID: String | ||
| public let shouldHighlight: Bool | ||
|
|
||
| public init(target: SettingsNavigationTarget, anchorID: String, shouldHighlight: Bool) { | ||
| self.target = target | ||
| self.anchorID = anchorID | ||
| self.shouldHighlight = shouldHighlight | ||
| } | ||
| } |
There was a problem hiding this comment.
Document the public navigation request and destination API with Swift-DocC.
Line 3 and Line 42 introduce public types and members without /// docs (notificationName, post, target(from:), destination(from:), SettingsNavigationDestination properties/init).
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigationRequest.swift`
around lines 3 - 52, Add Swift-DocC triple-slash documentation for all public
symbols introduced here: document SettingsNavigationRequest.notificationName,
the SettingsNavigationRequest.post(_:anchorID:highlight:) method,
SettingsNavigationRequest.target(from:),
SettingsNavigationRequest.destination(from:), and the
SettingsNavigationDestination type including its public properties (target,
anchorID, shouldHighlight) and init. For each symbol add a concise ///
description explaining its purpose, parameters and return value (where
applicable) and any default behavior (e.g., how anchorID is derived and what
highlight means) so the public API meets the package documentation guideline.
| public enum SettingsNavigationTarget: String, CaseIterable, Identifiable, Sendable { | ||
| case account | ||
| case app | ||
| case terminal | ||
| case sidebarAppearance | ||
| case betaFeatures | ||
| case automation | ||
| case browser | ||
| case browserImport | ||
| case globalHotkey | ||
| case keyboardShortcuts | ||
| case workspaceColors | ||
| case settingsJSON | ||
| case reset | ||
|
|
||
| public var id: Self { self } | ||
|
|
||
| public var title: String { | ||
| switch self { | ||
| case .account: | ||
| return String(localized: "settings.section.account", defaultValue: "Account") | ||
| case .app: | ||
| return String(localized: "settings.section.app", defaultValue: "App") | ||
| case .terminal: | ||
| return String(localized: "settings.section.terminal", defaultValue: "Terminal") | ||
| case .workspaceColors: | ||
| return String(localized: "settings.section.workspaceColors", defaultValue: "Workspace Colors") | ||
| case .sidebarAppearance: | ||
| return String(localized: "settings.section.sidebarAppearance", defaultValue: "Sidebar") | ||
| case .betaFeatures: | ||
| return String(localized: "settings.section.betaFeatures", defaultValue: "Beta Features") | ||
| case .automation: | ||
| return String(localized: "settings.section.automation", defaultValue: "Automation") | ||
| case .browser: | ||
| return String(localized: "settings.section.browser", defaultValue: "Browser") | ||
| case .browserImport: | ||
| return String(localized: "settings.browser.import", defaultValue: "Import Browser Data") | ||
| case .globalHotkey: | ||
| return String(localized: "settings.section.globalHotkey", defaultValue: "Global Hotkey") | ||
| case .keyboardShortcuts: | ||
| return String(localized: "settings.section.keyboardShortcuts", defaultValue: "Keyboard Shortcuts") | ||
| case .settingsJSON: | ||
| return String(localized: "settings.section.settingsJSON", defaultValue: "cmux.json") | ||
| case .reset: | ||
| return String(localized: "settings.section.reset", defaultValue: "Reset") | ||
| } | ||
| } | ||
|
|
||
| public var symbolName: String { | ||
| switch self { | ||
| case .account: | ||
| return "person.crop.circle" | ||
| case .app: | ||
| return "gearshape" | ||
| case .terminal: | ||
| return "terminal" | ||
| case .workspaceColors: | ||
| return "paintpalette" | ||
| case .sidebarAppearance: | ||
| return "sidebar.left" | ||
| case .betaFeatures: | ||
| return "exclamationmark.triangle" | ||
| case .automation: | ||
| return "wand.and.sparkles" | ||
| case .browser: | ||
| return "globe" | ||
| case .browserImport: | ||
| return "square.and.arrow.down" | ||
| case .globalHotkey: | ||
| return "keyboard.badge.ellipsis" | ||
| case .keyboardShortcuts: | ||
| return "keyboard" | ||
| case .settingsJSON: | ||
| return "doc.text" | ||
| case .reset: | ||
| return "arrow.counterclockwise" | ||
| } | ||
| } | ||
|
|
||
| public var searchText: String { | ||
| switch self { | ||
| case .account: | ||
| return "\(title) sign in team sync" | ||
| case .app: | ||
| return "\(title) appearance language workspace notifications menu bar telemetry" | ||
| case .terminal: | ||
| return "\(title) scrollbar auto resume restore reopen relaunch quit sessions agents claude codex opencode rovodev hibernation idle suspend commands approvals prefixes toggle" | ||
| case .workspaceColors: | ||
| return "\(title) palette tabs" | ||
| case .sidebarAppearance: | ||
| return "\(title) sidebar details branches badges material terminal background" | ||
| case .betaFeatures: | ||
| return "\(title) beta experimental unstable feed dock right sidebar" | ||
| case .automation: | ||
| return "\(title) socket integrations hooks ports claude cursor gemini" | ||
| case .browser: | ||
| return "\(title) search engine links history theme" | ||
| case .browserImport: | ||
| return "\(title) browser import data bookmarks history cookies" | ||
| case .globalHotkey: | ||
| return "\(title) system wide shortcut" | ||
| case .keyboardShortcuts: | ||
| return "\(title) keybindings commands chords" | ||
| case .settingsJSON: | ||
| return "\(title) config file preferences editor documentation schema jsonc reload" | ||
| case .reset: | ||
| return "\(title) defaults" | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Add Swift-DocC comments for the public enum and computed properties.
Line 3 and Lines 18-82 expose public API (SettingsNavigationTarget, id, title, symbolName, searchText) without required /// documentation in this new package source file.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsNavigationTarget.swift`
around lines 3 - 112, Add Swift-DocC triple-slash comments for the public API
introduced here: document the public enum SettingsNavigationTarget with a brief
overview of its purpose, and add /// comments for each public member: the id
property, the title computed property, the symbolName computed property, and the
searchText computed property; ensure each comment is a single-line DocC summary
(optionally follow with a short note if needed) so all public symbols in this
file satisfy the package documentation guideline.
| public enum SettingsSearchEntryKind: Sendable { | ||
| case section | ||
| case setting | ||
| } | ||
|
|
||
| public struct SettingsSearchEntry: Identifiable, Sendable { | ||
| public let id: String | ||
| public let kind: SettingsSearchEntryKind | ||
| public let target: SettingsNavigationTarget | ||
| public let title: String | ||
| public let subtitle: String? | ||
| public let symbolName: String | ||
| public let normalizedSearchText: String | ||
|
|
||
| public init( | ||
| id: String, | ||
| kind: SettingsSearchEntryKind, | ||
| target: SettingsNavigationTarget, | ||
| title: String, | ||
| subtitle: String?, | ||
| symbolName: String, | ||
| searchText: String | ||
| ) { | ||
| self.id = id | ||
| self.kind = kind | ||
| self.target = target | ||
| self.title = title | ||
| self.subtitle = subtitle | ||
| self.symbolName = symbolName | ||
| normalizedSearchText = SettingsSearchIndex.normalized("\(title) \(subtitle ?? "") \(searchText)") | ||
| } | ||
| } |
There was a problem hiding this comment.
Add required Swift-DocC docs for public search entry models.
Lines 3-34 expose public package API (SettingsSearchEntryKind, SettingsSearchEntry, and its initializer/properties) without /// documentation.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchEntry.swift`
around lines 3 - 34, Add Swift-DocC triple-slash comments for all public API in
this file: add a /// comment explaining the purpose of the
SettingsSearchEntryKind enum and each case, add a /// comment for the
SettingsSearchEntry struct describing the model, and add /// comments for the
public initializer (documenting parameters) and each public property (id, kind,
target, title, subtitle, symbolName, normalizedSearchText); ensure comments use
Swift-DocC style (///) and include brief descriptions for the initializer
parameters and the meaning of normalizedSearchText so the public API is fully
documented.
| public struct SettingsSearchHighlightState: Equatable, Sendable { | ||
| public let anchorID: String? | ||
| public let token: Int | ||
| public let startedAt: Date? | ||
|
|
||
| public init(anchorID: String?, token: Int, startedAt: Date?) { | ||
| self.anchorID = anchorID | ||
| self.token = token | ||
| self.startedAt = startedAt | ||
| } | ||
| } | ||
|
|
||
| private struct SettingsSearchHighlightStateKey: EnvironmentKey { | ||
| static let defaultValue = SettingsSearchHighlightState(anchorID: nil, token: 0, startedAt: nil) | ||
| } | ||
|
|
||
| public extension EnvironmentValues { | ||
| var settingsSearchHighlightState: SettingsSearchHighlightState { | ||
| get { self[SettingsSearchHighlightStateKey.self] } | ||
| set { self[SettingsSearchHighlightStateKey.self] = newValue } | ||
| } | ||
| } | ||
|
|
||
| public extension View { | ||
| @ViewBuilder | ||
| func settingsSearchAnchor(_ anchorID: String?) -> some View { | ||
| if let anchorID { | ||
| settingsSearchAnchors([anchorID]) | ||
| } else { | ||
| self | ||
| } | ||
| } | ||
|
|
||
| @ViewBuilder | ||
| func settingsSearchAnchors(_ anchorIDs: [String]) -> some View { | ||
| let filteredAnchorIDs = anchorIDs.filter { !$0.isEmpty } | ||
| if let primaryAnchorID = filteredAnchorIDs.first { | ||
| self | ||
| .id(primaryAnchorID) | ||
| .modifier(SettingsSearchHighlightModifier(anchorIDs: filteredAnchorIDs)) | ||
| } else { | ||
| self | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Document public highlight state and view/environment extensions.
Lines 3-47 define public symbols (SettingsSearchHighlightState, EnvironmentValues.settingsSearchHighlightState, settingsSearchAnchor, settingsSearchAnchors) without required Swift-DocC comments.
As per coding guidelines, Packages/**/Sources/**/*.swift: Every public symbol in any new 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/CmuxSettingsUI/Sources/CmuxSettingsUI/SettingsSearchHighlight.swift`
around lines 3 - 47, Add Swift-DocC triple-slash comments to each public symbol
so they are documented: document the SettingsSearchHighlightState struct
(describe what anchorID, token, and startedAt represent and their semantics),
the EnvironmentValues.settingsSearchHighlightState property (explain it stores
the current highlight state in the environment), and the View extensions
settingsSearchAnchor(_:) and settingsSearchAnchors(_:) (describe their behavior,
parameters, and effect on the view, e.g., assigning id and applying
SettingsSearchHighlightModifier). Ensure comments use /// and mention
lifetime/usage notes where relevant to meet package documentation guidelines.
Summary
Verification
Summary by CodeRabbit
Release Notes
New Features
Refactor
Tests