Skip to content

feat(sidebar): revive workspace icons - #4335

Closed
lawrencecchen wants to merge 6 commits into
mainfrom
issue-1652-workspace-icons-v2
Closed

lawrencecchen wants to merge 6 commits into
mainfrom
issue-1652-workspace-icons-v2

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 19, 2026 •

Copy link
Copy Markdown
Contributor

Revives workspace sidebar icons from #1655 for #1652.

Summary:

  • Adds circular workspace icons in the sidebar, with emoji:<emoji> and image file path support.
  • Adds Workspace Icon context menu actions to set from file, set emoji, and clear selected workspace icons.
  • Persists custom icons through session restore and autosave fingerprints.
  • Adds optional auto-detection for common PNG/JPG/JPEG project icons, disabled by default.
  • Adds CLI/socket support via workspace.action set_icon and clear_icon, plus cmux workspace-action --action set-icon --icon <path|emoji:🚀>.
  • Localizes new UI and command strings across the existing localization catalog.

Validation:

  • jq empty Resources/Localizable.xcstrings
  • git diff --check
  • xcodebuild -quiet -project cmux.xcodeproj -scheme cmux-unit -configuration Debug -destination 'platform=macOS,arch=arm64' -derivedDataPath /tmp/cmux-iconsv2-emoji -resultBundlePath /tmp/cmux-iconsv2-emoji.xcresult -only-testing:cmuxTests/WorkspaceIconTests test
  • xcodebuild -quiet -project cmux.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS,arch=arm64' -derivedDataPath /tmp/cmux-iconsv2-final-build build
  • ./scripts/reload.sh --tag iconsv2

Cloud recording:

Dogfood tag: iconsv2

@vercel

vercel Bot commented May 19, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled May 19, 2026 6:10am
cmux-staging Building Building Preview, Comment May 19, 2026 6:10am

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 19, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds comprehensive workspace icon support to cmux, enabling users to assign custom emoji or image icons to workspaces and optionally auto-detect icons from workspace directories. The feature spans icon storage and detection, state management, UI rendering, settings integration, CLI parsing, terminal API actions, and tests.

Changes

Workspace Icon Support

Layer / File(s) Summary
Icon value model and detection infrastructure
Sources/Sidebar/SidebarAppearanceSupport.swift
WorkspaceIconSettings enables auto-detect; WorkspaceIconValue normalizes persisted icon strings (emoji or file path); WorkspaceIconDetector finds icons in standard directories and Xcode app icon catalogs; WorkspaceIconView renders emoji text or loads images asynchronously with placeholder and failure states.
Workspace icon state and core API
Sources/Workspace.swift
customIconPath and detectedIconPath state; setCustomIcon(_:) normalizes and stores user icons; effectiveIconPath(autoDetectEnabled:) resolves custom or detected; refreshDetectedWorkspaceIcon(autoDetectEnabled:) asynchronously detects with directory validation and task cancellation; initialization and cleanup logic.
Session persistence and snapshot updates
Sources/SessionPersistence.swift, Sources/TabManager.swift, Sources/Workspace.swift
SessionWorkspaceSnapshot.customIconPath persists across sessions; workspace sessionSnapshot(...) includes customIconPath; restoreSessionSnapshot(...) restores icons; TabManager.setTabIcon(...) and applyWorkspaceIcon(...) expose icon-setting APIs; session autosave fingerprint includes icon changes.
Sidebar snapshot and refresh policies
Sources/Sidebar/SidebarWorkspaceSnapshotRefreshPolicy.swift, Sources/ContentView.swift
SidebarWorkspaceSnapshotBuilder.Snapshot carries workspaceIconPath; ContextMenuImmediateFields includes workspaceIconPath for immediate updates; context-menu icon changes trigger instant snapshot propagation while deferring noisy fields.
Settings configuration and UI
Sources/cmuxApp.swift, Sources/KeyboardShortcutSettingsFileStore.swift, Sources/SettingsNavigation.swift, Sources/SettingsSearchAliases.swift, Sources/CmuxSettingsJSONPathSupport.swift
autoDetectWorkspaceIcon AppStorage setting with UI toggle in sidebar appearance; JSONC template generation; JSON path whitelist; settings search index entry with localized title and anchor mapping; search alias for icon-related keywords.
CLI command parsing and documentation
CLI/cmux.swift
Parser recognizes --icon flag and infers icon from positional argument for set_icon; validates non-empty; includes icon in command parameters and payload summary; help text lists set-icon|clear-icon actions with flag documentation and usage examples.
Terminal API V2 workspace icon support
Sources/TerminalController.swift
v2WorkspaceSummaryPayload emits custom_icon and detected_icon; workspace.action recognizes set_icon and clear_icon actions; validates/normalizes icon parameter; updates workspace via TabManager.setTabIcon; returns icon state in response; sidebar_state output includes icon metadata.
UI rendering and context menu interaction
Sources/ContentView.swift, Resources/Localizable.xcstrings
TabItemView conditionally renders WorkspaceIconView with adaptive padding; refreshes icon detection on row appearance and observation changes; context menu gains "Workspace Icon" submenu with clear/set-from-file/set-emoji actions; localized strings for emoji icon UI and alert messages.
Workspace icon tests
cmuxTests/WorkspaceUnitTests.swift, cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
WorkspaceIconTests validates icon value normalization, path canonicalization, autosave fingerprint changes, and icon detection in standard directories; snapshot refresh policy test verifies icon path propagates to displayed fields.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • manaflow-ai/cmux#2475: Adds similar workspace-action handlers (set_description/clear_description) and CLI flag handling patterns that overlap the workspace.action dispatch and CLI parsing approach.

Poem

🐰 I found an icon in a root,

emoji and pixels in cahoots,
Sidebar hums, the workspaces glow,
Auto-detect helps icons grow,
Saved and shown — hop, let’s go!


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (4 errors, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Cmux Swift Actor Isolation ❌ Error WorkspaceIconImageBox marked @unchecked Sendable lacks required safety explanation comment per swift-actor-isolation.md rules for shared mutable reference types. Add documentation comment explaining why NSImage wrapper is safe (created on background thread, immediately returned to MainActor context for storage).
Cmux Swift @Concurrent ❌ Error File-I/O-heavy async closures called from @MainActor without @concurrent: Workspace.swift line 8740 (directory scan), SidebarAppearanceSupport.swift line 266 (image load). Add @concurrent to both Task.detached closures. Pattern: await Task.detached(priority: .utility) @Concurrent { ... }
Cmux Full Internationalization ❌ Error CLI/cmux.swift violates rule: workspace-action help (lines 9513+) materially changed but uses raw string literal instead of String(localized:). Set-icon error message also hardcoded, not localized. Add cli.help.workspace-action and cli.error.setIconMissing to Resources/Localizable.xcstrings with en+ja translations. Update CLI/cmux.swift to use String(localized:) for both workspace-action help and error messages.
Cmux Architecture Rethink ❌ Error Diff introduces representable bad state: promptEmojiIcon (line 14051) silently rejects invalid emoji without feedback. Alert closes unchanged; user cannot tell cancel from validation failure. Emit NSAlert with error message for invalid emoji instead of silently returning. Also fix onChange at line 12995 to watch only autoDetectWorkspaceIcon, not entire settings object.
Docstring Coverage ⚠️ Warning Docstring coverage is 3.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Cmux Swiftui State Layout ❓ Inconclusive No result was produced after verification. Marking as INCONCLUSIVE. Re-run the check or adjust instructions to produce a final result.
✅ Passed checks (11 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Blocking Runtime ✅ Passed Icon feature uses Task.detached for async detection/loading; no blocking primitives (semaphore, NSLock, sleep, sync) introduced in production code.
Cmux No Hacky Sleeps ✅ Passed PR changes are exclusively Swift code and resource files. The rule explicitly excludes Swift (scope: TypeScript, JavaScript, shell, non-Swift runtime). Swift timing is covered separately.
Cmux Swift Concurrency ✅ Passed Uses modern Swift concurrency: Task.detached for detection, .task modifier for image loading, @MainActor isolation. Proper Task lifecycle. No DispatchQueue or fire-and-forget patterns.
Cmux Swift File And Package Boundaries ✅ Passed Additions have clear single responsibility. No mixed UI/state/persistence concerns. Oversized files touched incidentally with <250 additions. Pre-existing violations permitted.
Cmux Swift Logging ✅ Passed No logging violations found. New files and methods contain no print/NSLog/debugPrint/dump statements. All detected logging is pre-existing or allowed by the rules.
Cmux User-Facing Error Privacy ✅ Passed All user-facing error messages and UI text comply with privacy rules. No vendor names, internal provider details, credentials, tokens, environment variables, or sensitive information exposed.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds workspace icon UI with modal dialogs (NSAlert, NSOpenPanel) invoked from context menus - these are allowed cases per the rule. Lint script passed. No new unregistered standalone windows added.
Title check ✅ Passed The title 'feat(sidebar): revive workspace icons' clearly and concisely summarizes the main change: restoring workspace icon functionality to the sidebar UI.
Description check ✅ Passed The pull request description comprehensively covers the feature summary, testing approach, validation steps, and includes a dogfood tag for deployment testing.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-1652-workspace-icons-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread Sources/Sidebar/SidebarAppearanceSupport.swift Outdated
Comment thread Sources/Workspace.swift
@greptile-apps

greptile-apps Bot commented May 19, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR revives workspace icons in the sidebar as circular iMessage-style avatars, adding support for emoji and image-file icons via context menu, socket actions, CLI, and an off-by-default auto-detect setting. Icons are persisted in session snapshots and included in autosave fingerprints.

  • Adds WorkspaceIconValue, WorkspaceIconDetector, WorkspaceIconView, and supporting types in SidebarAppearanceSupport.swift; hooks icon state (customIconPath, detectedIconPath, revision counters) into Workspace with a Task.detached filesystem scan triggered by currentDirectory changes.
  • Extends the socket workspace.action API with set_icon/clear_icon actions (including the custom_icon/detected_icon v2 payload fields), adds --icon to the CLI workspace-action command, and wires a context-menu submenu with "Set Emoji…", "Set Icon from File…", and "Clear Icon".
  • All new user-facing strings are fully catalogued across every supported locale; the cli.help.workspaceAction help block is now properly routed through String(localized:) with catalogue entries.

Confidence Score: 5/5

Safe to merge; all changes are additive and well-isolated behind the off-by-default auto-detect flag.

The new icon pipeline is purely additive: custom icons default to nil, auto-detect is off by default, and session snapshots use optional Codable fields that decode gracefully on older sessions. The socket/CLI additions follow the same guard-and-finish pattern as the existing set_color/clear_color actions. All new user-facing strings are fully catalogued across every supported locale. The only observations are about background I/O not being interrupted on cancellation and file size, neither of which affects correctness.

Sources/Sidebar/SidebarAppearanceSupport.swift — mixed responsibilities and the inner Task.detached image-load pattern are worth a follow-up refactor.

Important Files Changed

Filename Overview
Sources/Sidebar/SidebarAppearanceSupport.swift New 486-line file housing settings namespace, domain enums, file detector, file signature, detection result, SwiftUI view, and colour utilities — mixed responsibilities; Task.detached inside .task(id:) in WorkspaceIconView.loadFileIconIfNeeded won't be cancelled when the SwiftUI lifecycle cancels the outer task.
Sources/Workspace.swift Adds customIconPath, detectedIconPath, revision counters, and refreshDetectedWorkspaceIcon with proper weak-self capture and MainActor dispatch; currentDirectory.didSet correctly triggers detection re-runs.
Sources/ContentView.swift Wires WorkspaceIconView into TabItemView, adds context-menu submenu for icon management, and routes autoDetectWorkspaceIcon setting changes to refresh detection; promptEmojiIcon retry loop and promptIconFromFile use standard AppKit-synchronous modal patterns consistent with the existing promptCustomColor pattern.
Sources/TerminalController.swift Adds set_icon/clear_icon socket actions with correct WorkspaceIconValue.normalizedStorageValue validation and localised error message; v2 payload gains custom_icon and detected_icon fields.
CLI/cmux.swift Adds --icon flag and set_icon/clear_icon actions to workspace-action; help block correctly moved to String(localized:) with catalogue entries for all locales.
Resources/Localizable.xcstrings All new strings (context menu, settings, alerts, open-panel title, socket error, CLI error/help) are present with entries for every supported locale; Japanese receives actual translations while other locales carry English copy, consistent with existing project practice.
Sources/SessionPersistence.swift Adds customIconPath to SessionWorkspaceSnapshot; additive Codable change with correct var declaration for optional backward compatibility.
Sources/TabManager.swift Adds setTabIcon/applyWorkspaceIcon mirroring the existing setTabColor/applyWorkspaceColor pattern; customIconPath correctly added to autosave fingerprint hasher.
cmuxTests/WorkspaceUnitTests.swift New unit tests cover WorkspaceIconValue normalisation, emoji validation, and WorkspaceIconDetector path resolution; good coverage of the normalisation edge cases.
cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift Adds tests confirming workspaceIconPath and workspaceIconReloadToken are included in contextMenuImmediateFields and propagated by fromSnapshot.

Sequence Diagram

sequenceDiagram
    participant User
    participant ContextMenu
    participant TabItemView
    participant TabManager
    participant Workspace
    participant WorkspaceIconDetector
    participant WorkspaceIconView

    User->>ContextMenu: Set Emoji… / Set Icon from File…
    ContextMenu->>TabItemView: promptEmojiIcon / promptIconFromFile
    TabItemView->>TabManager: applyWorkspaceIcon(iconPath, targetIds)
    TabManager->>Workspace: setCustomIcon(iconPath)
    Workspace->>Workspace: normalise + bump customIconRevision
    Workspace-->>TabItemView: "@Published triggers snapshot rebuild"
    TabItemView->>WorkspaceIconView: iconPath + reloadToken (via snapshot)
    WorkspaceIconView->>WorkspaceIconView: .task(id: iconLoadKey) → Task.detached → Data(contentsOf:)
    WorkspaceIconView-->>User: Circular avatar rendered

    User->>Workspace: currentDirectory changes
    Workspace->>Workspace: didSet → refreshDetectedWorkspaceIcon
    Workspace->>WorkspaceIconDetector: Task.detached → detectedIconPath(in:)
    WorkspaceIconDetector-->>Workspace: MainActor.run → detectedIconPath + detectedIconRevision

    User->>SocketAPI: workspace.action set_icon / clear_icon
    SocketAPI->>TabManager: setTabIcon(tabId:iconPath:)
    TabManager->>Workspace: setCustomIcon

    User->>CLI: cmux workspace-action set-icon --icon emoji:🚀
    CLI->>SocketAPI: "params[icon] = icon"
Loading

Reviews (5): Last reviewed commit: "fix(sidebar): address workspace icon rev..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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 `@CLI/cmux.swift`:
- Line 4661: Replace the raw user-facing literal passed to CLIError in
cmux.swift (the throw CLIError(message: "...") instance) with a localized string
using String(localized:defaultValue:) and ensure the same change is applied to
the other occurrences referenced (around lines noted: 9526–9534, 9547–9553,
24222). Update the localization catalog: add matching keys/entries and
translated strings for every locale already supported, and wire the new keys
into the existing localization lookup so all supported locales have the
translated message for the workspace-action set-icon error.

In `@Resources/Localizable.xcstrings`:
- Around line 112820-112832: The new localization keys (e.g.,
"alert.emojiIcon.apply", "alert.emojiIcon.cancel", "alert.emojiIcon.message",
"alert.emojiIcon.placeholder", "alert.emojiIcon.title", "contextMenu.clearIcon",
"contextMenu.setEmojiIcon", "contextMenu.setIconFromFile",
"contextMenu.workspaceIcon", "openPanel.workspaceIcon.title",
"settings.app.autoDetectWorkspaceIcon",
"settings.app.autoDetectWorkspaceIcon.subtitle",
"settings.search.alias.setting.app.auto-detect-workspace-icon") are only
populated for en and ja; add matching entries for every other locale already
present in Resources/Localizable.xcstrings so the catalog is complete. For each
key, add a localization object for each existing locale with "stringUnit" {
"state": "translated", "value": "<localized string>" } (use the correct
translated text from your localization team or a verified placeholder flagged
for review), keep "extractionState": "manual", and ensure formatting matches the
surrounding entries so no partial-localization rule is violated.

In `@Sources/ContentView.swift`:
- Around line 13225-13231: The visibility check for the “Clear Icon” menu
currently uses tab.customIconPath and should instead consider the
multi-selection state in targetIds; update the conditional that wraps the Button
(which calls applyTabIcon(nil, targetIds: targetIds)) to be true when any of the
selected targets in targetIds have a customIconPath (not just the single tab
variable). Locate the block referencing tab.customIconPath and targetIds and
replace the boolean with a check that inspects the selected items (via the same
model or collection you use elsewhere to resolve targetIds to tabs/workspaces)
and shows the Button if any selected item has a non-nil customIconPath. Ensure
the applyTabIcon call and the Label("contextMenu.clearIcon", ...) remain
unchanged.

In `@Sources/Sidebar/SidebarAppearanceSupport.swift`:
- Around line 65-69: The auto-detection list standardIconFilenames in
SidebarAppearanceSupport.swift contains .svg and .ico entries which AppKit's
NSImage(contentsOfFile:) cannot reliably decode; remove SVG and ICO entries and
limit the array to raster formats (e.g., "favicon.png", "icon.png", "logo.png")
so only PNG (or other supported raster extensions) are auto-detected, or
alternatively implement and call a dedicated SVG/ICO decoding pipeline before
images reach NSImage — update the static let standardIconFilenames definition
(and any code that iterates it) to use only supported raster filenames like the
PNG variants.

In `@Sources/TerminalController.swift`:
- Line 5653: Replace the hard-coded error string assigned to result = .err(...)
with a localized message using the app's localization API (e.g.,
NSLocalizedString or the project's localization helper) and a catalog key (e.g.,
"icon.invalid_or_missing"); update the .err call to include a user-actionable
message that suggests recovery steps such as "provide an emoji or a file path
for icon, or use the clear_icon flag" and include those suggestions in the
localized string or appended recovery field; locate the assignment to result
(the .err(code: "invalid_params", ... ) usage in TerminalController) and swap
the literal message for the localized catalog key and a concise recovery action
to meet the user-facing text guidelines.

In `@Sources/Workspace.swift`:
- Around line 7178-7184: When currentDirectory changes you must cancel the
actual detached detector Task (not only its wrapper) so scans don't continue
running; update the logic around refreshDetectedWorkspaceIcon and the
workspaceIconDetectionTask handling to store and reuse the direct Task handle
returned by Task.detached (e.g., a new property like workspaceIconDetectorTask
or replace workspaceIconDetectionTask with the detached Task itself), cancel
that Task explicitly before starting a new detection or when auto-detect is
disabled, and ensure the new Task handle is replaced with the running detector
so subsequent cancellations target the real detached scan.
🪄 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: 872c6574-3092-40ed-a16d-e0c42cf35d9f

📥 Commits

Reviewing files that changed from the base of the PR and between a64b918 and d6ef7b5.

📒 Files selected for processing (17)
  • CLI/cmux.swift
  • Resources/Localizable.xcstrings
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettingsFileStore+Template.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/SessionPersistence.swift
  • Sources/SettingsNavigation.swift
  • Sources/SettingsSearchAliases.swift
  • Sources/Sidebar/SidebarAppearanceSupport.swift
  • Sources/Sidebar/SidebarWorkspaceSnapshotRefreshPolicy.swift
  • Sources/TabManager.swift
  • Sources/TerminalController.swift
  • Sources/Workspace.swift
  • Sources/cmuxApp.swift
  • cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
  • cmuxTests/WorkspaceUnitTests.swift

Comment thread CLI/cmux.swift Outdated
Comment thread Resources/Localizable.xcstrings Outdated
Comment thread Sources/ContentView.swift Outdated
Comment thread Sources/Sidebar/SidebarAppearanceSupport.swift
Comment thread Sources/TerminalController.swift Outdated
Comment thread Sources/Workspace.swift

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/Sidebar/SidebarAppearanceSupport.swift">

<violation number="1" location="Sources/Sidebar/SidebarAppearanceSupport.swift:66">
P2: Auto-detect includes `.svg` candidates, but the current image loading path uses `NSImage(contentsOfFile:)`, so detected SVG files can fail to render and block available PNG fallbacks.</violation>
</file>

<file name="CLI/cmux.swift">

<violation number="1" location="CLI/cmux.swift:4620">
P2: `--icon` accepts option-like tokens as values, so a missing icon can silently consume the next flag instead of failing fast.

(Based on your team's feedback about rejecting option-like flag values.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Sources/Sidebar/SidebarAppearanceSupport.swift Outdated
Comment thread CLI/cmux.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
Sources/Workspace.swift (1)

8738-8742: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cancel the detached detector task directly.

Line 8739 starts the real scan with Task.detached, so the cancellations on Lines 8712, 8725, and 7945 only cancel the wrapper task. Old scans keep running after cwd changes or when auto-detect is disabled.

In Swift concurrency, does canceling a Task that is awaiting Task.detached(...).value automatically cancel the detached task, or must the detached task handle be canceled separately?
♻️ Suggested fix
-        workspaceIconDetectionTask?.cancel()
-        workspaceIconDetectionTask = Task { [weak self, directory] in
-            let result = await Task.detached(priority: .utility) {
-                WorkspaceIconDetectionResult.detect(in: directory)
-            }.value
-
-            guard !Task.isCancelled,
-                  let self,
-                  self.workspaceIconDetectionDirectory == directory,
-                  WorkspaceIconValue.normalizedStorageValue(self.currentDirectory) == directory else {
-                return
-            }
-            let didChange = self.detectedIconPath != result.path || self.detectedIconSignature != result.signature
-            if didChange {
-                self.detectedIconPath = result.path
-                self.detectedIconSignature = result.signature
-                self.detectedIconRevision &+= 1
-            }
-        }
+        workspaceIconDetectionTask?.cancel()
+        workspaceIconDetectionTask = Task.detached(priority: .utility) { [weak self, directory] in
+            let result = WorkspaceIconDetectionResult.detect(in: directory)
+            guard !Task.isCancelled, let self else { return }
+
+            await MainActor.run {
+                guard self.workspaceIconDetectionDirectory == directory,
+                      WorkspaceIconValue.normalizedStorageValue(self.currentDirectory) == directory else {
+                    return
+                }
+                let didChange = self.detectedIconPath != result.path || self.detectedIconSignature != result.signature
+                if didChange {
+                    self.detectedIconPath = result.path
+                    self.detectedIconSignature = result.signature
+                    self.detectedIconRevision &+= 1
+                }
+            }
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Workspace.swift` around lines 8738 - 8742, Current code awaits a
Task.detached() inside workspaceIconDetectionTask but only cancels the wrapper
task (workspaceIconDetectionTask?.cancel()), which does not cancel the detached
detector; fix by keeping and cancelling the detached Task handle explicitly:
when creating the detached operation (the Task.detached that runs
WorkspaceIconDetectionResult.detect(in:)), assign that Task to a persistent
variable (e.g., workspaceIconDetectionDetachedTask) before awaiting its .value,
cancel any existing detached handle when starting a new scan or when disabling
auto-detect, and ensure places that currently call
workspaceIconDetectionTask?.cancel() also cancel the detached handle so the
long-running detect(in:) is stopped promptly.
Sources/ContentView.swift (1)

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

Base “Clear Icon” visibility on the targeted workspaces, not the clicked row.

Line 13230 only checks tab.customIconPath, but the action clears targetIds. In a multi-selection, right-clicking a workspace without a custom icon hides the clear action even if other selected workspaces have one.

🛠️ Suggested fix
         Menu(String(localized: "contextMenu.workspaceIcon", defaultValue: "Workspace Icon")) {
-            if tab.customIconPath != nil {
+            let hasCustomIconTarget = targetIds.contains { workspaceId in
+                tabManager.tabs.first(where: { $0.id == workspaceId })?.customIconPath != nil
+            }
+            if hasCustomIconTarget {
                 Button {
                     applyTabIcon(nil, targetIds: targetIds)
                 } label: {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/ContentView.swift` around lines 13229 - 13236, The "Clear Icon" menu
item visibility currently checks only tab.customIconPath but should check the
selected/targeted workspaces (targetIds); change the condition to show the
button when any workspace among targetIds has a customIconPath. Implement this
by replacing the if tab.customIconPath != nil check with a predicate over
targetIds (for example: targetIds.contains { id in tabsById[id]?.customIconPath
!= nil } or a small helper like hasCustomIcon(in: targetIds)) and keep the call
to applyTabIcon(nil, targetIds: targetIds) as-is.
🤖 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/ContentView.swift`:
- Around line 12995-12997: The onChange handler on the settings snapshot is too
broad: it always calls refreshWorkspaceIconDetection(force: true) even when
unrelated fields change; update the onChange closure that observes settings
(SidebarTabItemSettingsSnapshot) to compare the previous and new
autoDetectWorkspaceIcon value and only call refreshWorkspaceIconDetection(force:
true) when that boolean actually changed, while still calling
refreshWorkspaceSnapshot(force: true) for any change. Locate the onChange usage
that references settings and the functions refreshWorkspaceIconDetection and
refreshWorkspaceSnapshot to implement a simple diff check (or capture old/new
settings) against settings.autoDetectWorkspaceIcon before invoking the forced
icon refresh.
- Around line 14035-14053: The promptEmojiIcon flow currently returns silently
when WorkspaceIconValue.normalizedStorageValue(...) fails; instead, present a
small localized warning and allow the user to correct input. Change
promptEmojiIcon so that if normalizedStorageValue returns nil you show an
NSAlert (or update the current alert) with a localized invalid-emoji
title/message (e.g. keys like "alert.emojiIcon.invalid.title" /
"alert.emojiIcon.invalid.message"), then re-present the emoji prompt (e.g. call
promptEmojiIcon(targetIds) again or loop) so the user can retry, and only call
applyTabIcon when normalized is non-nil; reference promptEmojiIcon,
WorkspaceIconValue.normalizedStorageValue(...) and applyTabIcon in your changes.

---

Duplicate comments:
In `@Sources/ContentView.swift`:
- Around line 13229-13236: The "Clear Icon" menu item visibility currently
checks only tab.customIconPath but should check the selected/targeted workspaces
(targetIds); change the condition to show the button when any workspace among
targetIds has a customIconPath. Implement this by replacing the if
tab.customIconPath != nil check with a predicate over targetIds (for example:
targetIds.contains { id in tabsById[id]?.customIconPath != nil } or a small
helper like hasCustomIcon(in: targetIds)) and keep the call to applyTabIcon(nil,
targetIds: targetIds) as-is.

In `@Sources/Workspace.swift`:
- Around line 8738-8742: Current code awaits a Task.detached() inside
workspaceIconDetectionTask but only cancels the wrapper task
(workspaceIconDetectionTask?.cancel()), which does not cancel the detached
detector; fix by keeping and cancelling the detached Task handle explicitly:
when creating the detached operation (the Task.detached that runs
WorkspaceIconDetectionResult.detect(in:)), assign that Task to a persistent
variable (e.g., workspaceIconDetectionDetachedTask) before awaiting its .value,
cancel any existing detached handle when starting a new scan or when disabling
auto-detect, and ensure places that currently call
workspaceIconDetectionTask?.cancel() also cancel the detached handle so the
long-running detect(in:) is stopped promptly.
🪄 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: 5defa880-72b6-48d1-97ef-ddb17a163514

📥 Commits

Reviewing files that changed from the base of the PR and between d6ef7b5 and 4732e4e.

📒 Files selected for processing (6)
  • Sources/ContentView.swift
  • Sources/Sidebar/SidebarAppearanceSupport.swift
  • Sources/Sidebar/SidebarWorkspaceSnapshotRefreshPolicy.swift
  • Sources/Workspace.swift
  • cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
  • cmuxTests/WorkspaceUnitTests.swift

Comment thread Sources/ContentView.swift Outdated
Comment thread Sources/ContentView.swift Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="CLI/cmux.swift">

<violation number="1" location="CLI/cmux.swift:4620">
P2: `--icon` accepts option-like tokens as values, so a missing icon can silently consume the next flag instead of failing fast.

(Based on your team's feedback about rejecting option-like flag values.) [FEEDBACK_USED]</violation>
</file>

<file name="cmuxTests/WorkspaceUnitTests.swift">

<violation number="1" location="cmuxTests/WorkspaceUnitTests.swift:270">
P2: This test has a race: the initial forced icon refresh is launched before creating `favicon.png`, so async scheduling can make the first refresh detect the icon and mask whether the final forced refresh is actually required.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread cmuxTests/WorkspaceUnitTests.swift Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Sources/Workspace.swift Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1df9ec5. Configure here.

Comment thread Sources/Sidebar/SidebarAppearanceSupport.swift Outdated
Comment thread Sources/ContentView.swift
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — dbcd0c89 Deployed May 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants