Skip to content

ios: mark workspace read/unread from terminal menu; chat button top-level - #6362

Merged
lawrencecchen merged 2 commits into
mainfrom
feat-ios-mark-unread-menu
Jun 18, 2026
Merged

lawrencecchen merged 2 commits into
mainfrom
feat-ios-mark-unread-menu

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

What

  • Add a Mark as Read / Mark as Unread row to the terminal-icon picker menu (top-right) on the workspace detail, so you can flip a workspace's read state without going back to the list. Mirrors the workspace list's swipe action.
  • Remove the dedicated New Workspace top-bar button — it stays available in the picker menu.
  • The agent-chat toggle remains a top-level button in the freed slot (next to the terminal picker), shown only when the visible tab has an agent session.

How

  • terminalPickerMenuContent: new section with a single toggle row that reads "Mark as Read" when the workspace is unread and "Mark as Unread" otherwise (envelope.open / envelope.badge). Action calls store.setWorkspaceUnread(id:, !hasUnread), which sends the mutation to the Mac and re-syncs the authoritative list. Gated on store.supportsWorkspaceReadStateActions so it's hidden on older Macs.
  • The three trailing toolbars (terminal / chat / browser) drop newWorkspaceToolbarButton, leaving [chatToggleButton, terminalPickerToolbarButton].

Reuses existing localized strings mobile.workspace.markRead / mobile.workspace.markUnread (en + ja already present); no new user-facing strings.

Builds on the unread-count work (#6350): opening a workspace marks it read, and this gives an explicit re-flag-as-unread path.

Verification

  • iOS device build (dev.cmux.ios.mkunrd) + macOS app on the same tag.
  • Dogfood: in a workspace, open the terminal-icon menu → "Mark as Unread"; go back, the workspace shows unread (and the back-button count reflects it). Re-open and it flips to "Mark as Read".

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Low Risk
Localized iOS toolbar and menu wiring reusing existing setWorkspaceUnread and capability gating; no auth or data-model changes.

Overview
On workspace detail (terminal, chat, and browser chrome), the top bar no longer shows New Workspace; that action stays in the terminal picker menu. The agent chat toggle remains a top-level trailing button next to the picker.

The terminal picker menu gains Mark as Read / Mark as Unread for the current workspace (same behavior as the list swipe), shown only when supportsWorkspaceReadStateActions is true. It calls setWorkspaceUnread on the Mac and re-syncs list state.

The Swift file-length budget for WorkspaceDetailView.swift is bumped to match the added lines.

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


Summary by cubic

Add a Mark as Read/Unread action to the terminal-icon picker on workspace detail so you can change a workspace’s read state without leaving the view. The New Workspace top-bar button is removed; the agent-chat toggle stays top-level next to the terminal picker when an agent session is present.

  • New Features

    • Terminal picker adds a state-aware “Mark as Read/Mark as Unread” with icons; calls setWorkspaceUnread; shown only when supportsWorkspaceReadStateActions is true.
    • Top bar update: remove New Workspace button (now in the picker menu); keep the chat toggle as a top-level button.
  • Refactors

    • Shortened duplicate toolbar comments and raised WorkspaceDetailView.swift length budget in .github/swift-file-length-budget.tsv to 757.

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

Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a Mark as Read / Mark as Unread option to the terminal picker menu for managing workspace read state.
  • UI Improvements

    • Updated the workspace detail toolbar layout by moving the New Workspace action out of the top-right toolbar area to improve navigation.

…op-level

- Add a "Mark as Read"/"Mark as Unread" row to the terminal-icon picker menu
  (top-right), mirroring the workspace list's swipe action. Flips the current
  workspace's read state on the Mac; only shown when the Mac advertises
  read-state actions.
- Remove the dedicated New Workspace top-bar button (it stays in the picker
  menu). The agent-chat toggle remains a top-level button in that freed slot
  (next to the terminal picker), shown only when the visible tab has a session.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 17, 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 Jun 17, 2026 11:55pm
cmux-staging Building Building Preview, Comment Jun 17, 2026 11:55pm

@coderabbitai

coderabbitai Bot commented Jun 17, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

WorkspaceDetailView removes the "New Workspace" toolbar button from the top-trailing group in three iOS panes (chat, browser, terminal detail), leaving only the agent-chat toggle there. The terminal picker menu gains a conditional "Mark as Read / Mark as Unread" section backed by a new toggleWorkspaceReadStateFromMenu() helper.

Changes

iOS WorkspaceDetailView toolbar and picker menu

Layer / File(s) Summary
Remove New Workspace from toolbar trailing groups
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
The top-trailing toolbar group in the chat pane (line 155–157), browser pane (line 253–255), and terminal-detail pane (line 367–369) each drop the "New Workspace" button, leaving only the agent-chat toggle; comments updated to reflect the button moved to the picker menu.
Read/unread toggle in terminal picker menu
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
A new menu section gated on store.supportsWorkspaceReadStateActions is added to the terminal picker menu (line 470–486) with a "Mark as Read/Unread" action and accessibility identifier. A new private toggleWorkspaceReadStateFromMenu() helper (line 709–718) inverts workspace.hasUnread and calls store.setWorkspaceUnread(id:markUnread:) asynchronously.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • manaflow-ai/cmux#5618: Refactored the terminal picker into a native Menu that first introduced the "New Workspace" entry, which this PR subsequently moves and extends with the read/unread action.

Poem

🐇 Hop hop, the toolbar grew lean,
"New Workspace" now hides in the menu unseen.
Mark it as read, mark it as new,
A flick of the state and the badge bids adieu.
The picker grows richer, the toolbar stays neat —
A tidy little burrow, oh isn't it sweet! 🌿

🚥 Pre-merge checks | ✅ 21 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (21 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding mark read/unread to terminal menu and promoting chat button to top-level, matching the PR's primary objectives.
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 Changes introduce UI methods on SwiftUI View (intentionally MainActor) that access @MainActor-isolated store using established async/await patterns; no actor isolation violations.
Cmux Swift Blocking Runtime ✅ Passed No blocking or timing-based synchronization primitives (semaphores, blocking waits, sleeps, delayed dispatch, polling, main-queue sync, or manual locks) are introduced in the PR changes to Workspac...
Cmux Expensive Synchronous Load ✅ Passed The new toggleWorkspaceReadStateFromMenu() function calls store.setWorkspaceUnread() asynchronously via Task { await ... }, avoiding synchronous main-actor blocking. No expensive synchronous...
Cmux Cache Substitution Correctness ✅ Passed The code reads cached workspace.hasUnread to compute a toggle state, but immediately invalidates the cache via refreshWorkspaces() which fetches authoritative state from the Mac. No stale value is...
Cmux No Hacky Sleeps ✅ Passed PR modifies only Swift code (.swift file). Rule scope explicitly excludes Swift (handled by separate swift-blocking-runtime.md check); covers only TypeScript, JavaScript, shell, and build scripts.
Cmux Algorithmic Complexity ✅ Passed New iOS UI code for marking workspaces read/unread performs O(1) operations: toggleWorkspaceReadStateFromMenu() inverts a boolean property and calls store.setWorkspaceUnread() once per action; the...
Cmux Swift Concurrency ✅ Passed The PR uses proper async/await for the new workspace read-state toggle. The fire-and-forget Task in toggleWorkspaceReadStateFromMenu() bridges a SwiftUI Button action boundary, which is an allowed...
Cmux Swift @Concurrent ✅ Passed PR adds new menu button & helper calling existing store.setWorkspaceUnread method; the method is @MainActor-bound & re-syncs UI state after RPC, so MainActor isolation is intentional & allowed per...
Cmux Swift File And Package Boundaries ✅ Passed WorkspaceDetailView.swift (757 lines) is a small UI-only SwiftUI View inherently belonging in the app target. The +34/-3 line change adds UI menu wiring and a simple event handler delegating to CMU...
Cmux Swiftpm Lockfiles ✅ Passed PR includes SwiftPM lockfile changes that comply with the rule: the new CmuxMobileShellUI package .gitignore does not ignore Package.resolved; package-local Package.resolved and root Xcode Package....
Cmux Swift Logging ✅ Passed The PR adds only one NSLog statement guarded by #if DEBUG in copyDebugLogsFromMenu(), which is explicitly allowed per swift-logging.md rules for debug-only diagnostics.
Cmux User-Facing Error Privacy ✅ Passed PR only adds generic, localized user-facing strings ("Mark as Read/Unread") without exposing vendor names, internal details, credentials, or sensitive information. Complies with user-facing error p...
Cmux Full Internationalization ✅ Passed PR uses L10n.string() wrapper for String(localized:defaultValue:) and references existing localized keys mobile.workspace.markRead/Unread with full translations (en+ja) in ios/cmux/Resources/Locali...
Cmux Swiftui State Layout ✅ Passed PR uses existing @Observable store with @Bindable, adds event-handler functions (not render-time mutations), no lazy-list store references, and no new state declarations. Complies fully with swiftu...
Cmux Architecture Rethink ✅ Passed PR adds a "Mark as Read/Unread" menu toggle in WorkspaceDetailView with no timing patterns, state duplication, or observer side channels; it cleanly delegates to the existing store.setWorkspaceUnre...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed WorkspaceDetailView.swift is an iOS SwiftUI View without any NSWindow, NSPanel, NSWindowController, Window(), or WindowGroup declarations; changes only reorganize toolbar items and add menu section...
Cmux Source Artifacts ✅ Passed All 50 changed files are legitimate source code (47 Swift files), configs (YAML workflow, TSV budget), and project files (pbxproj). No artifacts, logs, caches, or generated files detected.
Description check ✅ Passed The PR description comprehensively covers what changed, why, and how, with verification steps and build details provided.

✏️ 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-ios-mark-unread-menu

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 Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Reworks the workspace detail toolbar on iOS: removes the dedicated New Workspace button from the three trailing toolbar declarations (terminal, chat, browser panes) and adds a capability-gated Mark as Read / Mark as Unread toggle to the terminal-icon picker menu, mirroring the workspace list's swipe action.

  • newWorkspaceToolbarButton is removed from the three iOS ToolbarItemGroup blocks; the macOS #else path via terminalToolbarButtons is intentionally unchanged.
  • A new Section in terminalPickerMenuContent renders the correct label and envelope.open / envelope.badge icon based on workspace.hasUnread, gated on store.supportsWorkspaceReadStateActions; action delegates to the existing setWorkspaceUnread mutation.
  • Reuses already-translated strings mobile.workspace.markRead / mobile.workspace.markUnread; the file-length budget TSV is updated to reflect the 31-line increase.

Confidence Score: 5/5

Safe to merge — localized SwiftUI toolbar and menu changes that call an existing Mac mutation API with capability gating and no new auth or data paths.

All three iOS toolbar removals are straightforward and the macOS path is intentionally preserved via terminalToolbarButtons. The new menu section reuses already-translated strings, is properly gated on supportsWorkspaceReadStateActions, and the label/icon logic correctly mirrors hasUnread. The fire-and-forget Task in toggleWorkspaceReadStateFromMenu follows the same pattern as comparable user-initiated mutations in this file. No actor isolation issues, no new concurrency primitives, no new user-facing strings requiring translation, and the file stays well under the 800-line budget.

No files require special attention.

Important Files Changed

Filename Overview
.github/swift-file-length-budget.tsv Budget entry for WorkspaceDetailView.swift updated from 726 → 757 lines to match the new line count; no policy concerns (file stays under the 800-line limit).
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift Three iOS toolbar declarations drop newWorkspaceToolbarButton; a new capability-gated Mark as Read/Unread section added to the terminal picker menu with correct label/icon logic; new fire-and-forget helper toggleWorkspaceReadStateFromMenu() follows established patterns. macOS #else path (terminalToolbarButtons) is intentionally unchanged.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant TerminalPickerMenu
    participant WorkspaceDetailView
    participant Store
    participant MacApp

    User->>TerminalPickerMenu: Tap terminal icon
    TerminalPickerMenu->>WorkspaceDetailView: Check supportsWorkspaceReadStateActions
    WorkspaceDetailView-->>TerminalPickerMenu: Show "Mark as Read/Unread" row (if supported)
    User->>TerminalPickerMenu: Tap "Mark as Read" / "Mark as Unread"
    TerminalPickerMenu->>WorkspaceDetailView: toggleWorkspaceReadStateFromMenu()
    WorkspaceDetailView->>Store: "Task { await setWorkspaceUnread(id:, markUnread:) }"
    Store->>MacApp: Send mutation (IPC/XPC)
    MacApp-->>Store: Re-sync authoritative workspace list
    Store-->>WorkspaceDetailView: workspace.hasUnread updated (Observable)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant TerminalPickerMenu
    participant WorkspaceDetailView
    participant Store
    participant MacApp

    User->>TerminalPickerMenu: Tap terminal icon
    TerminalPickerMenu->>WorkspaceDetailView: Check supportsWorkspaceReadStateActions
    WorkspaceDetailView-->>TerminalPickerMenu: Show "Mark as Read/Unread" row (if supported)
    User->>TerminalPickerMenu: Tap "Mark as Read" / "Mark as Unread"
    TerminalPickerMenu->>WorkspaceDetailView: toggleWorkspaceReadStateFromMenu()
    WorkspaceDetailView->>Store: "Task { await setWorkspaceUnread(id:, markUnread:) }"
    Store->>MacApp: Send mutation (IPC/XPC)
    MacApp-->>Store: Re-sync authoritative workspace list
    Store-->>WorkspaceDetailView: workspace.hasUnread updated (Observable)
Loading

Reviews (2): Last reviewed commit: "ci: trim toolbar comments + bump Workspa..." | Re-trigger Greptile

Shorten the (triplicated) chat-toggle toolbar comment and refresh the
swift-length budget for WorkspaceDetailView.swift to its new size (757) after
adding the mark-read/unread menu row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrencecchen
lawrencecchen merged commit 8a46fc4 into main Jun 18, 2026
27 of 31 checks passed
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jun 18, 2026
…r + ios chat top-level)

- v1 debug/test control dispatch cut over to ControlCommandCoordinator (manaflow-ai#6343)
- iOS workspace read/unread from terminal menu, chat button promoted (manaflow-ai#6362)
- iOS unread-workspace count badge on back button (manaflow-ai#6350)

Adapter: stub v2DebugSidebarVisible / v2DebugIsTerminalFocused /
v2DebugSimulateTerminalFileDrop call sites with .err(unimplemented).
Bodies moved into ControlCommandCoordinator upstream; v2 ctl path
not used by cmux_term workflow.
lawrencecchen added a commit that referenced this pull request Jun 18, 2026
Merge brings in the back-button unread badge (#6350) and mark read/unread +
chat-top-level toolbar (#6362). Resolved the swift-length-budget conflict to a
single WorkspaceDetailView entry.

Also in this branch update:
- Add "Rename Workspace" to the terminal-icon picker menu (gated on
  supportsWorkspaceActions), presenting the shared WorkspaceRenameSheet and
  forwarding the new name via store.renameWorkspace. Sheet attached across the
  terminal / chat / browser panes so it works from any mode.
- Increase the glass title pill's vertical padding (5 -> 9) so its glass
  background height matches the back button and other bar-button glass items.
  Padding-based (not a fixed height) so the multi-line chat header pill grows.
- Bump WorkspaceDetailView length budget to 839.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview – cmux — bd056a11 Deployed Jun 17, 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