Skip to content

Keep selected workspace visible after sidebar reorders - #4083

Merged
lawrencecchen merged 11 commits into
mainfrom
feat-workspace-reorder-scroll
May 14, 2026
Merged

lawrencecchen merged 11 commits into
mainfrom
feat-workspace-reorder-scroll

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary:\n- Add a UI regression for Cmd+Shift+P Move to Top keeping the moved workspace visible.\n- Reveal the selected workspace when workspace order changes move its index.\n- Cover selected-workspace scroll policy for selected moves and other reorders that shift the selected index.\n- Handle right-sidebar tool panels in global search as title-only so the branch builds on current main.\n\nTests:\n- Passed: ./scripts/reload.sh --tag scrolltop\n- Passed: https://github.com/manaflow-ai/cmux/actions/runs/25789124112\n- PR checks: pending CircleCI and Vercel


Note

Medium Risk
Changes workspace reordering behavior and introduces new order-change notifications that drive sidebar scrolling, which could affect tab ordering and selection UX if edge cases are missed.

Overview
Ensures the sidebar keeps the currently selected workspace visible when the workspace list changes order, by centralizing the scroll decision in SidebarSelectedWorkspaceScrollPolicy and triggering scroll on explicit order-change events.

TabManager now posts a .workspaceOrderDidChange notification (with movedWorkspaceIds) from reorder/move-to-top operations, and ContentView listens for it to request a scroll when the selected workspace was moved. Adds unit tests for the scroll policy and reorder notifications, plus a UI test covering Cmd+Shift+P “Move to Top” keeping the moved workspace visible.

Reviewed by Cursor Bugbot for commit 6705ff2. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Sidebar now intelligently scrolls to keep moved workspaces visible when reordering operations occur.
  • Improvements

    • Enhanced workspace reordering notifications with better tracking of moved workspaces.
    • Improved automatic scrolling behavior to ensure moved workspaces remain visible in the sidebar.
  • Tests

    • Added comprehensive test coverage for workspace reordering scenarios and "Move to Top" command visibility.

Review Change Stack

@vercel

vercel Bot commented May 13, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 14, 2026 0:36am
cmux-staging Building Building Preview, Comment May 14, 2026 0:36am

@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 13, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements deferred scrolling of the selected workspace when it is moved to a new position via reordering. TabManager posts workspace-order-change notifications with moved workspace IDs; a new scroll-policy enum determines when scrolling is needed; ContentView integrates the policy and defers scroll targets when workspaces are reordered. Comprehensive unit and UI tests validate the flow.

Changes

Workspace Reorder and Sidebar Scroll Deferral

Layer / File(s) Summary
Workspace order change notification infrastructure
Sources/TabManager.swift
TabManager posts .workspaceOrderDidChange notifications when workspaces are moved via moveTabToTop, moveTabsToTop, moveTabToTopForNotification, and reorderWorkspace; adds the notification name constant and a standardized userInfo key for moved workspace IDs.
Scroll policy decision logic
Sources/Sidebar/SidebarState.swift, cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace decides scroll eligibility based on selected workspace presence, index changes, and array equality; unit tests cover nil, first-appearance, move-to-top, unchanged-index, and missing-workspace scenarios.
Sidebar scroll deferral integration
Sources/ContentView.swift
ContentView delegates scroll decisions to the policy for workspaceIds changes, listens for .workspaceOrderDidChange notifications to mark pending scroll targets when the selected workspace is moved, flushes pending scrolls when not needed, and requests deferred scrolling after layout.
Workspace reorder notification unit tests
cmuxTests/WorkspaceUnitTests.swift
Unit tests verify .workspaceOrderDidChange is posted with accurate movedWorkspaceIds for reorder and move operations, and omitted when effective workspace order is unchanged.
End-to-end scroll deferral UI test
cmuxUITests/WorkspaceSidebarScrollUITests.swift
UI test exercises the command-palette "Move Workspace to Top" flow and asserts the moved workspace remains visible; includes helpers to automate the command palette action and wait for UI element dismissal.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • manaflow-ai/cmux-dev-artifacts#1466: The test added in this PR (testCommandPaletteMoveWorkspaceToTopKeepsMovedWorkspaceVisible) directly exercises the workspace-reorder and sidebar-scroll behavior introduced here.
  • manaflow-ai/cmux-dev-artifacts#1461: This PR addresses the failing UI test symptom (moved workspace not being scrolled into view) by adding workspace-order notifications and sidebar scroll deferral logic.
  • manaflow-ai/cmux-dev-artifacts#1465: This PR directly implements the scrolling/selection logic touched by the failing WorkspaceSidebarScrollUITests by centralizing scroll decisions and adding notification-driven scroll deferral.
  • manaflow-ai/cmux-dev-artifacts#1463: The UI test added in this PR (testCommandPaletteMoveWorkspaceToTopKeepsMovedWorkspaceVisible) directly exercises the workspace reorder and scroll behavior introduced here.
  • manaflow-ai/cmux-dev-artifacts#1462: The CI test result directly exercises the new workspace reorder and sidebar scroll behavior, including the added UI test.
  • manaflow-ai/cmux-dev-artifacts#1464: This PR implements the exact functionality validated by that CI test result—workspace reorder notifications and deferred sidebar scrolling.

Possibly related PRs

  • manaflow-ai/cmux#1503: Both PRs modify reorderWorkspace in Sources/TabManager.swift; #1503 changes pinned-segment logic and this PR layers workspace-order notifications on top.
  • manaflow-ai/cmux#3152: Both PRs enhance selected-workspace scrolling in Sources/ContentView.swift; #3152 adds initial ScrollViewReader-based scrolling and this PR refactors the decision logic and adds notification-driven scroll deferral.
  • manaflow-ai/cmux#1116: Both PRs touch moveTabToTopForNotification in TabManager; #1116 adds the method to prevent pinned reordering and this PR adds notification posting to it.

Poem

🐰 When workspaces dance and reorder with grace,
A notification zips through cyberspace!
The policy weighs each scroll with care,
While sidebar defers with flair—
Moved workspaces stay right there! ✨

🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers the summary of changes and testing, with test results included, but lacks a demo video, bot review requests, and missing several checklist items completion confirmations. Add a demo video link for the UI changes, include the review trigger block with bot review requests, and clearly mark all checklist items as completed or explain their status.
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the primary change: keeping the selected workspace visible after sidebar reorders.
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 No Swift 6 actor isolation issues. TabManager stays @MainActor. New utility enums are pure value types. SwiftUI View correctly manages @State.
Cmux Swift Blocking Runtime ✅ Passed The PR introduces no blocking or timing-based synchronization. Changes use NotificationCenter async pattern and SwiftUI reactive observers, with proper deferred state management.
Cmux No Hacky Sleeps ✅ Passed Check not applicable. All PR changes are Swift files, which are excluded per rule scope (applies to TypeScript, JavaScript, shell, non-Swift runtime scripts only).
Cmux Swift Concurrency ✅ Passed No legacy async patterns. Uses NotificationCenter and SwiftUI .onReceive (allowed). No DispatchQueue, Combine state, completion-handlers, or fire-and-forget Tasks.
Cmux Swift @Concurrent ✅ Passed All new code is synchronous or properly isolated. No improper @concurrent usage. Test methods correctly annotated with @MainActor.
Cmux Swift File And Package Boundaries ✅ Passed New enum is 78 lines with single responsibility. Oversized files have only +17 and +23 lines added. No mixed responsibilities violations. Tests in proper locations.
Cmux Swift Logging ✅ Passed PR changes do not violate Swift logging rules. No print, debugPrint, dump, or NSLog statements added in production code. Only business logic for workspace scroll notifications was added.
Cmux Swiftui State Layout ✅ Passed PR uses @State for scroll coordination in proper lifecycle callbacks. Row views receive value snapshots and closures, not store refs. No GeometryReader layout changes, no render-time state writes.
Cmux Architecture Rethink ✅ Passed PR uses notification/preference-key pattern for deferred scroll. No timing hacks, locks, polling, or duplicate ownership. Clear invariants and single action path. Follows Swift/SwiftUI best practices.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR modifies sidebar UI within main window and adds workspace notifications. No new standalone auxiliary windows created. Test window helpers are allowed test-only fixtures.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-workspace-reorder-scroll

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.

@greptile-apps

greptile-apps Bot commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Introduces scroll-reveal behavior for the selected workspace row after sidebar reordering, by adding a SidebarSelectedWorkspaceScrollPolicy pure function and a new workspaceOrderDidChange notification that carries the explicitly-moved workspace IDs from TabManager.

  • TabManager: Fixes moveTabToTop to guard against the pinned-aware target index (not just index 0), adds postWorkspaceOrderDidChange on all four reorder entry-points, and skips the notification when moveTabsToTop produces no actual order change.
  • ContentView / SidebarState: Replaces the inlined first-appear scroll check with the extracted SidebarSelectedWorkspaceScrollPolicy, adds an onReceive handler for the new notification to set pendingSelectedWorkspaceScrollId when the selected workspace is among the explicitly moved ones, and flushes that pending scroll in the onChange(workspaceIds) else-branch.
  • Tests: Unit tests cover notification emission/suppression for all four reorder paths and nil-selection policy; a new UI regression test verifies Cmd+Shift+P "Move to Top" keeps the moved workspace visible.

Confidence Score: 5/5

Safe to merge — the reorder paths are guarded correctly, all three issues from the prior review round are resolved, and both unit and UI test coverage is thorough.

The three previously flagged issues — notification filter inversion, passive-shift over-scrolling, and missing nil-selection test — are all addressed cleanly in this revision. moveTabToTop's early-return now correctly computes the pinned-aware target index, moveTabsToTop suppresses the notification when the order is unchanged, and the onReceive guard requires identity equality with the owning tabManager. No new isolation, blocking, or state-ownership problems were introduced.

No files require special attention.

Important Files Changed

Filename Overview
Sources/TabManager.swift Adds postWorkspaceOrderDidChange and updates all four reorder entry-points; moveTabToTop now guards on the pinned-aware target index rather than absolute index 0, and moveTabsToTop correctly suppresses the notification when the order is unchanged.
Sources/Sidebar/SidebarState.swift Adds SidebarSelectedWorkspaceScrollPolicy as a caseless enum with a generic, nonisolated static function — correct isolation, pure and independently testable.
Sources/ContentView.swift Replaces the inlined first-appear check with SidebarSelectedWorkspaceScrollPolicy, adds onReceive(.workspaceOrderDidChange) to set pendingSelectedWorkspaceScrollId only when the selected workspace is among the explicitly moved ones, and flushes the pending scroll in the onChange else-branch for the index-unchanged case.
cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift New SidebarSelectedWorkspaceScrollPolicyTests suite covers first-appearance, move-to-top, passive index shift, unchanged index, missing workspace, and nil-selection paths — comprehensive coverage for the extracted policy.
cmuxTests/WorkspaceUnitTests.swift Adds unit tests for notification emission (reorderWorkspace, moveTabsToTop, moveTabToTop) and suppression (order unchanged, already-first-unpinned), including correct @mainactor annotation on each test.
cmuxUITests/WorkspaceSidebarScrollUITests.swift New UI regression test creates 20 workspaces, runs Cmd+Shift+P Move to Top, and asserts the moved workspace row is hittable; helper waitForNonExistence uses the existing pollUntil utility.

Sequence Diagram

sequenceDiagram
    participant UI as UI Action (e.g. Move to Top)
    participant TM as TabManager
    participant NC as NotificationCenter
    participant SB as VerticalTabsSidebar
    participant SV as ScrollViewProxy

    UI->>TM: moveTabToTop / moveTabsToTop / reorderWorkspace
    TM->>TM: mutate tabs (guard: order actually changes)
    TM->>NC: post .workspaceOrderDidChange (movedWorkspaceIds: [UUID])

    NC-->>SB: onReceive(.workspaceOrderDidChange)
    SB->>SB: "guard object === tabManager"
    SB->>SB: guard selectedId in movedWorkspaceIds
    SB->>SB: "pendingSelectedWorkspaceScrollId = selectedId"

    TM-->>SB: onChange(renderContext.workspaceIds)
    alt selected workspace index changed
        SB->>SB: requestSelectedWorkspaceScroll() sets pendingSelectedWorkspaceScrollId
        SB->>SV: flushPendingSelectedWorkspaceScroll() proxy.scrollTo(selectedId)
    else selected workspace index unchanged
        SB->>SV: flushPendingSelectedWorkspaceScroll() flushes notification-set pending ID
    end

    SB-->>SB: onPreferenceChange(SidebarWorkspaceRowIdsPreferenceKey) flushPendingSelectedWorkspaceScroll() if row now laid out
Loading

Reviews (8): Last reviewed commit: "Scroll selected workspace on index shift..." | Re-trigger Greptile

Comment thread Sources/Sidebar/SidebarState.swift Outdated
return true
}

return oldIndex != newIndex

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Scroll triggered for every passive index shift

oldIndex != newIndex fires even when a completely unrelated workspace is reordered and merely bumps the selected workspace one position. If the selected workspace is already fully visible when the reorder happens, proxy.scrollTo will still be enqueued. SwiftUI's minimum-reveal scroll usually makes this a no-op, but the scroll is applied after any reorder animation completes — so during an in-flight animation the row can momentarily appear outside the laid-out bounds, causing a small visible jump. The PR description flags this as a known risk, so flagging for visibility rather than blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed by making passive selected-index shifts a no-scroll path and adding an explicit workspace-order notification for workspaces that were actually reordered.

— Claude Code

Comment on lines +146 to +196
final class SidebarSelectedWorkspaceScrollPolicyTests: XCTestCase {
func testRequestsScrollWhenSelectedWorkspaceFirstAppears() {
XCTAssertTrue(
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace(
selectedWorkspaceId: "b",
oldWorkspaceIds: ["a"],
newWorkspaceIds: ["a", "b"]
)
)
}

func testRequestsScrollWhenSelectedWorkspaceMovesToTop() {
XCTAssertTrue(
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace(
selectedWorkspaceId: "c",
oldWorkspaceIds: ["a", "b", "c"],
newWorkspaceIds: ["c", "a", "b"]
)
)
}

func testRequestsScrollWhenAnotherReorderMovesSelectedWorkspaceIndex() {
XCTAssertTrue(
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace(
selectedWorkspaceId: "b",
oldWorkspaceIds: ["a", "b", "c"],
newWorkspaceIds: ["c", "a", "b"]
)
)
}

func testSkipsScrollWhenReorderLeavesSelectedWorkspaceIndexUnchanged() {
XCTAssertFalse(
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace(
selectedWorkspaceId: "a",
oldWorkspaceIds: ["a", "b", "c"],
newWorkspaceIds: ["a", "c", "b"]
)
)
}

func testSkipsScrollWhenSelectedWorkspaceIsMissing() {
XCTAssertFalse(
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace(
selectedWorkspaceId: "b",
oldWorkspaceIds: ["a", "b"],
newWorkspaceIds: ["a", "c"]
)
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing test for nil selected workspace

The new SidebarSelectedWorkspaceScrollPolicyTests suite exercises first-appearance, move-to-top, passive shift, unchanged index, and removed-workspace paths — but not the selectedWorkspaceId: nil branch. The policy correctly short-circuits via guard let selectedWorkspaceId and returns false, but an explicit test would lock in the contract that a nil selection never triggers a scroll, which matters if this function is later inlined or refactored.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed by adding explicit nil-selection coverage for SidebarSelectedWorkspaceScrollPolicy.

— Claude Code

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/TabManager.swift (1)

3869-3877: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid emitting workspaceOrderDidChange when moveTabToTop is a no-op.

Line 3876 can fire even when effective order is unchanged (e.g., moving an unpinned workspace already at the first unpinned slot). That creates false reorder events and can trigger unnecessary sidebar reveal/scroll behavior.

Suggested fix
 func moveTabToTop(_ tabId: UUID) {
     guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return }
     guard index != 0 else { return }
+    let previousOrder = tabs.map(\.id)
     let tab = tabs.remove(at: index)
     let pinnedCount = tabs.filter { $0.isPinned }.count
     let insertIndex = tab.isPinned ? 0 : pinnedCount
     tabs.insert(tab, at: insertIndex)
-    postWorkspaceOrderDidChange(movedWorkspaceIds: [tabId])
+    if tabs.map(\.id) != previousOrder {
+        postWorkspaceOrderDidChange(movedWorkspaceIds: [tabId])
+    }
 }
🤖 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/TabManager.swift` around lines 3869 - 3877, The moveTabToTop
implementation currently removes the tab then computes pinnedCount/insertIndex
and always calls postWorkspaceOrderDidChange even when the effective position
doesn't change; to fix, compute pinnedCount and target insertIndex before
mutating tabs (use tabs.filter { $0.isPinned }.count and let insertIndex =
tab.isPinned ? 0 : pinnedCount), if insertIndex == index just return (no-op)
otherwise perform the remove(at: index), insert(tab, at: insertIndex) and then
call postWorkspaceOrderDidChange(movedWorkspaceIds: [tabId]) so events are only
emitted when the visible order actually changes (refer to moveTabToTop, tabs,
pinnedCount, insertIndex, postWorkspaceOrderDidChange).
🤖 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.

Outside diff comments:
In `@Sources/TabManager.swift`:
- Around line 3869-3877: The moveTabToTop implementation currently removes the
tab then computes pinnedCount/insertIndex and always calls
postWorkspaceOrderDidChange even when the effective position doesn't change; to
fix, compute pinnedCount and target insertIndex before mutating tabs (use
tabs.filter { $0.isPinned }.count and let insertIndex = tab.isPinned ? 0 :
pinnedCount), if insertIndex == index just return (no-op) otherwise perform the
remove(at: index), insert(tab, at: insertIndex) and then call
postWorkspaceOrderDidChange(movedWorkspaceIds: [tabId]) so events are only
emitted when the visible order actually changes (refer to moveTabToTop, tabs,
pinnedCount, insertIndex, postWorkspaceOrderDidChange).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 03859eed-b070-419a-8868-d91e0703ed74

📥 Commits

Reviewing files that changed from the base of the PR and between 85bde67 and 0ad6f29.

📒 Files selected for processing (7)
  • Sources/ContentView.swift
  • Sources/Search/GlobalSearchDocuments.swift
  • Sources/Sidebar/SidebarState.swift
  • Sources/TabManager.swift
  • cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
  • cmuxTests/WorkspaceUnitTests.swift
  • cmuxUITests/WorkspaceSidebarScrollUITests.swift

@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 1 potential issue.

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 0ad6f29. Configure here.

Comment thread Sources/TabManager.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes May 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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/WorkspaceUnitTests.swift`:
- Around line 3178-3194: Add an assertion to verify the tab order is unchanged
after the no-op move in
testMoveTabsToTopSkipsNotificationWhenOrderDoesNotChange: after calling
manager.moveTabsToTop([first.id]) assert the ids remain the same (e.g. compare
manager.tabs.map(\.id) to the expected order containing first.id) so the test
not only checks notificationCount but also confirms the workspace order did not
change.
🪄 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: 0d563c9a-940c-4c52-a64e-45b7f4a0c33e

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad6f29 and c775c45.

📒 Files selected for processing (2)
  • Sources/TabManager.swift
  • cmuxTests/WorkspaceUnitTests.swift

Comment thread cmuxTests/WorkspaceUnitTests.swift
Comment thread Sources/ContentView.swift Outdated
Comment on lines 9111 to 9113
if let manager = notification.object as? TabManager, manager !== tabManager {
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 The notification filter logic is semantically inverted. The if let … !== tabManager pattern only rejects notifications whose object is a TabManager that is not our manager; it silently accepts notifications with a nil object or a non-TabManager object (neither of which can be cast). In a multi-window app, any future code that posts .workspaceOrderDidChange without setting object: would be processed by every sidebar instance, setting a spurious pendingSelectedWorkspaceScrollId and triggering an unexpected scroll. The correct guard requires the object to be exactly our tabManager.

Suggested change
if let manager = notification.object as? TabManager, manager !== tabManager {
return
}
guard let manager = notification.object as? TabManager, manager === tabManager else {
return
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed by requiring the notification object to be a TabManager and identical to this sidebar's tabManager before enqueueing a selected-workspace scroll. Notifications with nil or unrelated objects now return immediately.

— Claude Code

…r-scroll

# Conflicts:
#	cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes May 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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/SidebarWorkspaceSnapshotRefreshPolicyTests.swift`:
- Around line 177-185: The test
testSkipsScrollWhenAnotherReorderOnlyShiftsSelectedWorkspaceIndex is asserting
false while the selected workspace "b" moves index (1 → 2), which contradicts
the scroll-policy contract; update the expectation in the call to
SidebarSelectedWorkspaceScrollPolicy.shouldScrollSelectedWorkspace to assert
true (i.e., the test should expect scrolling) so it matches the intended
behavior for reorders that shift the selected index.
🪄 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: 2357b30e-2e82-4d34-807c-3f9050b4e0b5

📥 Commits

Reviewing files that changed from the base of the PR and between 2ac8cee and 8d07f15.

📒 Files selected for processing (4)
  • Sources/ContentView.swift
  • Sources/TabManager.swift
  • cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift
  • cmuxTests/WorkspaceUnitTests.swift

Comment thread cmuxTests/SidebarWorkspaceSnapshotRefreshPolicyTests.swift Outdated
@lawrencecchen
lawrencecchen dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] May 14, 2026 00:44

Addressed by adding the no-op order assertion and CodeRabbit confirmed the inline thread as resolved.

@lawrencecchen
lawrencecchen merged commit f7bd659 into main May 14, 2026
28 checks passed
@lawrencecchen
lawrencecchen deleted the feat-workspace-reorder-scroll branch May 14, 2026 00:45
@lawrencecchen lawrencecchen mentioned this pull request May 14, 2026
2 of 3 tasks
lawrencecchen added a commit that referenced this pull request May 14, 2026
Hotfix release for the 0.64.5 cmux ssh stdin regression.

Includes:
- #4135 SSH stdin fix (community contribution by @kays0x)
- #4154 follow-up Swift interpolation fix in the regression test
- #4088 transparent backgrounds for file preview panels
- #4083 sidebar reorder visibility
- #4094 Cloud VM error guidance
- #4120 Pi Vault icon and JSONL titles
- f85cc56 command palette settings toggles

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant