Skip to content

Commit sidebar selection synchronously so rename shortcut targets the focused workspace - #9336

Closed
austinywang wants to merge 6 commits into
mainfrom
issue-9199-rename-shortcut-targets-group
Closed

austinywang wants to merge 6 commits into
mainfrom
issue-9199-rename-shortcut-targets-group

Conversation

@austinywang

@austinywang austinywang commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #9199

Bug 1: rename shortcut targeted the group

With a workspace inside a group focused, Cmd+Shift+R opened the rename prompt for the parent workspace group instead of the focused workspace.

Root cause: plain sidebar clicks routed through SidebarSelectionCoalescer, which deferred the selection commit by up to 100 ms after a preceding click. Clicking a grouped child right after its group header left the header as the committed command target during that window, so the rename shortcut resolved against the group.

Fix: remove the coalescer entirely — plain clicks commit selection synchronously in SidebarWorkspaceTableController, the same path modifier clicks already used.

Bug 2 (dogfood follow-up): group title disappeared after header → child clicks

Clicking the group header and then a grouped child left the group's title invisible (white-on-white) until an unrelated repaint.

Root cause: NSColor.labelColor.withAlphaComponent(0.9) resolves the dynamic color against the ambient drawing appearance at call time and returns a static color. With selection now committing synchronously inside the click dispatch, the header's authoritative reconfigure can run while a dark appearance (the swapped-in terminal view's) is current — the title snapshots the dark variant (white) and keeps it in the light sidebar. Verified with a standalone AppKit probe: labelColor.withAlphaComponent(0.9) called under darkAqua stays brightness 1.0 when later resolved under aqua, while plain labelColor resolves to 0.0.

Fix: the header title keeps the dynamic labelColor and dims via alphaValue (no appearance resolution at configure time); all layer-color conversions in SidebarGroupHeaderTableCellView and SidebarWorkspaceRowCellView resolve through the cell's own effectiveAppearance; the header re-resolves on viewDidChangeEffectiveAppearance. A DEBUG-only probe logs the resolved title paint to the debug event log for live diagnosis.

Tests

Failing tests commit first, fix after, per the regression-test commit policy:

  • rapidGroupHeaderThenChildClickCommitsChildSelectionSynchronously / rapidChildThenGroupHeaderClickCommitsAnchorSelectionSynchronously — real SidebarWorkspaceTableController row clicks asserting the command target synchronously after the click (bug 1's exact repro path).
  • groupHeaderTitleIgnoresAmbientAppearanceAtConfigureTime / anchorActiveGroupHeaderTintResolvesAgainstCellAppearance — configure the header cell under a darkAqua ambient appearance and assert the title/tint still resolve dark in a light appearance (bug 2's mechanism).

Notes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved sidebar selection when rapidly switching between workspace groups and child rows.
    • Prevented temporary text-color flicker during selection updates.
    • Cleared deferred clicks during double-click and drag operations.
    • Improved sidebar colors and text appearance across different interface themes.
  • Tests

    • Added coverage for selection commits and appearance-aware sidebar rendering.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Sidebar cells now resolve dynamic colors against their effective appearance. Workspace and group-header selections execute synchronously. The selection coalescer and its tests were removed. New tests cover appearance-aware rendering and grouped selection.

Changes

Sidebar appearance and selection

Layer / File(s) Summary
Effective-appearance color rendering
Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift, Sources/Sidebar/AppKitList/Cells/SidebarWorkspaceRowCellView.swift
Dynamic colors resolve against each cell’s effective appearance. Title dimming uses alphaValue. Appearance changes reapply the current model.
Synchronous grouped selection
Sources/Sidebar/AppKitList/SidebarWorkspaceTableController.swift, cmux.xcodeproj/project.pbxproj, Sources/Sidebar/AppKitList/SidebarSelectionCoalescer.swift
Workspace and group-header actions execute immediately. Deferred clicks are cleared during cancellation, rename, and drag handling. The selection coalescer is removed from the project.
Validation
cmuxTests/SidebarAppKitRowCellTests.swift, cmuxTests/SidebarWorkspaceTableTests.swift, cmuxTests/SidebarSelectionCoalescerTests.swift
Tests verify effective-appearance color resolution and immediate grouped selection. Coalescer tests are removed.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SidebarWorkspaceTableController
  participant SelectionAction
  User->>SidebarWorkspaceTableController: click group header or child row
  SidebarWorkspaceTableController->>SelectionAction: invoke selection action immediately
  SelectionAction-->>SidebarWorkspaceTableController: apply selected workspace or group
Loading

Possibly related PRs

Suggested reviewers: azooz2003-bit


Important

Pre-merge checks failed

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

❌ Failed checks (2 errors)

Check name Status Explanation Resolution
Cmux Swift Logging ❌ Error The DEBUG cmux log adds raw user-controlled group content via name=\\(model.name.prefix(12)); truncation is not explicit redaction. Remove the group name from the probe, or replace it with a non-sensitive group ID prefix or an explicitly private-redacted value.
Cmux No Test Or Debug Seam In Production Source ❌ Error The PR adds inline #if DEBUG members logTitlePaintProbe, titlePaintForTesting, and headerBackgroundLayerColorForTesting in Sources/.../SidebarGroupHeaderRowView.swift; tests are their only callers. Remove the production test/debug accessors and move observation into cmuxTests via @testable import, widening private state to internal as needed; isolate unavoidable diagnostics in a dedicated debug file.
✅ Passed checks (23 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the primary fix: synchronous sidebar selection so rename shortcuts target the focused workspace.
Description check ✅ Passed The description clearly explains both bugs, their root causes, fixes, and regression tests, although some template sections are omitted.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 Actor Isolation ✅ Passed Production diff only changes existing @MainActor AppKit cells/controller; it adds no model, service protocol, Sendable, or background-context declarations. New async isolation is test-only and @Mai...
Cmux Swift Blocking Runtime ✅ Passed The PR removes SidebarSelectionCoalescer and its delayed clock sleep; added production Swift lines contain no blocking or timing primitives. Test changes are test-only.
Cmux Browser Automation Off-Main ✅ Passed The PR changes only sidebar selection and AppKit color code; no browser socket commands, worker routing, WebKit waits, or policy tests are changed.
Cmux Expensive Synchronous Load ✅ Passed The PR diff adds no RestorableAgentSessionIndex, SharedLiveAgentIndex, agent-history file reads, JSON decoding, directory scans, or syscalls; changes are synchronous selection dispatch and AppKit c...
Cmux Cache Substitution Correctness ✅ Passed The production diff removes deferred selection and changes dynamic color resolution only; it does not replace any fresh persistence, history, undo, or snapshot read with a cache.
Cmux No Hacky Sleeps ✅ Passed The patch changes only Swift files and an Xcode project file; it adds no sleep, timer, polling, or delayed-dispatch code and removes the selection coalescer.
Cmux Algorithmic Complexity ✅ Passed Production changes remove the coalescer and replace deferred calls with direct actions; added color helpers are constant-time, and no new scalable scans or nested collection loops appear.
Cmux Swift Concurrency ✅ Passed The runtime diff adds no async legacy pattern; it removes the Task-based selection coalescer. New async/await code is test-only and allowed by the modernization rules.
Cmux Swift @Concurrent ✅ Passed The PR adds no @concurrent or nonisolated async work. New async tests are explicitly @MainActor and UI-bound; existing Task-based helpers are unchanged.
Cmux Swift Package Boundaries ✅ Passed Production changes are @MainActor AppKit cell/table-controller glue in the app target; the generic coalescer is deleted, and no reusable domain logic is added.
Cmux Swiftpm Lockfiles ✅ Passed The PR changes no Package.swift, Package.resolved, or .gitignore files; its cmux.xcodeproj edit only removes source-file references, not SwiftPM package references.
Cmux User-Facing Error Privacy ✅ Passed The diff adds no user-facing error or recovery copy. The only new diagnostic is a #if DEBUG log probe, and added assertion text is test-only.
Cmux Full Internationalization ✅ Passed The diff adds no user-facing text or localization assets. Added prose is comments or DEBUG-only diagnostics; remaining changes are appearance, selection logic, tests, and project cleanup.
Cmux Swiftui State Layout ✅ Passed The PR changes only AppKit sidebar code and tests; no SwiftUI view/state/layout changes are present, so this SwiftUI-specific check is not applicable.
Cmux Architecture Rethink ✅ Passed The diff removes the timed selection coalescer and dispatches clicks synchronously; appearance fixes stay owned by each cell via effectiveAppearance, with regression tests and no new production del...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR changes sidebar cells/controller and test fixtures only; it adds no standalone NSWindow, NSPanel, NSWindowController, Window, or WindowGroup production code. Test-only NSWindow fixtures are allo...
Cmux Source Artifacts ✅ Passed All eight changed paths are Swift source/tests or Xcode configuration; two coalescer files are removed, and no artifact-named paths, binaries, or generated output files were added.
Cmux No Ambient Global State ✅ Passed The PR adds only private instance helpers and an appearance override inside existing AppKit cell types; it adds no top-level mutable state, static-only namespace, or new singleton.
✨ 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-9199-rename-shortcut-targets-group

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.

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

No issues found across 6 files

Re-trigger cubic

cmux reload-cloud and others added 5 commits July 31, 2026 21:59
@austinywang
austinywang force-pushed the issue-9199-rename-shortcut-targets-group branch from 2641dc5 to 25f63bb Compare August 1, 2026 04:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift`:
- Around line 288-297: Remove the test-only properties
dropIndicatorPaintForTesting, titlePaintForTesting, and
headerBackgroundLayerColorForTesting from
Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift. Update
cmuxTests/SidebarAppKitRowCellTests.swift lines 957-980 to inspect the supported
view behavior without these production accessors.
- Around line 281-285: Remove the user-provided model.name prefix from the debug
event assembled in the line diagnostic block. Keep the remaining fixed
diagnostic fields and cmuxDebugLog call unchanged, or remove the probe if no
safe fields remain.
🪄 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 Plus

Run ID: 57e34613-039d-4012-a157-c7c80fc3bd7f

📥 Commits

Reviewing files that changed from the base of the PR and between 2641dc5 and 25f63bb.

📒 Files selected for processing (8)
  • Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift
  • Sources/Sidebar/AppKitList/Cells/SidebarWorkspaceRowCellView.swift
  • Sources/Sidebar/AppKitList/SidebarSelectionCoalescer.swift
  • Sources/Sidebar/AppKitList/SidebarWorkspaceTableController.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/SidebarAppKitRowCellTests.swift
  • cmuxTests/SidebarSelectionCoalescerTests.swift
  • cmuxTests/SidebarWorkspaceTableTests.swift
💤 Files with no reviewable changes (2)
  • cmuxTests/SidebarSelectionCoalescerTests.swift
  • Sources/Sidebar/AppKitList/SidebarSelectionCoalescer.swift

Comment on lines +281 to +285
var line = "sidebar.groupHeader.titlePaint name=\(model.name.prefix(12)) active=\(activeFlag)"
line += " ambient=\(ambientName) effective=\(effectiveName)"
line += " brightness=\(brightnessText) colorAlpha=\(colorAlphaText)"
line += " fieldAlpha=\(fieldAlphaText) frameW=\(frameWidth)"
cmuxDebugLog(line)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the raw group name from the debug event.

model.name is user-provided workspace content. Line 281 writes its prefix to the debug record. Keep only fixed diagnostic fields, or remove this probe.

As per coding guidelines, production Swift runtime logs must not expose customer content without explicit private redaction.

Proposed fix
-        var line = "sidebar.groupHeader.titlePaint name=\(model.name.prefix(12)) active=\(activeFlag)"
+        var line = "sidebar.groupHeader.titlePaint active=\(activeFlag)"
📝 Committable suggestion

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

Suggested change
var line = "sidebar.groupHeader.titlePaint name=\(model.name.prefix(12)) active=\(activeFlag)"
line += " ambient=\(ambientName) effective=\(effectiveName)"
line += " brightness=\(brightnessText) colorAlpha=\(colorAlphaText)"
line += " fieldAlpha=\(fieldAlphaText) frameW=\(frameWidth)"
cmuxDebugLog(line)
var line = "sidebar.groupHeader.titlePaint active=\(activeFlag)"
line += " ambient=\(ambientName) effective=\(effectiveName)"
line += " brightness=\(brightnessText) colorAlpha=\(colorAlphaText)"
line += " fieldAlpha=\(fieldAlphaText) frameW=\(frameWidth)"
cmuxDebugLog(line)
🤖 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/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift` around
lines 281 - 285, Remove the user-provided model.name prefix from the debug event
assembled in the line diagnostic block. Keep the remaining fixed diagnostic
fields and cmuxDebugLog call unchanged, or remove the probe if no safe fields
remain.

Source: Coding guidelines

Comment on lines 288 to +297
var dropIndicatorPaintForTesting: (top: Bool, bottom: Bool) {
(!topDropIndicator.isHidden, !bottomDropIndicator.isHidden)
}

var titlePaintForTesting: (text: String, color: NSColor?, alpha: CGFloat) {
(nameField.stringValue, nameField.textColor, nameField.alphaValue)
}

var headerBackgroundLayerColorForTesting: CGColor? {
backgroundView.layer?.backgroundColor

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove the shared test-only paint-state seam.

The DEBUG-only properties in production source exist only to support these tests. Production source must not export paint state solely for cmuxTests.

  • Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift#L288-L297: remove dropIndicatorPaintForTesting, titlePaintForTesting, and headerBackgroundLayerColorForTesting.
  • cmuxTests/SidebarAppKitRowCellTests.swift#L957-L980: inspect supported view behavior after removing the production-only accessors.

As per coding guidelines, production Swift source must not add test-only members. As per path instructions, production Sources code must not add test-only or debug-only seams.

📍 Affects 2 files
  • Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift#L288-L297 (this comment)
  • cmuxTests/SidebarAppKitRowCellTests.swift#L957-L980
🤖 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/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift` around
lines 288 - 297, Remove the test-only properties dropIndicatorPaintForTesting,
titlePaintForTesting, and headerBackgroundLayerColorForTesting from
Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift. Update
cmuxTests/SidebarAppKitRowCellTests.swift lines 957-980 to inspect the supported
view behavior without these production accessors.

Sources: Coding guidelines, Path instructions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@austinywang
austinywang force-pushed the issue-9199-rename-shortcut-targets-group branch from 25f63bb to c223940 Compare August 1, 2026 05:10
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

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

♻️ Duplicate comments (1)
Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift (1)

259-290: 🔒 Security & Privacy | 🟠 Major

Remove the temporary title-paint probe before merge.

logTitlePaintProbe is investigation code called from normal model application and optimistic deselection paths. It also writes model.name.prefix(12) to cmuxDebugLog. Group names are user-provided workspace content and are not explicitly redacted.

Remove the probe and its call sites. If a permanent diagnostic is required, log fixed fields only and use the Sidebar dlog logger.

Minimum privacy fix if the probe remains temporarily
-        var line = "sidebar.groupHeader.titlePaint name=\(model.name.prefix(12)) active=\(activeFlag)"
+        var line = "sidebar.groupHeader.titlePaint active=\(activeFlag)"

As per coding guidelines, remove temporary debug probes before merge and do not log customer content without explicit private redaction. Based on learnings, Sidebar diagnostics should use dlog, not cmuxDebugLog.

🤖 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/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift` around
lines 259 - 290, Remove the temporary logTitlePaintProbe method and every call
to it from the normal model-application and optimistic-deselection paths. Do not
log model.name or other user-provided content; if any permanent diagnostic
remains, use fixed fields only through the Sidebar dlog logger rather than
cmuxDebugLog.

Sources: Coding guidelines, Learnings

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

Duplicate comments:
In `@Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift`:
- Around line 259-290: Remove the temporary logTitlePaintProbe method and every
call to it from the normal model-application and optimistic-deselection paths.
Do not log model.name or other user-provided content; if any permanent
diagnostic remains, use fixed fields only through the Sidebar dlog logger rather
than cmuxDebugLog.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ae9ab0a1-da34-4a4d-8c61-1c70aeae1378

📥 Commits

Reviewing files that changed from the base of the PR and between 25f63bb and c223940.

📒 Files selected for processing (1)
  • Sources/Sidebar/AppKitList/Cells/SidebarGroupHeaderRowView.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

Live verification of the disappearing-title mechanism and fix, from the tagged dev build's debug probe (sidebar.groupHeader.titlePaint), while switching between the group anchor and a grouped child:

22:37:25.981 name=Issue 9199 G active=0 ambientLabelBrightness=1.00 effective=NSAppearanceNameAqua brightness=0.00 colorAlpha=0.85 fieldAlpha=0.90 frameW=160
22:37:26.551 name=Issue 9199 G active=1 ambientLabelBrightness=1.00 effective=NSAppearanceNameAqua brightness=0.00 colorAlpha=0.85 fieldAlpha=1.00 frameW=160
22:37:27.121 name=Issue 9199 G active=0 ambientLabelBrightness=1.00 effective=NSAppearanceNameAqua brightness=0.00 colorAlpha=0.85 fieldAlpha=0.90 frameW=160

ambientLabelBrightness=1.00 shows the ambient drawing appearance is genuinely dark at header configure time on every selection transition (the synchronously swapped-in terminal's context) — with the old labelColor.withAlphaComponent(0.9) this snapshotted a static white title on a light sidebar. With the fix the drawn title resolves through the cell's own appearance (brightness=0.00, black, visible) and dims via fieldAlpha.

Startup entries also caught the cell's own effectiveAppearance transiently reporting darkAqua before settling to aqua; the viewDidChangeEffectiveAppearance re-apply covers that window.

🤖 Generated with Claude Code

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.

Cmd+Shift+R rename shortcut targets workspace group, not the focused workspace

3 participants