Allow customizing File Explorer git status colors (#3848) - #3893
austinywang wants to merge 23 commits into
Conversation
File Explorer status colors were owned only by FileExplorerStyle, which made editor/theme matching impossible without changing source. This adds a config-owned override map for fileExplorer.gitStatusColors while preserving the existing style palettes as fallback values. Constraint: Do not build or run local tests before CI; user explicitly forbids local tests and direct xcodebuild. Rejected: Add another style-specific switch in the row view | keeps config and presentation coupled. Confidence: medium Scope-risk: moderate Directive: Keep File Explorer appearance overrides in the settings/defaults boundary; do not push per-row config parsing into FileExplorerView. Tested: python3 -m json.tool web/data/cmux.schema.json; git diff --check Not-tested: Local Swift tests/builds per user instruction; CI will run after push.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds cmux.json support for fileExplorer.gitStatusColors: schema and allowlist, docs and template entry, parser and managed-UserDefaults persistence with validation/logging, a main-actor cached NSColor resolver used by File Explorer with fallback, Xcode project wiring, and tests. ChangesFile Explorer Git Status Colors Feature
Sequence DiagramsequenceDiagram
participant SettingsFile as cmux.json
participant SettingsStore as CmuxSettingsFileStore
participant Defaults as UserDefaults
participant Palette as FileExplorerGitStatusColorPalette
participant NotifCenter as Notification Center
participant FileExplorerStyle
SettingsFile->>SettingsStore: parseSettingsFile reads fileExplorer.gitStatusColors
SettingsStore->>SettingsStore: parseFileExplorerSection validates & normalizes entries
SettingsStore->>Defaults: write normalized hex map to fileExplorer.gitStatusColors
SettingsStore->>NotifCenter: post .fileExplorerStyleDidChange
NotifCenter->>Palette: reload reads stored hex map
Palette->>Palette: parse hex -> NSColor and cache per-status
FileExplorerStyle->>Palette: request resolved color for status (fallback if missing)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (2 errors, 1 warning)
✅ Passed checks (13 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 |
…rer-git-status-colors
Greptile SummaryThis PR adds
Confidence Score: 5/5Safe to merge; all previously flagged actor-isolation and cache-timing issues have been resolved, and the managed-defaults lifecycle correctly handles both applying and removing the new key. The new parsing, caching, and notification wiring are all sound. The backup/restore path in restoreUserDefaultsBackup calls applyManagedDefaultSideEffects, so removing gitStatusColors from config also triggers a palette reload and fileExplorerStyleDidChange with no stale-cache window. The only finding is a new English-only prose description in the configuration docs page, which follows an existing pattern in that file and does not affect runtime behavior. web/app/[locale]/docs/configuration/page.tsx — new hardcoded English description text should be added to web/messages/*.json for all 20 supported locales. Important Files Changed
Sequence DiagramsequenceDiagram
participant CF as cmux.json parser
participant KS as CmuxSettingsFileStore
participant UD as UserDefaults
participant PA as FileExplorerGitStatusColorPalette
participant NC as NotificationCenter
participant FE as FileExplorerView (rows)
CF->>KS: parseFileExplorerSection(gitStatusColors)
KS->>KS: "normalize status names & hex values"
KS->>KS: "snapshot.managedUserDefaults[key] = .stringDictionary(normalizedColors)"
KS->>UD: defaults.set(normalizedColors, forKey: key)
KS->>KS: applyManagedDefaultSideEffects(key)
Note over KS: Thread.isMainThread check
KS->>PA: "reloadSharedPaletteOnMainThread() [sync, @MainActor]"
PA->>UD: dictionary(forKey: key)
PA->>PA: "colorsByStatus = loadedColors(from: defaults)"
KS->>NC: post(.fileExplorerStyleDidChange)
NC->>FE: observer fires → reload rows
FE->>PA: gitColor(for: status) → resolvedColor(for:fallback:)
PA-->>FE: cached NSColor (override or style fallback)
Reviews (19): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile |
…rer-git-status-colors
Review feedback flagged two small issues in the file explorer git status color path: new diagnostics bypassed structured logging, and every cell color lookup reached into UserDefaults. The palette is now cached behind a small model that refreshes from file-explorer appearance/defaults notifications, and config parse warnings use Logger. Constraint: Project policy forbids local test runs; validation relies on CI plus the required tagged reload build. Rejected: Leave low-priority review-bot feedback unresolved | user asked iterate-pr to continue autonomously. Confidence: medium Scope-risk: narrow Directive: Keep file explorer color preference reads out of row and cell configuration paths. Tested: git diff --check Not-tested: Local tests/build completion; tagged reload is still queued behind the shared xcodebuild lock.
…rer-git-status-colors
…rer-git-status-colors
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/FileExplorerStore.swift`:
- Around line 211-295: Move the git-status color settings and cache out of
FileExplorerStore.swift by extracting the entire
FileExplorerGitStatusColorSettings enum and the
FileExplorerGitStatusColorPalette class (including methods
normalizedStatusName(_:), resolvedColor(for:fallback:defaults:),
FileExplorerGitStatusColorPalette.shared, init(...), deinit,
color(for:fallback:defaults:), reload(), and loadedColors(from:)) into a new
dedicated Swift file (e.g., FileExplorerGitStatusColors.swift); preserve their
APIs, imports (Foundation/AppKit), and the UserDefaults key constant, update any
references in the codebase to import/use the new file, and ensure the
NotificationCenter observer registration and NSLock usage remain identical so
behavior and threading are unchanged.
🪄 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: 403f1dff-a6ad-4150-b6a9-764d226ef131
📒 Files selected for processing (7)
Sources/CmuxSettingsJSONPathSupport.swiftSources/FileExplorerStore.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftcmuxTests/KeyboardShortcutSettingsFileStoreStartupTests.swiftweb/app/[locale]/docs/configuration/page.tsxweb/data/cmux.schema.json
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/FileExplorerGitStatusColors.swift`:
- Around line 75-77: The current guard in FileExplorerGitStatusColors that casts
defaults.dictionary(forKey: FileExplorerGitStatusColorSettings.userDefaultsKey)
as [String: String] should be changed to read as [String: Any] and then iterate
over each key/value to validate and coerce only valid string color values into a
new [String: String] dictionary; drop entries with wrong types or malformed
strings (e.g., non-String or invalid hex) so one bad value doesn't discard all
overrides. Update the code paths that use the resulting colors map (the same
variable used after the guard) to consume this sanitized dictionary, and keep
the existing userDefaultsKey and surrounding logic (e.g., inside
FileExplorerGitStatusColors initialization) unchanged otherwise.
🪄 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: f26e5c83-0b13-47c4-a7d6-72b63d302980
📒 Files selected for processing (3)
GhosttyTabs.xcodeproj/project.pbxprojSources/FileExplorerGitStatusColors.swiftSources/FileExplorerStore.swift
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@cmuxTests/KeyboardShortcutSettingsFileStoreStartupTests.swift`:
- Line 473: The call to FileExplorerStyle.highDensity.gitColor(for: .modified)
is unused and should either be removed or documented; update the test in
KeyboardShortcutSettingsFileStoreStartupTests to delete the discarded call if it
serves no purpose, or if it is intentionally warming a cache to influence later
behavior, replace it with a clear comment describing that intention (mentioning
FileExplorerStyle.highDensity.gitColor(for: .modified) and the expected
cache/override effect) so the purpose is explicit.
🪄 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: 832d4283-5ace-43c3-90ba-bb011454094b
📒 Files selected for processing (2)
Sources/FileExplorerGitStatusColors.swiftcmuxTests/KeyboardShortcutSettingsFileStoreStartupTests.swift
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fbb6463. Configure here.
…rer-git-status-colors
Dismissed as stale: the CodeRabbit inline thread for extracting FileExplorerGitStatusColorSettings/FileExplorerGitStatusColorPalette was addressed in a later commit, the thread is resolved/outdated, and CodeRabbit posted a follow-up confirmation.
…rer-git-status-colors
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/KeyboardShortcutSettingsFileStore.swift`:
- Around line 645-647: The warning currently logs sourcePath as public in
Self.logger.warning (the call that includes "Ignoring unknown file explorer git
status ... in \(sourcePath, privacy: .public)"); change the logging so that path
values are treated as private/redacted (e.g., use privacy: .private or a
redacted placeholder) instead of .public for sourcePath (and any other similar
occurrences around the same block) to avoid exposing local filesystem paths in
production logs.
🪄 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: 35e096e5-0bea-4452-8e27-90840c3b21c8
📒 Files selected for processing (9)
GhosttyTabs.xcodeproj/project.pbxprojSources/CmuxSettingsJSONPathSupport.swiftSources/FileExplorerGitStatusColors.swiftSources/FileExplorerStore.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftcmuxTests/KeyboardShortcutSettingsFileStoreStartupTests.swiftweb/app/[locale]/docs/configuration/page.tsxweb/data/cmux.schema.json
|
Review follow-up applied in 7bc332d: fileExplorer.gitStatusColors warning logs now redact sourcePath with private privacy, and reloadSharedPaletteOnMainThread is explicitly @mainactor. The Greptile inline thread is resolved; CI is rerunning on the fix commit. Local tests were not run per repo instructions. |
…rer-git-status-colors
Stale bot review after follow-up fix: sourcePath is now logged with privacy: .private at both warning sites in Sources/KeyboardShortcutSettingsFileStore.swift, and the latest CodeRabbit check is green.
…rer-git-status-colors
…-3848-file-explorer-git-status-colors
…rer-git-status-colors

Summary
Architecture
Chose a config-owned settings boundary instead of adding more style-specific view logic. FileExplorerGitStatusColorSettings owns the defaults key and status-name normalization; FileExplorerStyle only supplies fallback colors.
Verification
Local Swift tests/builds were not run per instruction; CI should exercise the added XCTest.
Closes #3848
Note
Low Risk
Low risk: adds a new optional settings surface that only affects File Explorer styling, with validation and fallbacks to existing palette colors.
Overview
Adds support for
fileExplorer.gitStatusColorsincmux.json, persisting validated per-status hex overrides into managedUserDefaultsand documenting the new section in the JSON schema, template, and configuration docs.File Explorer git label coloring now resolves through a main-thread cached palette (
FileExplorerGitStatusColorSettings), falling back to eachFileExplorerStyle’s built-in colors when an override is missing; applying this setting reloads the cache and triggers a.fileExplorerStyleDidChangerefresh. Includes new XCTest coverage for settings application and ignoring malformed/unknown entries.Reviewed by Cursor Bugbot for commit 45fb8a6. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds customizable File Explorer git status colors via
fileExplorer.gitStatusColorsincmux.json, resolved through a main-thread cached palette with style fallbacks; the explorer only reloads when this setting changes. Fixes #3848.New Features
fileExplorer.gitStatusColors; normalize status names, validate hex, persist to managed defaults (allowlisted), and warn viaLogger. Schema, docs, and settings template updated with examples.FileExplorerStyle.gitColor(for:)uses a shared cached palette; applying this setting refreshes the cache on the main thread and posts.fileExplorerStyleDidChange, avoiding per-rowUserDefaultsreads.Bug Fixes
fileExplorer.gitStatusColorschanges. Enforce@MainActorisolation and correct palette ownership/reload order; ignore unknown statuses and invalid entries. Tests cover startup overrides, cache warmup/reload, and malformed persisted values.Written for commit 45fb8a6. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
New Features
Documentation
Bug Fixes / Reliability
Tests