Skip to content

iOS: inline Move to Group context-menu picker for workspaces - #9779

Merged
azooz2003-bit merged 1 commit into
mainfrom
feat-ios-group-move-picker
Aug 8, 2026
Merged

azooz2003-bit merged 1 commit into
mainfrom
feat-ios-group-move-picker

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Aug 7, 2026 •

Copy link
Copy Markdown
Collaborator

Moving a workspace into a group on iOS previously required drag-and-drop, which is fiddly on the phone and impossible when a drag is hard to start (long lists, collapsed groups). Long-pressing a workspace row now shows a Move to Group submenu with one entry per workspace group on that Mac; the current group renders checked and disabled, and grouped workspaces also get Remove from Group.

Selection routes through the exact drag-and-drop path: MobileWorkspaceMovePolicy.normalizedIntent for validity, the optimistic joinGroupAtEnd move chain, and the existing workspace.move RPC, so the picker can never offer a move the drop path would reject, and gating (pending-move cap, search/filter flattening, foreground-Mac-only mutations in All Computers) is identical. A new pure model, MobileWorkspaceGroupMoveMenu, computes the entries (same-Mac filtering with the aggregation empty-vs-nil stamp quirk handled) and is unit-tested; the picker appears in both row pipelines (UIKit table coordinator context menu and the SwiftUI row .contextMenu).

Anchor workspaces get no picker (an anchor moves with its group, matching the host's workspace.move rejection). Remove from Group maps to workspace.move without group_id, which the host resolves as ungroup + move to end of the root list — the same order the optimistic prediction applies locally.

Localization: mobile.workspace.moveToGroup and mobile.workspace.removeFromGroup added to the iOS catalog (en, ja), matching the macOS sidebar's existing contextMenu.workspaceGroup.moveTo/.remove strings.

Tests: 9 new MobileWorkspaceGroupMoveMenuTests (all 247 CmuxMobileShellModel tests green locally) and a coordinator test asserting the submenu structure per capability/anchor/grouped state.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Note

Medium Risk
Touches workspace list mutation UX and extends the grouped move path, but actions still go through the established policy, optimistic ordering, and workspace.move RPC with capability and Mac-scoping gates.

Overview
Adds a Move to Group context-menu path on iOS workspace rows so grouping no longer depends on drag-and-drop alone.

A new MobileWorkspaceGroupMoveMenu model builds picker entries (same-Mac groups, current group checked/disabled, Remove from Group when valid) using MobileWorkspaceMovePolicy.normalizedIntent, matching drag-and-drop rules. Anchor workspaces get no menu; orphan groupID values cannot remove until the group exists in the snapshot.

joinGroupAtEnd now accepts an optional group id (nil = leave group) and is shared by drop-into-group and the menu. The UIKit table coordinator and SwiftUI WorkspaceNavigationRow both wire lazy groupMoveMenu / moveToGroup callbacks with the same reorder gating as drags. English and Japanese strings were added for the new menu labels.

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


Summary by cubic

Adds an inline “Move to Group” context-menu picker on iOS so a workspace can join or leave a group without drag-and-drop. Keeps move behavior identical to drag-and-drop, including gating and ordering.

  • New Features
    • Long-press a workspace to open “Move to Group” with one item per group on the same Mac; current group is checked and disabled, and “Remove from Group” is shown when applicable.
    • Uses the same path as drag-and-drop: MobileWorkspaceMovePolicy + optimistic joinGroupAtEnd + workspace.move RPC, so invalid moves are never offered.
    • Available in both UIKit table menus and SwiftUI row .contextMenu.
    • Anchor workspaces don’t show the picker; removing from a group ungroups and moves to end of the root list.
    • Added iOS localization entries for mobile.workspace.moveToGroup and mobile.workspace.removeFromGroup, plus unit/UI tests for menu logic and structure.

Written for commit b6d07f4. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added workspace context-menu and navigation-row actions for moving workspaces between groups.
    • Added support for moving workspaces into ungrouped destinations or removing them from groups.
    • Group menus now show valid destinations, current membership, and unavailable options.
    • Added English and Japanese labels for group movement actions.
  • Bug Fixes

    • Prevented unsupported group anchors and invalid destinations from appearing as move options.
  • Tests

    • Added coverage for grouped, ungrouped, cross-device, anchor, and removal scenarios.

Long-pressing a workspace row now offers a Move to Group submenu (one
item per group on the workspace's Mac, current membership checked and
disabled) plus Remove from Group, so a workspace can join a group
without drag-and-drop. Selection routes through the same
MobileWorkspaceMovePolicy intent + optimistic joinGroupAtEnd path as
dropping a row onto a group, and reuses drag gating, so the picker can
never offer a move the drop path would reject. Covers both the UIKit
table pipeline and the SwiftUI row context menu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3b8c5eb4-9798-44fc-a70d-9c93f0e5ff99

📥 Commits

Reviewing files that changed from the base of the PR and between 42715b7 and b6d07f4.

📒 Files selected for processing (10)
  • Packages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspaceGroupMoveMenu.swift
  • Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceGroupMoveMenuTests.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListTable.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListTableCoordinator+Actions.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+DragDrop.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+Table.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceNavigationRow.swift
  • Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceListScrollUpdateTests.swift
  • ios/cmux/Resources/Localizable.xcstrings

📝 Walkthrough

Walkthrough

Adds a public group-move menu model for iOS workspaces. The UI displays valid group targets and removal actions in context menus. Workspace list callbacks route selections to group joining or removal. Tests and localized strings cover the new behavior.

Changes

Workspace group movement

Layer / File(s) Summary
Move menu model and policy validation
Packages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspaceGroupMoveMenu.swift, Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceGroupMoveMenuTests.swift
Adds public group move entries, same-Mac filtering, current-membership state, policy-based enablement, removal eligibility, and coverage for workspace and device metadata cases.
Context-menu group picker
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListTable.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListTableCoordinator+Actions.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceNavigationRow.swift, ios/cmux/Resources/Localizable.xcstrings
Adds optional move callbacks, supports heterogeneous context-menu elements, and renders localized group selection and removal actions.
Workspace move callback wiring
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+DragDrop.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+Table.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift, Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceListScrollUpdateTests.swift
Builds group menus when grouped reordering is enabled, routes selected group IDs to join or leave operations, and verifies menu behavior without triggering moves during construction.

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

Possibly related PRs


Important

Pre-merge checks failed

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

❌ Failed checks (1 error, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Cmux Algorithmic Complexity ❌ Error MobileWorkspaceGroupMoveMenu.swift:65-80 validates each candidate group with normalizedIntent, which rescans the workspace collection; this is O(G×W), quadratic when ~1000 workspaces/groups. Refactor policy validation to compute all candidate destinations with one indexed or one-pass plan, or cache results per snapshot; add a benchmark for about 1000 workspaces.
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.
Cmux Swift Actor Isolation ❓ Inconclusive The checkout exposes none of the Swift files described in the pull request; only an unrelated homebrew-cmux change is present, so actor isolation cannot be verified. Provide the pull request commit or a checkout containing the listed Swift changes and their diff.
✅ Passed checks (22 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary iOS change: adding an inline Move to Group context-menu picker.
Description check ✅ Passed The description clearly explains the behavior, implementation, scope, and testing, although it omits the template's demo video and checklist sections.
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 The production diff adds no semaphores, blocking waits, sleeps, delayed dispatch, polling, main-queue sync, or locks; the existing Task move chain is unchanged.
Cmux Browser Automation Off-Main ✅ Passed PASS: The patch only changes iOS workspace-group UI/model, tests, and localization; it adds no browser.* socket automation, WebKit waits, worker routing, or policy changes.
Cmux Expensive Synchronous Load ✅ Passed The PR adds only in-memory menu/policy evaluation over existing workspace and group snapshots; no agent-history loader, file read, JSON parse, directory scan, or background-load change appears in t...
Cmux Cache Substitution Correctness ✅ Passed The diff adds transient menu UI and reuses the existing optimistic move/RPC path; it introduces no persistence, history, undo, or durable snapshot read and no cache substitution.
Cmux No Hacky Sleeps ✅ Passed The diff changes only Swift files and localization data. The rule covers TypeScript, JavaScript, shell, and non-Swift build/runtime scripts, so it is not applicable.
Cmux Swift Concurrency ✅ Passed Added Swift code introduces no Dispatch, Combine, completion-handler, or fire-and-forget Task patterns; the reused move Task is stored in pendingWorkspaceMoveTask and remains lifecycle-managed.
Cmux Swift @Concurrent ✅ Passed The PR adds no @concurrent, nonisolated, async, await, or Task code; new menu and move helpers are synchronous, while the existing @MainActor move task is unchanged.
Cmux Swift Package Boundaries ✅ Passed The reusable menu model and unit tests are in the CmuxMobileShellModel SwiftPM target; UI changes are in CmuxMobileShellUI, with no app-root production logic added.
Cmux Swiftpm Lockfiles ✅ Passed Packages/iOS/CmuxMobileShellModel and CmuxMobileShellUI add no manifest dependencies; the diff changes no Package.resolved, .gitignore, workflow, or Xcode project references.
Cmux Swift Logging ✅ Passed The PR diff adds no print, debugPrint, dump, NSLog, Logger, os_log, file, stdout, or stderr logging; changed runtime Swift code contains no diagnostic output.
Cmux User-Facing Error Privacy ✅ Passed The production diff adds only generic “Move to Group” and “Remove from Group” UI labels; it adds no user-facing errors, raw upstream messages, diagnostics, secrets, or provider details.
Cmux Full Internationalization ✅ Passed All new UI labels use L10n.string with localized keys, and both catalog keys have translated en and ja entries; the touched catalog supports only en and ja.
Cmux Swiftui State Layout ✅ Passed SwiftUI additions use immutable menu snapshots and action closures; the diff adds no ObservableObject/@published, GeometryReader, row store reference, or render-time state mutation.
Cmux Architecture Rethink ✅ Passed The diff adds no timing, polling, locks, observers, or side channels; both UI adapters use the shared menu model and WorkspaceListView joinGroupAtEnd action path.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR adds only iOS workspace models, rows, and menus; no NSWindow, NSPanel, NSWindowController, Window, or WindowGroup code changed, and scripts/lint_auxiliary_window_close_shortcuts.py passes.
Cmux Source Artifacts ✅ Passed The PR changes only Swift source/tests and the iOS localization catalog; no scratch directories, generated output, caches, logs, screenshots, recordings, or dependency artifacts appear in the commit.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Changed production Swift adds no DEBUG/test guards or test/debug-named accessors; the new menu and callbacks are used by UIKit and SwiftUI product paths.
Cmux No Ambient Global State ✅ Passed The diff adds a constructable model with instance state and private static helpers; UI behavior is on existing type extensions. No new top-level funcs, mutable globals, static-helper namespace, or...
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ios-group-move-picker

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.

@azooz2003-bit
azooz2003-bit merged commit 8c766f7 into main Aug 8, 2026
7 checks passed
@azooz2003-bit
azooz2003-bit deleted the feat-ios-group-move-picker branch August 8, 2026 03:38
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