Skip to content

Add pane controls and single-terminal attach to cmux TUI - #8710

Merged
lawrencecchen merged 38 commits into
mainfrom
feat-cmux-tui-pane-controls
Jul 26, 2026
Merged

lawrencecchen merged 38 commits into
mainfrom
feat-cmux-tui-pane-controls

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Centralizes executable TUI actions in one English/Japanese catalog used by config parsing, shortcut labels, the prefix guide, shortcut modal, context menus, and a future command palette.

Scopes right-click menus by region. Pane menus include New pane with Alt-n, pane controls, IDs, and shortcut help. Sidebar menus include show/hide, compact/full, focus, and shortcut help. Screen menus contain screen actions and shortcut help. The files/workspaces view toggle stays keyboard-only.

Adds pane maximize/restore, Ctrl-b Ctrl-b prefix forwarding, screen and workspace navigation, compact sidebar mode, and cmux-tui attach --surface <id> for a chrome-free single-terminal client.

The shortcut modal now uses neutral shortcut text, an always-visible neutral scrollbar, and a localized [Esc close] button. The workspace sidebar has its own always-visible scrollbar one column inside the resize divider. Both scrollbars share row-viewport geometry and support wheel, track-click, and thumb-drag input.

Validated with formatting, Clippy with warnings denied, the full locked Rust workspace test suite (including 447 cmux-tui unit tests and 9 CLI integration tests), an exact debug build, and live track-click, thumb-drag, and close-button interaction on the PR binary.


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


Summary by cubic

Adds pane controls, localized menus with shortcut labels, a scrollable shortcut modal, unified scrollbars, and a chrome-free single-terminal attach. Event filtering now isolates single-surface attach clients, preserves scoped browser input state, follows targets after tab moves, guards single-surface topology; fixes scoped attach lifecycle and explicit zoom intent, prevents a rare render deadlock, renders control characters as blanks, bounds attach actions, reserves capacity for reliable browser mouse releases, and exposes cell pixel size as a production API.

  • New Features

    • Pane and sidebar menus: Maximize/Restore, Show/Hide, Compact/Full, View toggle, Focus, Send Prefix, Smart New Pane; localized (EN/JA) with right-aligned shortcut labels.
    • Action discovery and overlays: press the prefix to see suffix keys; Ctrl-b ? opens a centered, scrollable list with a visible [Esc close] button; Ctrl-b Ctrl-b sends a literal prefix; routing prioritizes Close Tab and honors explicit pane targets; overlay drags finish cleanly and auto-dismiss on very small terminals.
    • Compact sidebar: toggle with Ctrl-b m; width via sidebar.compact_width. Dragging the divider exits compact mode and applies a full-width override.
    • Single-terminal attach: cmux-tui attach --surface <id> accepts decimal or short ids; uses the full grid without chrome; isolates the client with a capability-gated, surface-scoped subscription that refreshes when the tab moves; exits when the terminal closes; bounds available actions; shows a clear message and falls back when filtered subscriptions are unavailable.
    • Workspace controls: added prev/next workspace and close workspace actions.
    • Exposed cell pixel size as a stable API for precise layout and graphics.
  • Migration

    • Optional config: add sidebar.compact_width; rebind via keys.toggle-sidebar-compact.
    • Use --surface only with attach: cmux-tui attach --surface <id>.
    • CLI id flags now require canonical decimal values; leading-zero ids are rejected. Interactive attach --surface still accepts exact six-character short ids and rejects ambiguous digit-only values.

Written for commit 54b76a2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added attach --surface <id> for surface-only PTY rendering (single terminal, full host grid) that exits when the target surface closes.
    • Added a centered, scrollable shortcut help modal (Ctrl-b ?) with improved shortcut label rendering and overlay dismissal controls.
    • Added workspace viewport scrollbar and hit/drag support in the workspaces sidebar.
    • Added compact sidebar sizing via sidebar.compact_width, plus related toggle/focus and expanded context-menu actions.
  • Documentation
    • Updated CLI, configuration, keyboard, mouse, and getting-started guides for surface attach, compact sidebar behavior, and shortcuts.
  • Tests
    • Expanded CLI/TUI coverage for --surface, shortcut modal behavior/labels, compact sizing, and scrollbar interaction.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

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

The TUI adds attach --surface for PTY-only sessions, compact sidebar configuration, centralized shortcut metadata, shortcut-aware context menus, a scrollable shortcut-help overlay, focus-aware sidebar rendering, and updated CLI, integration, and user documentation.

Changes

TUI surface attach

Layer / File(s) Summary
Surface-only attach flow
cmux-tui/crates/cmux-tui/src/main.rs, cmux-tui/crates/cmux-tui/src/session/tree.rs, cmux-tui/crates/cmux-tui/src/app.rs, cmux-tui/crates/cmux-tui/tests/cli.rs
attach --surface resolves numeric or short PTY identifiers, starts a chrome-free TUI for the selected surface, and exits when that surface closes.
Surface attach documentation
cmux-tui/README.md, cmux-tui/docs/getting-started.md, cmux-tui/spec/cli.md
Usage examples describe single-PTY attach, full-terminal rendering, omitted session chrome, and termination behavior.

Action catalog and compact sidebar

Layer / File(s) Summary
Action catalog and compact sidebar configuration
cmux-tui/crates/cmux-tui/src/config.rs, cmux-tui/crates/cmux-tui/src/localization.rs, cmux-tui/crates/cmux-tui/src/app.rs
Configuration adds compact sidebar sizing and actions for compact mode and shortcut help; centralized action definitions provide resolved labels and shortcut metadata in English and Japanese.
Configuration documentation
cmux-tui/docs/configuration.md
Configuration examples and descriptions include sidebar.compact_width, toggle-sidebar-compact, and show-shortcuts.

Shortcut-aware menus and overlays

Layer / File(s) Summary
Shortcut-aware menus and help overlay
cmux-tui/crates/cmux-tui/src/app.rs, cmux-tui/crates/cmux-tui/src/ui/mod.rs, cmux-tui/crates/cmux-tui/src/ui/overlay.rs
Context menus show resolved shortcuts, shared menu actions use existing action handlers, prefix help lists configured suffixes, and a scrollable shortcut modal captures keyboard and mouse input.
Interaction documentation and validation
cmux-tui/docs/keyboard.md, cmux-tui/docs/mouse.md, cmux-tui/crates/cmux-tui/src/app.rs
Keyboard, mouse, menu, resize, scrollbar, and shortcut-help behavior descriptions and related test expectations are updated.

Focused sidebar rendering

Layer / File(s) Summary
Focused sidebar rendering
cmux-tui/crates/cmux-tui/src/ui/rail.rs, cmux-tui/crates/cmux-tui/src/ui/sidebar.rs, cmux-tui/crates/cmux-tui/src/ui/scrollbar.rs
Sidebar rails, plugins, workspaces, and files use focus-aware symbols, bold styling, focused colors, and viewport scrollbar geometry.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#8622: The rail and workspace scrollbar changes overlap with this PR’s sidebar rendering and hit-testing updates.
  • manaflow-ai/cmux#8698: The shortcut catalog and help overlay are connected to the resolved shortcut display behavior changed here.
🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary and testing, but it omits required template sections like Demo Video, Review Trigger, and Checklist. Add the missing ## Demo Video, ## Review Trigger, and ## Checklist sections, and format the summary/testing to match the template.
✅ Passed checks (24 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
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 PASS: diff vs origin/main touches only cmux-tui Rust/docs files; no .swift, Package.swift, or Xcode project changes, so no Swift actor-isolation risk.
Cmux Swift Blocking Runtime ✅ Passed The diff only changes Rust/docs; no Swift files were added or modified, so the Swift blocking-runtime rule isn’t implicated.
Cmux Browser Automation Off-Main ✅ Passed PR only changes cmux-tui docs/UI files; it doesn't touch TerminalController or ControlCommandExecutionPolicy, so the browser-automation routing rule isn't implicated.
Cmux Expensive Synchronous Load ✅ Passed No Swift files or Swift load-path changes are in the diff; the rule is not applicable here.
Cmux Cache Substitution Correctness ✅ Passed PASS: attach --surface does a fresh remote.refresh_tree() before resolve_surface(), and single-surface mode re-selects each new tree snapshot; no cached/stale value replaces an authoritative read.
Cmux No Hacky Sleeps ✅ Passed No production runtime sleeps were added; the only new waits are bounded polling in the new CLI test, which the rule allows.
Cmux Algorithmic Complexity ✅ Passed New scans are over fixed catalogs (63 actions, 70 bindings) or one-time linear tree lookup; no hot-path nested scans over scalable collections were introduced.
Cmux Swift Concurrency ✅ Passed HEAD changes only Rust docs; no .swift files were modified, so no Swift concurrency patterns were introduced.
Cmux Swift @Concurrent ✅ Passed No Swift files changed and no @concurrent-related diff appears, so the Swift-specific rule is not implicated.
Cmux Swift Package Boundaries ✅ Passed PASS: The PR only changes Rust/docs files; no Swift, Xcode, or Package.swift paths are touched, so the Swift package-boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed PR only changes Rust source/docs; no SwiftPM/Xcode, .gitignore, workflow, or dependency files changed, so the Package.resolved policy isn’t implicated.
Cmux Swift Logging ✅ Passed No Swift production files changed; the PR only touches Rust and docs, so the Swift logging rule is not implicated.
Cmux User-Facing Error Privacy ✅ Passed No new user-facing errors/alerts were added; the diff only changes docs, key bindings, and tests, with no vendor/provider leaks or raw upstream messages.
Cmux Full Internationalization ✅ Passed New runtime UI text is fully backed by EN/JA catalogs; the PR didn’t add unsupported locale surfaces, and existing English-only menu labels were unchanged.
Cmux Swiftui State Layout ✅ Passed PR only changes cmux-tui Rust/docs; no SwiftUI/AppKit views, @Observable/@published, GeometryReader, lazy row state, or render-time mutations are in diff.
Cmux Architecture Rethink ✅ Passed Not applicable: the diff only touches Rust/docs in cmux-tui, with no Swift files or SwiftUI/AppKit lifecycle patterns to evaluate.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only changes Rust/docs; no Swift NSWindow/WindowGroup code or cmuxAuxiliaryWindowIdentifiers changes, so the auxiliary-window shortcut rule doesn’t apply.
Cmux Source Artifacts ✅ Passed Changed paths are only source/docs files (app.rs, config.rs, main.rs, docs/*.md); no temp/build/cache/artifact paths appear, matching the pass list.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PASS: The PR changes only Rust/docs files; no Swift production Sources/ files or test/debug seams are present in the diff.
Cmux No Ambient Global State ✅ Passed PR touches only Rust/docs; no Swift source was added, so the Swift ambient-global-state rule isn’t implicated.
Title check ✅ Passed The title clearly captures two major changes: pane controls and single-terminal attach.
✨ 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-cmux-tui-pane-controls

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.

@greptile-apps

greptile-apps Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds pane maximize/restore controls, a compact sidebar mode, scoped right-click context menus with localized shortcut labels, a scrollable shortcut-help modal, unified scrollbar rendering, and a cmux-tui attach --surface <id> chrome-free single-terminal client. It also fixes a rare render deadlock by switching the stdout guard to parking_lot::ReentrantMutex, adds a fallback slot for reliable browser mouse release delivery under backpressure, and updates the event subscription layer to support surface-scoped filtering for both local Mux and remote sessions.

  • Action catalog (config.rs, localization.rs): All executable TUI actions are now defined once as ActionDefinition (English + Japanese labels, config key), used by key config, menus, shortcut modal, and future command surfaces. New actions include SendPrefix, PrevWorkspace, CloseWorkspace, ToggleSidebarCompact, and ShowShortcuts.
  • Surface-only attach (main.rs, session/remote.rs, event_bus.rs, mux.rs): --surface resolves a decimal or six-character short ID, validates it is a PTY surface, sends a filtered server subscription via subscribe { surface }, and drives a chrome-free app that quits when the target PTY exits. The subscription filter is maintained across tab moves via update_surface_session_path.
  • Reliability fixes (browser.rs, browser_input.rs, ui/graphics_writer.rs): The browser command queue gains one reserve slot for guaranteed mouse-release delivery under backpressure; the app stdout guard switches to a reentrant mutex to prevent render-loop panic deadlock.

Confidence Score: 5/5

Safe to merge. The changes are large but well-structured, with comprehensive test coverage. Every new code path has matching tests, and changes are isolated behind clear feature flags.

The surface-only attach lifecycle is carefully sequenced to avoid event loss. The browser mouse-release reserve slot, the ReentrantMutex stdout guard, and the SurfaceSessionScope path-tracking mutation all address real edge cases with targeted fixes. No correctness issues were found in the changed paths.

Files Needing Attention: No files require special attention. The most complex new logic — SurfaceSessionScope.accepts_tree_delta and BrowserInputDispatcher's reliable_mouse_release — are both covered by tests that exercise the specific edge cases they address.

Important Files Changed

Filename Overview
cmux-tui/crates/cmux-tui/src/app.rs Adds surface-only mode, compact sidebar, shortcut-help modal, pane zoom actions, and workspace scrollbar. Surface exit triggers quit; event sender gains a surface filter; stdout guard switched to ReentrantMutex for deadlock safety.
cmux-tui/crates/cmux-tui/src/config.rs New action catalog with ActionDefinition, EN/JA labels, and action_definitions(). Adds SendPrefix, PrevWorkspace, CloseWorkspace, ToggleSidebarCompact, ShowShortcuts. ActionIndex replaces bare u8 for SelectTab/SelectScreen.
cmux-tui/crates/cmux-tui/src/session/remote.rs Adds deferred subscription path and scope_events_to_surface for single-terminal attach; primed subscription avoids event loss before first subscribe() call; subscription request includes surface filter param.
cmux-tui/crates/cmux-tui-core/src/event_bus.rs Adds SurfaceSessionScope filter that tracks the target surface's workspace/screen/pane path, updating it when TabAdded moves the surface. LayoutChanged is suppressed for surface-only subscribers to avoid unrelated tree churn.
cmux-tui/crates/cmux-tui-core/src/browser.rs Adds one-slot reserve beyond regular queue capacity for guaranteed mouse-release delivery; regular slot accounting via AtomicUsize prevents the reserve from being used by normal commands.
cmux-tui/crates/cmux-tui/src/browser_input.rs Adds reliable_mouse_release fallback slot in the app-layer browser input dispatcher; blocks new presses while a fallback release is queued; clears the slot on surface cleanup.
cmux-tui/crates/cmux-tui/src/ui/graphics_writer.rs Replaces std::sync::Mutex stdout guard with parking_lot::ReentrantMutex (aliased StdoutLock) to allow the panic hook to reacquire the lock on the same thread without deadlocking.
cmux-tui/crates/cmux-tui/src/session/tree.rs Adds surface(), resolve_surface() (handles decimal, six-char short-id, and ambiguous digit-only IDs), and select_surface() for single-terminal attach navigation.
cmux-tui/crates/cmux-tui/src/ui/overlay.rs Adds draw_shortcut_help with centered modal, right-aligned shortcut labels, scrollbar with wheel/track/drag support, and localized [Esc close] button. Menu rendering extended for right-aligned shortcut suffixes.
cmux-tui/crates/cmux-tui/src/localization.rs Adds MenuMessages, ShortcutMessages, and AttachMessages structs for EN/JA; Catalog gains action_label() delegating to ActionDefinition. Both ENGLISH and JAPANESE catalogs fully populated.
cmux-tui/crates/cmux-tui/src/main.rs Wires --surface flag through parse_args_result, run_attach, run_tui, and run_tui_once. publish_session_default_colors skips color publication for surface-only clients. All callers of run_tui_once updated.
cmux-tui/crates/cmux-tui-core/src/mux.rs Adds subscribe_surface_session() for local Mux, cell_pixel_size() as stable API. move_tab_in_state now calls update_surface_session_path so surface-only subscribers follow tab moves.
cmux-tui/crates/cmux-tui-core/src/server.rs Adds SURFACE_SUBSCRIBE_FILTER_CAPABILITY constant; Subscribe command gains optional surface field; subscribe handler routes to filtered or global Mux subscription based on presence of surface.
cmux-tui/crates/cmux-tui/tests/cli.rs Adds CLI integration tests for --surface attach mode, shortcut modal behavior, compact sidebar, scrollbar interaction, and leading-zero ID rejection.

Sequence Diagram

sequenceDiagram
    participant CLI as cmux-tui attach --surface
    participant RS as RemoteSession
    participant SRV as cmux-tui server
    participant APP as App (surface-only)
    participant BUS as MuxEventBroadcaster

    CLI->>RS: connect_for_surface_attach (no subscribe yet)
    RS->>SRV: identify + set-client-info
    CLI->>RS: refresh_tree()
    RS->>SRV: list-tree request
    SRV-->>RS: tree snapshot
    CLI->>RS: resolve_surface(reference)
    Note over CLI: validates PTY kind and capability
    CLI->>RS: scope_events_to_surface(surface_id)
    RS->>BUS: prime_local_subscription (All filter)
    RS->>SRV: "subscribe { surface: id }"
    Note over RS: events accumulate in primed mailbox
    CLI->>RS: refresh_tree() [surface still alive?]
    CLI->>APP: "run_tui(session, surface_only=id)"
    APP->>RS: subscribe() returns primed mailbox
    loop event loop
        SRV-->>RS: surface events (filtered)
        RS->>BUS: emit(MuxEvent)
        BUS->>APP: forward via accepts_mux_event
        APP->>APP: render target surface full-screen
    end
    SRV-->>RS: surface-exited
    RS->>APP: SurfaceExited(id)
    APP->>APP: "quit = true, exit"
Loading

Reviews (22): Last reviewed commit: "fix(tui): bound browser release and atta..." | Re-trigger Greptile

Comment thread cmux-tui/crates/cmux-tui/src/config.rs

@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)
cmux-tui/crates/cmux-tui/src/app.rs (1)

7613-7636: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Surface-only context menu should not offer pane/tab creation actions.

In open_context_menu, only TogglePaneZoom is gated by self.surface_only.is_none(), while NewPaneSmart, NewTab, NewBrowserTab, SplitRight, and SplitDown remain available. Surface-only mode always renders only Node::Leaf(screen.active_pane), so activating these actions creates panes/panes that this client cannot display or manage. Remove them in surface-only mode alongside TogglePaneZoom.

🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs` around lines 7613 - 7636, Update
open_context_menu to filter pane/tab creation actions from the context-menu
groups when self.surface_only is set, including NewPaneSmart, NewTab,
NewBrowserTab, SplitRight, and SplitDown. Preserve the existing menu behavior
otherwise, and keep TogglePaneZoom gated by self.surface_only.is_none().
🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs`:
- Around line 7613-7636: Update open_context_menu to filter pane/tab creation
actions from the context-menu groups when self.surface_only is set, including
NewPaneSmart, NewTab, NewBrowserTab, SplitRight, and SplitDown. Preserve the
existing menu behavior otherwise, and keep TogglePaneZoom gated by
self.surface_only.is_none().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7805af13-7ffa-4a19-bf5a-56c1647f3b0a

📥 Commits

Reviewing files that changed from the base of the PR and between ccfcaf8 and 9a47fac.

📒 Files selected for processing (15)
  • cmux-tui/README.md
  • cmux-tui/crates/cmux-tui/src/app.rs
  • cmux-tui/crates/cmux-tui/src/config.rs
  • cmux-tui/crates/cmux-tui/src/localization.rs
  • cmux-tui/crates/cmux-tui/src/main.rs
  • cmux-tui/crates/cmux-tui/src/session/tree.rs
  • cmux-tui/crates/cmux-tui/src/ui/mod.rs
  • cmux-tui/crates/cmux-tui/src/ui/overlay.rs
  • cmux-tui/crates/cmux-tui/src/ui/sidebar.rs
  • cmux-tui/crates/cmux-tui/tests/cli.rs
  • cmux-tui/docs/configuration.md
  • cmux-tui/docs/getting-started.md
  • cmux-tui/docs/keyboard.md
  • cmux-tui/docs/mouse.md
  • cmux-tui/spec/cli.md

@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 (5)
cmux-tui/crates/cmux-tui/src/app.rs (1)

7657-7657: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Ctrl-b z still mutates shared zoom state in --surface mode, unlike the equivalent menu item.

open_context_menu deliberately omits TogglePaneZoom when self.surface_only.is_some() because zoom has no visible effect in the single-pane view (sync_layout always renders screen.active_pane as a Leaf, ignoring zoomed_pane). But Action::ZoomPane in run_action_for_pane isn't gated the same way, so Ctrl-b z still calls self.session.zoom_pane(pane) and silently changes zoomed_pane on the shared tree — invisible to this client but visible to any other client attached to the same session without --surface.

🐛 Proposed fix: gate the keyboard action the same way the menu already is
-            Action::ZoomPane => self.session.zoom_pane(pane),
+            Action::ZoomPane => {
+                if !self.is_surface_only() {
+                    self.session.zoom_pane(pane);
+                }
+            }

Also applies to: 10209-10217

🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs` at line 7657, Gate the Action::ZoomPane
branch in run_action_for_pane with the same surface_only check used by
open_context_menu, so --surface mode does not call self.session.zoom_pane(pane)
or mutate shared zoom state; preserve the existing zoom behavior for non-surface
sessions.
cmux-tui/docs/configuration.md (1)

45-45: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add locale-specific documentation for the new settings and shortcuts.

These user-facing Markdown additions are English-only, while cmux-tui/crates/cmux-tui/src/localization.rs establishes English and Japanese support. Add the corresponding Japanese documentation/source and keep the examples synchronized across supported locales.

As per coding guidelines, user-facing Markdown must use locale-specific sources and update every supported locale.

Also applies to: 229-229, 248-248, 286-286, 338-338, 347-347

🤖 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 `@cmux-tui/docs/configuration.md` at line 45, Add Japanese locale-specific
documentation for the new settings and shortcuts, including the
sidebar.compact_width entry and all related examples at the referenced sections.
Keep the English and Japanese content synchronized, following the locale sources
defined by localization.rs and covering every supported locale.

Source: Coding guidelines

cmux-tui/crates/cmux-tui/src/ui/sidebar.rs (2)

389-395: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep new UI and documentation copy locale-complete.

The workspace rail hard-codes shell and screens while adjacent copy uses localization::catalog(), so Japanese users receive English fragments. The updated shortcut and mouse documentation is also English-only despite this PR’s English/Japanese localization scope.

  • cmux-tui/crates/cmux-tui/src/ui/sidebar.rs#L389-L395: add catalog-backed fallback and count formatting for the workspace subtitle.
  • cmux-tui/docs/keyboard.md#L9-L10: update the corresponding localized documentation source for the shortcut-help behavior.
  • cmux-tui/docs/mouse.md#L37-L41: update the corresponding localized documentation source for context-menu behavior.

As per coding guidelines, “web, metadata, API, markdown, changelog, and user-facing data must use locale-specific sources and update every supported locale.”

🤖 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 `@cmux-tui/crates/cmux-tui/src/ui/sidebar.rs` around lines 389 - 395, Make the
workspace subtitle locale-complete in cmux-tui/crates/cmux-tui/src/ui/sidebar.rs
lines 389-395 by using localization::catalog() for the fallback title and
screen-count formatting instead of hard-coded “shell” and “screens”. Update
cmux-tui/docs/keyboard.md lines 9-10 with the corresponding localized
shortcut-help documentation, and update cmux-tui/docs/mouse.md lines 37-41 with
localized context-menu documentation for every supported locale.

Source: Coding guidelines


172-228: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Bound rail rendering to the viewport and index managed machines.

draw_machines renders all N machines and linearly searches M managed records for each one (O(N×M)) despite a scroll viewport; draw_workspaces similarly formats all W+R rows before discarding offscreen entries. Provider inventories and workspace histories are not fixed-size, so this runs on every redraw.

  • cmux-tui/crates/cmux-tui/src/ui/sidebar.rs#L172-L228: build a MachineKey lookup once per machine-state update and iterate only the viewport-visible machine indices.
  • cmux-tui/crates/cmux-tui/src/ui/sidebar.rs#L379-L441: derive visible workspace/recoverable ranges from viewport offsets and emit rows/hits only for those ranges.

As per coding guidelines, “Avoid repeated full scans, sorting, filtering, or per-item nested scans over scalable collections in production code.”

🤖 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 `@cmux-tui/crates/cmux-tui/src/ui/sidebar.rs` around lines 172 - 228, The
draw_machines section in cmux-tui/crates/cmux-tui/src/ui/sidebar.rs:172-228 must
build a MachineKey lookup once per machine-state update and iterate only machine
indices visible in the viewport, replacing the per-machine managed_machines
scan. The draw_workspaces section in
cmux-tui/crates/cmux-tui/src/ui/sidebar.rs:379-441 must derive visible workspace
and recoverable ranges from viewport offsets and emit rows and hit targets only
for those ranges; preserve existing rendering and selection behavior for visible
entries.

Source: Coding guidelines

cmux-tui/crates/cmux-tui/src/localization.rs (1)

213-216: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Sanitize technical details in user-facing error messages.

These strings expose implementation terms such as provider lifecycle/workspace updates, catalog updates, and managed-workspace authority bindings. Replace them with product-level wording and concrete retry, reconnect, or alternate-action guidance; retain technical details in logs or telemetry.

As per coding guidelines, user-facing errors must omit implementation details and provide one or two safe next actions.

Also applies to: 247-252, 307-310, 341-346

🤖 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 `@cmux-tui/crates/cmux-tui/src/localization.rs` around lines 213 - 216, Update
the user-facing localization strings around managed workspace errors, including
managed_workspace_unsupported, managed_workspace_machine_inactive,
managed_workspace_unavailable, and managed_workspace_operation_not_allowed, plus
the referenced ranges, to remove provider lifecycle, catalog, update, and
authority-binding terminology. Replace it with product-level wording and one or
two concrete safe actions such as retrying, reconnecting, or choosing an
available menu action; keep technical details only in logs or telemetry.

Source: Coding guidelines

🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs`:
- Line 7657: Gate the Action::ZoomPane branch in run_action_for_pane with the
same surface_only check used by open_context_menu, so --surface mode does not
call self.session.zoom_pane(pane) or mutate shared zoom state; preserve the
existing zoom behavior for non-surface sessions.

In `@cmux-tui/crates/cmux-tui/src/localization.rs`:
- Around line 213-216: Update the user-facing localization strings around
managed workspace errors, including managed_workspace_unsupported,
managed_workspace_machine_inactive, managed_workspace_unavailable, and
managed_workspace_operation_not_allowed, plus the referenced ranges, to remove
provider lifecycle, catalog, update, and authority-binding terminology. Replace
it with product-level wording and one or two concrete safe actions such as
retrying, reconnecting, or choosing an available menu action; keep technical
details only in logs or telemetry.

In `@cmux-tui/crates/cmux-tui/src/ui/sidebar.rs`:
- Around line 389-395: Make the workspace subtitle locale-complete in
cmux-tui/crates/cmux-tui/src/ui/sidebar.rs lines 389-395 by using
localization::catalog() for the fallback title and screen-count formatting
instead of hard-coded “shell” and “screens”. Update cmux-tui/docs/keyboard.md
lines 9-10 with the corresponding localized shortcut-help documentation, and
update cmux-tui/docs/mouse.md lines 37-41 with localized context-menu
documentation for every supported locale.
- Around line 172-228: The draw_machines section in
cmux-tui/crates/cmux-tui/src/ui/sidebar.rs:172-228 must build a MachineKey
lookup once per machine-state update and iterate only machine indices visible in
the viewport, replacing the per-machine managed_machines scan. The
draw_workspaces section in cmux-tui/crates/cmux-tui/src/ui/sidebar.rs:379-441
must derive visible workspace and recoverable ranges from viewport offsets and
emit rows and hit targets only for those ranges; preserve existing rendering and
selection behavior for visible entries.

In `@cmux-tui/docs/configuration.md`:
- Line 45: Add Japanese locale-specific documentation for the new settings and
shortcuts, including the sidebar.compact_width entry and all related examples at
the referenced sections. Keep the English and Japanese content synchronized,
following the locale sources defined by localization.rs and covering every
supported locale.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 63e48b44-76f7-4b25-afcb-4d67bcfa57c1

📥 Commits

Reviewing files that changed from the base of the PR and between 9a47fac and aba5838.

📒 Files selected for processing (15)
  • cmux-tui/README.md
  • cmux-tui/crates/cmux-tui/src/app.rs
  • cmux-tui/crates/cmux-tui/src/config.rs
  • cmux-tui/crates/cmux-tui/src/localization.rs
  • cmux-tui/crates/cmux-tui/src/main.rs
  • cmux-tui/crates/cmux-tui/src/ui/mod.rs
  • cmux-tui/crates/cmux-tui/src/ui/overlay.rs
  • cmux-tui/crates/cmux-tui/src/ui/rail.rs
  • cmux-tui/crates/cmux-tui/src/ui/sidebar.rs
  • cmux-tui/crates/cmux-tui/tests/cli.rs
  • cmux-tui/docs/configuration.md
  • cmux-tui/docs/getting-started.md
  • cmux-tui/docs/keyboard.md
  • cmux-tui/docs/mouse.md
  • cmux-tui/spec/cli.md

@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

Caution

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

⚠️ Outside diff range comments (1)
cmux-tui/crates/cmux-tui/src/ui/overlay.rs (1)

253-275: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cap reserved shortcut width before truncating the label.

The label reserves the uncapped shortcut.width(), while rendering caps it to the available row width. A long configured shortcut can therefore reduce the label budget to zero even though the displayed shortcut is truncated. Compute one capped shortcut width and use it for both calculations.

🤖 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 `@cmux-tui/crates/cmux-tui/src/ui/overlay.rs` around lines 253 - 275, In the
menu-row rendering logic, compute the shortcut width once with the
available-width cap before calling label truncation. Reuse that capped value in
both the label’s saturating width calculation and the shortcut
rendering/positioning path, replacing the separate uncapped calculation in the
`MenuItem` handling block.
🤖 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 `@cmux-tui/docs/keyboard.md`:
- Around line 9-10: Localize the new shortcut-modal instructions in
cmux-tui/docs/keyboard.md lines 9-10 and the context-menu and shortcut-modal
instructions in cmux-tui/docs/mouse.md lines 37-41 by adding locale-specific
documentation sources or catalog entries and updating every supported locale,
including Japanese. Preserve the documented controls and behavior across all
translations.

---

Outside diff comments:
In `@cmux-tui/crates/cmux-tui/src/ui/overlay.rs`:
- Around line 253-275: In the menu-row rendering logic, compute the shortcut
width once with the available-width cap before calling label truncation. Reuse
that capped value in both the label’s saturating width calculation and the
shortcut rendering/positioning path, replacing the separate uncapped calculation
in the `MenuItem` handling block.
🪄 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: 5767593a-c756-4024-9241-b1ae3733cb98

📥 Commits

Reviewing files that changed from the base of the PR and between aba5838 and c40022e.

📒 Files selected for processing (9)
  • cmux-tui/crates/cmux-tui/src/app.rs
  • cmux-tui/crates/cmux-tui/src/config.rs
  • cmux-tui/crates/cmux-tui/src/localization.rs
  • cmux-tui/crates/cmux-tui/src/main.rs
  • cmux-tui/crates/cmux-tui/src/ui/mod.rs
  • cmux-tui/crates/cmux-tui/src/ui/overlay.rs
  • cmux-tui/docs/configuration.md
  • cmux-tui/docs/keyboard.md
  • cmux-tui/docs/mouse.md

Comment thread cmux-tui/docs/keyboard.md Outdated

@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 (2)
cmux-tui/crates/cmux-tui/src/app.rs (1)

8295-8331: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

"Send prefix" silently no-ops on an unfocused browser pane.

forward_key_to_pane (called by Action::SendPrefix via run_action_for_pane, and reachable from the new MenuAction::SendPrefix(pane) context-menu item) only forwards to forward_pty_key_to_surface when surface.kind() == SurfaceKind::Pty. forward_key handles the active-pane case for both PTY and Browser (via forward_browser_key), but there is no browser-aware equivalent for an explicit non-active pane. Right-clicking a background pane whose active tab is a browser tab and choosing "Send prefix" therefore does nothing, with no status feedback.

🛡️ Proposed fix
 fn forward_key_to_pane(&mut self, key: &KeyEvent, pane: Option<PaneId>) {
     if pane == self.active_pane() {
         self.forward_key(key);
         return;
     }
     if !self.session_available() {
         self.status_message =
             Some(localization::catalog().sidebar.no_active_session.to_string());
         return;
     }
     let Some(surface_id) =
         pane.and_then(|pane| self.tree.pane(pane)).and_then(|pane| pane.active_surface())
     else {
         return;
     };
     let Some(surface) = self.session.surface(surface_id) else { return };
-    if surface.kind() == SurfaceKind::Pty {
-        self.forward_pty_key_to_surface(key, surface_id, surface);
-    }
+    if surface.kind() == SurfaceKind::Pty {
+        self.forward_pty_key_to_surface(key, surface_id, surface);
+    } else {
+        self.status_message =
+            Some("Send prefix is not supported for browser panes".to_string());
+    }
 }
🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs` around lines 8295 - 8331, Update
forward_key_to_pane so an explicitly targeted non-active pane with an active
SurfaceKind::Browser is handled through the browser key-forwarding path,
matching forward_key behavior; retain the existing PTY forwarding path and avoid
silently dropping SendPrefix for browser panes.
cmux-tui/crates/cmux-tui/src/main.rs (1)

697-710: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject --surface when dynamic providers are requested, or require --machine-provider-command if the surface should remain attach-only.

validate_provider_process_args only checks args.attach; a provider invocation like cmux-tui --surface s:abc --machine-provider /tmp/cmux.sock is currently accepted, but resolve_provider_launch returns Some(...) and main calls run_provider_machine_client(...) instead of run_attach(...). That drops the requested single-surface filter and shows the full TUI instead.

🤖 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 `@cmux-tui/crates/cmux-tui/src/main.rs` around lines 697 - 710, Update
validate_provider_process_args and the provider dispatch around
resolve_provider_launch so --surface cannot be combined with a dynamic provider
request; alternatively require --machine-provider-command for surface
attach-only behavior. Ensure surface invocations continue through run_attach and
never call run_provider_machine_client, preserving the requested single-surface
filter.
🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs`:
- Around line 8295-8331: Update forward_key_to_pane so an explicitly targeted
non-active pane with an active SurfaceKind::Browser is handled through the
browser key-forwarding path, matching forward_key behavior; retain the existing
PTY forwarding path and avoid silently dropping SendPrefix for browser panes.

In `@cmux-tui/crates/cmux-tui/src/main.rs`:
- Around line 697-710: Update validate_provider_process_args and the provider
dispatch around resolve_provider_launch so --surface cannot be combined with a
dynamic provider request; alternatively require --machine-provider-command for
surface attach-only behavior. Ensure surface invocations continue through
run_attach and never call run_provider_machine_client, preserving the requested
single-surface filter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f4a31e94-2be9-4f90-81c8-ec3f732cc3bd

📥 Commits

Reviewing files that changed from the base of the PR and between c40022e and 53e52e3.

📒 Files selected for processing (9)
  • cmux-tui/crates/cmux-tui/src/app.rs
  • cmux-tui/crates/cmux-tui/src/localization.rs
  • cmux-tui/crates/cmux-tui/src/main.rs
  • cmux-tui/crates/cmux-tui/src/ui/mod.rs
  • cmux-tui/crates/cmux-tui/src/ui/overlay.rs
  • cmux-tui/crates/cmux-tui/src/ui/scrollbar.rs
  • cmux-tui/crates/cmux-tui/src/ui/sidebar.rs
  • cmux-tui/docs/keyboard.md
  • cmux-tui/docs/mouse.md

@cursor

cursor Bot commented Jul 23, 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.

Caution

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

⚠️ Outside diff range comments (2)
cmux-tui/docs/configuration.md (1)

207-207: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Localize these documentation changes across supported locales.

configuration.md and mouse.md contain end-user copy and new reference-table text, but the matching localized docs are only present for the root README, README.ja.md, and the web frontend READMEs. Add translated versions of the new configuration, shortcut, scrollbar, resizing, and context-menu content for each supported locale before merging.

🤖 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 `@cmux-tui/docs/configuration.md` at line 207, Localize the new end-user
documentation across all supported locales: translate the added configuration,
shortcut, scrollbar, resizing, and context-menu content in
cmux-tui/docs/configuration.md (line 207) and cmux-tui/docs/mouse.md (line 7),
while preserving the existing localized documentation structure and terminology.

Source: Coding guidelines

cmux-tui/crates/cmux-tui/src/app.rs (1)

7657-7699: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Action::CloseTab ignores the explicit pane parameter, unlike Action::ClosePane.

In run_action_for_pane, Action::ClosePane correctly derives the target from the pane argument, but Action::CloseTab reads self.active_surface() instead. This contradicts the function's own doc comment ("Execute an action against an explicit pane. Context menus use this shared path because right-clicking does not change keyboard focus.") and the analogous handling immediately below it. Currently every call path reaching CloseTab here happens to pass pane == self.active_pane() (menu's MenuAction::CloseTab bypasses this function entirely), so there's no live bug today, but it's a latent trap if CloseTab is ever routed through this shared path with a non-focused pane (e.g. a future context-menu refactor).

🐛 Proposed fix to honor the explicit pane
             Action::CloseTab => {
                 // Close the active tab; the pane collapses with its last
                 // tab, so this is also "close pane" for single-tab panes.
-                if let Some(surface) = self.active_surface() {
+                if let Some(surface) =
+                    pane.and_then(|pane| self.tree.pane(pane)).and_then(|pane| pane.active_surface())
+                {
                     self.render_states.remove(&surface);
                     self.session.close_surface(surface);
                 }
             }
🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs` around lines 7657 - 7699, Update the
Action::CloseTab branch in run_action_for_pane to target the explicitly provided
pane rather than self.active_surface(). Resolve the pane’s surface using the
same pane-to-surface relationship used by the surrounding pane actions, then
remove its render state and close that surface; preserve the existing no-op
behavior when no pane is provided.
🤖 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 `@cmux-tui/crates/cmux-tui/src/app.rs`:
- Around line 7657-7699: Update the Action::CloseTab branch in
run_action_for_pane to target the explicitly provided pane rather than
self.active_surface(). Resolve the pane’s surface using the same pane-to-surface
relationship used by the surrounding pane actions, then remove its render state
and close that surface; preserve the existing no-op behavior when no pane is
provided.

In `@cmux-tui/docs/configuration.md`:
- Line 207: Localize the new end-user documentation across all supported
locales: translate the added configuration, shortcut, scrollbar, resizing, and
context-menu content in cmux-tui/docs/configuration.md (line 207) and
cmux-tui/docs/mouse.md (line 7), while preserving the existing localized
documentation structure and terminology.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9ab3121a-b72a-404b-98a1-00fc9355f83d

📥 Commits

Reviewing files that changed from the base of the PR and between 53e52e3 and 70eb228.

📒 Files selected for processing (6)
  • cmux-tui/crates/cmux-tui/src/app.rs
  • cmux-tui/crates/cmux-tui/src/config.rs
  • cmux-tui/crates/cmux-tui/src/main.rs
  • cmux-tui/docs/configuration.md
  • cmux-tui/docs/keyboard.md
  • cmux-tui/docs/mouse.md

@lawrencecchen
lawrencecchen force-pushed the feat-cmux-tui-pane-controls branch from 7b28338 to 232c55c Compare July 24, 2026 12:26
@cursor

cursor Bot commented Jul 26, 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.

@cursor

cursor Bot commented Jul 26, 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.

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