Skip to content

cmux-tui: preserve smallest sizing through web reattach - #8302

Merged
lawrencecchen merged 49 commits into
mainfrom
fix-tui-smallest-reconnect
Jul 18, 2026
Merged

lawrencecchen merged 49 commits into
mainfrom
fix-tui-smallest-reconnect

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #8031.

The web client now republishes its unchanged local viewport whenever an overflow recovery creates a new attach stream. Both byte and render attachments use bounded recovery, so the server never loses the client from the smallest-size calculation. The byte fallback also uses Menlo, matching the styled renderer and Ghostty default.

The first commit is the failing behavior test. The second commit fixes it.

Verified:

  • 114 web tests and production build
  • 23 TypeScript SDK tests and build
  • strict Rust workspace Clippy
  • live 120x40 plus 80x50 clients resolve to 80x40
  • concurrent TUI input, smaller-client detach expansion, and unsized-tab inheritance
  • Cmux Lite and web share Monokai chrome, terminal background, Menlo, and blinking bar cursor

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


Summary by cubic

Preserves the smallest shared terminal grid across recovery and visibility changes, matching tmux sizing semantics and bounds. Client presence now refreshes after remote recovery, and remote subscription recovery is linearized and coalesced so pane labels and menus stay accurate.

  • New Features

    • Per‑pane controls in the web UI: bottom‑left clients indicator and nested menu to include/exclude, “use only this client”, “use all”, or detach.
    • Protocol/CLI: set-client-sizing (with exclusive) and release-surface-size; list-clients adds size_participating and transport: "local" | "unix" | "ws".
    • SDK/UI: CmuxClient#setClientSizing, useOnlyClientSizing, useAllClientSizing; Rust release_surface_size; pane border label shows clients and shared minimum; render attach events include OverflowEvent.
  • Bug Fixes

    • Route all resizes through shared client sizing; clamp terminal sizes (max 10,000); match tmux ignore‑size defaults; ignore unattached fits.
    • Attach recovery: bounded retry with stabilization; apply fits only with an active stream; re‑publish unchanged local fit after reattach.
    • Remote recovery: linearize and coalesce subscription refresh; update client presence so labels and client menus stay correct.
    • UI: nested context menus stay in the viewport with stable focus/hover; coalesced refresh prevents overlapping presence updates; Menlo added to the byte fallback font; preserve sidebar focus during plugin sync.

Written for commit 233eb06. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved render/terminal attachment recovery after overflow events using retry + stabilization, with safer cleanup to prevent resize during teardown.
    • Prevented terminal fit/surface “echo back” loops by clarifying resize behavior and only applying fit while an attachment is active.
    • Stopped key/mouse/paste handling from unintentionally reasserting viewer sizes, and fixed sidebar focus retention during plugin sync.
    • Improved web terminal font fallback by adding Menlo to the monospace stack.
  • Tests
    • Added coverage for sizing during overflow reattachment and verifying no resize after disconnect.
    • Added coverage ensuring user inputs never trigger unintended viewer size reassertions.
  • Chores
    • Tightened a sidebar smoke-test assertion for more reliable matching.

@cursor

cursor Bot commented Jul 17, 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 commented Jul 17, 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 PR adds OverflowEvent to render attachment typings, retries targeted attachment overflows with cancellable backoff, republishes fit sizing after reattachment, and prevents input handling from reasserting viewer sizes. Sidebar plugin synchronization and rendered-text smoke assertions are also updated.

Changes

Render Attachment Recovery

Layer / File(s) Summary
Attachment contracts and sizing
cmux-tui/bindings/typescript/src/protocol/events.ts, cmux-tui/crates/cmux-tui-core/src/mux.rs, cmux-tui/frontends/web/src/hooks/*, cmux-tui/frontends/web/src/lib/fit.ts
Render attachment typing includes OverflowEvent; fit state resets across attachment rounds, font fallbacks are updated, and related resize handling and comments are clarified.
Overflow recovery loop
cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts
Targeted overflow events trigger delayed reattachment with cancellable retry waits, stable-period attempt resets, teardown-safe fitting, and timer cleanup.
Input and sidebar state handling
cmux-tui/crates/cmux-tui/src/app.rs
Key, mouse, and paste input no longer reassert visible surface sizes; sidebar plugin state is cleared separately from visibility gating, while built-in sidebar focus is preserved.
Recovery and regression validation
cmux-tui/frontends/web/test/*, cmux-tui/scripts/smoke-tui.py
Mocked attach streams verify sizing across overflow reattachment and disconnection; sidebar reload checks use rendered-text exclusion and snapshots.

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

Sequence Diagram(s)

sequenceDiagram
  participant TerminalHook as useRenderTerminal
  participant Client as CmuxClient
  participant Stream as attachSurface
  participant Fit as terminal fit
  TerminalHook->>Client: attachSurface(surface)
  Client->>Stream: read attach events
  Stream-->>TerminalHook: OverflowEvent
  TerminalHook->>TerminalHook: waitForRetry(delayMs)
  TerminalHook->>Client: reattach surface
  Client->>Stream: read next attach events
  TerminalHook->>Fit: republish fitted size
  Fit-->>Client: resizeSurface(80, 24)
Loading

Possibly related PRs

  • manaflow-ai/cmux#7695: Introduces sidebar-plugin focus management extended by this PR’s app.rs changes.
  • manaflow-ai/cmux#7886: Modifies the related TypeScript render-attachment event union.
  • manaflow-ai/cmux#8031: Introduces the protocol render-attach event system used by this PR’s overflow typing and recovery logic.
🚥 Pre-merge checks | ✅ 23 | ❌ 2

❌ Failed checks (2 warnings)

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.
Description check ⚠️ Warning The description covers the change and testing, but it misses the required template sections and headings. Rewrite the PR description to match the template: add Summary, Testing, Demo Video, Review Trigger, and Checklist sections.
✅ Passed checks (23 passed)
Check name Status Explanation
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 files are changed in this PR; the diff is Rust/TS/Python only, so Swift actor-isolation checks don’t apply.
Cmux Swift Blocking Runtime ✅ Passed Diff vs merge-base touches TS, Rust, and Python only; no .swift files changed, so the Swift blocking-runtime rule is not applicable.
Cmux Browser Automation Off-Main ✅ Passed PR changes only TUI/web files; no macOS browser-automation command or routing code was modified, and the diff contains no worker/mainActor routing symbols.
Cmux Expensive Synchronous Load ✅ Passed The PR diff only touches a Rust file; no Swift production paths or synchronous agent-history loads were added or moved.
Cmux Cache Substitution Correctness ✅ Passed The changed cache is only transient fit state for live UI attachment; it’s reset on reattach/error and never used in persistence/history/undo/snapshot paths.
Cmux No Hacky Sleeps ✅ Passed The new timers are a bounded, tested attach-recovery abstraction, and the smoke-script polling is test-only scaffolding.
Cmux Algorithmic Complexity ✅ Passed Bounded retry/timer logic only; no new nested scans, repeated sorts/filters, or batch rescans on scalable collections in changed runtime code.
Cmux Swift Concurrency ✅ Passed No Swift files were changed in this PR; the only modified file is Rust, so the Swift concurrency rule is not applicable.
Cmux Swift @Concurrent ✅ Passed The PR touches only TS/Rust/Python/tests; no Swift files are changed, so the @concurrent rule is not applicable.
Cmux Swift Package Boundaries ✅ Passed No Swift files or Package.swift changes appear in the diff, so the Swift package-boundary rule doesn’t apply.
Cmux Swiftpm Lockfiles ✅ Passed PR diff has no Package.resolved, .gitignore, workflow, Package.swift, or Xcode project changes, so the SwiftPM lockfile policy isn’t triggered.
Cmux Swift Logging ✅ Passed No Swift files were changed in the PR diff, so the Swift logging rule is not applicable.
Cmux User-Facing Error Privacy ✅ Passed The new recovery copy is generic, and the production diff doesn’t expose vendor/provider names, raw upstream messages, or other private details.
Cmux Full Internationalization ✅ Passed No new user-facing copy was introduced; the only runtime text uses existing localized key attachOverflowRecoveryFailed, already translated in en and ja.
Cmux Swiftui State Layout ✅ Passed No SwiftUI files or state/layout patterns appear in the PR diff; the rule is not triggered.
Cmux Architecture Rethink ✅ Passed Only app.rs changed; this is a small Rust correctness fix that preserves builtin sidebar focus when no plugin is configured.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No Swift files were changed in this PR diff, so the auxiliary-window close-shortcut rule is not applicable.
Cmux Source Artifacts ✅ Passed PASS: The diff only changes source, tests, scripts, and docs paths; none are artifact-like or in scratch/build/cache dirs, and the rule allows these.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No changed files are Swift sources under a production Sources/ path; the diff only touches cmux-tui Rust/TS/web/test files, so the seam rule is not applicable.
Cmux No Ambient Global State ✅ Passed PASS: the diff contains no Swift production files, so the no-ambient-global-state rule is not triggered.
Title check ✅ Passed The title clearly summarizes the main change: preserving smallest sizing through web reattach.
✨ 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 fix-tui-smallest-reconnect

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

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the web client losing its size contribution to the shared minimum after an overflow-recovery reattach. Both useAttachedTerminal and useRenderTerminal now null out reportedFit and call releaseSurfaceSize on reconnect/teardown, so the server always has a fresh report. The refresh_clients blocking call flagged in a previous review is replaced by a background thread with generation tracking.

  • Re-publish on recovery: Both attach hooks null reportedFit before re-entering the event loop, ensuring applyFit sends the local viewport again even when the size didn't change, preventing the server from dropping this client from the smallest-size pool.
  • releaseSurfaceSize lifecycle: Called in the IIFE finally block (when not cancelled) and unconditionally in the cleanup function; the server implementation is idempotent so the extra call on normal termination is benign but redundant.
  • Rust: non-blocking client refresh: refresh_clients_background spawns a deduplicated background thread with a generation counter, eliminating the 10 s blocking ceiling on the event loop identified in prior review.

Confidence Score: 5/5

Safe to merge; the attach-recovery and size-release logic is well-tested and the server correctly handles all lifecycle transitions.

The core fix — nulling reportedFit before each reattach so the local viewport is re-published — is straightforward and covered by two new sizing tests. The refresh_clients blocking issue from a prior review is fully resolved via a background thread with generation tracking. The one notable finding (double releaseSurfaceSize on the detached path) is a benign redundant call because the server's ReleaseSurfaceSize handler is idempotent.

Both useAttachedTerminal.ts and useRenderTerminal.ts share the double-release pattern on detached; worth a one-line sizeReleased guard in a follow-up.

Important Files Changed

Filename Overview
cmux-tui/frontends/web/src/hooks/useAttachedTerminal.ts Adds overflow-recovery loop with reportedFit = null on reconnect and releaseSurfaceSize in finally/cleanup; double-release on normal detached termination is server-idempotent but redundant
cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts Parallel recovery loop for render attachment with the same reportedFit reset and releaseSurfaceSize lifecycle; same double-release pattern on detached path
cmux-tui/frontends/web/src/lib/coalescedRefresh.ts New coalesced-drain primitive replacing per-event await refreshClients() calls; drain logic is race-free in JS single-threaded model
cmux-tui/frontends/web/src/lib/clientSizing.ts New helper computes pane-level client summary and menu items; all i18n keys (unnamed, disconnect, new keys) are present in both locales
cmux-tui/frontends/web/src/components/ContextMenu.tsx Extends existing custom menu with nested-submenu support; keyboard navigation (ArrowRight/Left) scopes correctly to the active menu level via MenuLayoutContext
cmux-tui/crates/cmux-tui/src/app.rs Replaces blocking refresh_clients() with refresh_clients_background() that spawns a deduplicated thread; generation counter prevents stale results from overwriting newer ones
cmux-tui/crates/cmux-tui/src/pty_input.rs Adds after_operation settlement callback for post-barrier notifications; #[cfg(test)] seam for after_operation_before_cleanup is Rust-only and does not trigger the Swift production-source rule
cmux-tui/frontends/web/test/render-terminal-sizing.test.tsx Three new test cases cover overflow reattach re-publish, no-resize while disconnected, and release-on-detach; the 150 ms real-clock wait in the 'no resize' test is a minor flakiness risk

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser as Web Client
    participant Server as cmux-tui-core

    Note over Browser: Component mounts
    Browser->>Server: attachSurface(surface)
    Server-->>Browser: vt-state / render-state
    Browser->>Server: resizeSurface(cols, rows) [reportedFit set]

    alt Overflow event received
        Server-->>Browser: "overflow{scope:"surface"}"
        Browser->>Server: stream.close()
        Note over Browser: reportedFit = null
        Browser->>Server: attachSurface(surface) [retry]
        Server-->>Browser: vt-state / render-state
        Browser->>Server: resizeSurface(cols, rows) [re-publish unchanged fit]
    end

    alt detached event received
        Server-->>Browser: detached
        Note over Browser: IIFE returns (cancelled=false)
        Browser->>Server: "releaseSurfaceSize(surface) [finally, call #1]"
        Note over Browser: Component later unmounts
        Browser->>Server: "releaseSurfaceSize(surface) [cleanup, call #2 - no-op]"
    end

    alt Component unmount (cancelled path)
        Note over Browser: cancelled = true
        Browser->>Server: releaseSurfaceSize(surface) [cleanup only, single call]
    end
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 Browser as Web Client
    participant Server as cmux-tui-core

    Note over Browser: Component mounts
    Browser->>Server: attachSurface(surface)
    Server-->>Browser: vt-state / render-state
    Browser->>Server: resizeSurface(cols, rows) [reportedFit set]

    alt Overflow event received
        Server-->>Browser: "overflow{scope:"surface"}"
        Browser->>Server: stream.close()
        Note over Browser: reportedFit = null
        Browser->>Server: attachSurface(surface) [retry]
        Server-->>Browser: vt-state / render-state
        Browser->>Server: resizeSurface(cols, rows) [re-publish unchanged fit]
    end

    alt detached event received
        Server-->>Browser: detached
        Note over Browser: IIFE returns (cancelled=false)
        Browser->>Server: "releaseSurfaceSize(surface) [finally, call #1]"
        Note over Browser: Component later unmounts
        Browser->>Server: "releaseSurfaceSize(surface) [cleanup, call #2 - no-op]"
    end

    alt Component unmount (cancelled path)
        Note over Browser: cancelled = true
        Browser->>Server: releaseSurfaceSize(surface) [cleanup only, single call]
    end
Loading

Reviews (19): Last reviewed commit: "fix(tui): linearize subscription recover..." | Re-trigger Greptile

Comment thread cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts
Comment on lines +147 to +155
const waitForRetry = (delayMs: number) =>
new Promise<void>((resolve) => {
wakeRetry = resolve;
retryTimer = setTimeout(() => {
retryTimer = undefined;
wakeRetry = null;
resolve();
}, delayMs);
});

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 waitForRetry duplicated from useAttachedTerminal

The waitForRetry closure (lines 147–155) is copied verbatim from useAttachedTerminal. Both hooks now own identical implementations of the retry-wait primitive. Moving it into attachRecovery.ts—as a standalone exported function alongside attachRecoveryDelay and ATTACH_RECOVERY_STABLE_MS—would keep the cancellation and delay logic in one place.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +71 to +98
it("reports an unchanged local fit again after overflow reattachment", async () => {
globalThis.ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
};
const streams = [
new TestStream([
{ event: "vt-state", surface: 7, cols: 100, rows: 30, data: new Uint8Array(), colors: {} },
{ event: "overflow", scope: "surface", surface: 7, error: "subscriber fell behind" },
]),
new TestStream([
{ event: "vt-state", surface: 7, cols: 100, rows: 30, data: new Uint8Array(), colors: {} },
]),
];
const client = {
attachSurface: vi.fn(async () => streams.shift()!),
resizeSurface: vi.fn(async () => ({ accepted: true, reservation_id: null })),
send: vi.fn(async () => ({})),
} as unknown as CmuxClient;

render(<Harness client={client} />);

await waitFor(() => expect(client.attachSurface).toHaveBeenCalledTimes(2));
await waitFor(() => expect(client.resizeSurface).toHaveBeenCalledTimes(2));
expect(client.resizeSurface).toHaveBeenNthCalledWith(1, 7, 80, 24);
expect(client.resizeSurface).toHaveBeenNthCalledWith(2, 7, 80, 24);
});

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 Test uses real timers; adds ~100 ms per run

After the overflow event the hook waits attachRecoveryDelay(0) = 100 ms before reconnecting. Because vi.useFakeTimers() is not installed, that timer runs on the real wall clock. The test passes but adds about 100 ms of real elapsed time to the suite. Adding vi.useFakeTimers() in beforeEach and vi.runAllTimersAsync() after render would make the test deterministic and instant.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@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 `@cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts`:
- Around line 512-518: Update the resize publication path around applyFit so it
also verifies that the attachment stream is active before registering or
publishing dimensions. Keep resize observers installed if needed, but suppress
viewport-driven reports while stream is null during retry backoff and after
cancellation or failed recovery.
🪄 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: 84c63449-bd61-4c74-bbcb-b160f8f9f016

📥 Commits

Reviewing files that changed from the base of the PR and between 673ee6f and 9b0f783.

📒 Files selected for processing (8)
  • cmux-tui/bindings/typescript/src/protocol/events.ts
  • cmux-tui/crates/cmux-tui-core/src/mux.rs
  • cmux-tui/frontends/web/src/hooks/useAttachedTerminal.ts
  • cmux-tui/frontends/web/src/hooks/useCmuxClient.ts
  • cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts
  • cmux-tui/frontends/web/src/lib/fit.ts
  • cmux-tui/frontends/web/test/attached-terminal-sizing.test.tsx
  • cmux-tui/frontends/web/test/fit.test.ts

Comment thread cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts

@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/frontends/web/src/hooks/useRenderTerminal.ts (1)

134-155: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Move recovery timing into a dedicated cancellation-aware scheduler.

These locally managed setTimeout paths materially add wall-clock retry synchronization. Put retry waiting, stability reset, and cancellation under the shared attachment-recovery abstraction with focused tests.

As per coding guidelines, production retry timing must use a dedicated cancellation-aware abstraction with bounded deadlines and tests. As per path instructions, production runtime timers used for synchronization must be flagged.

Also applies to: 469-473, 534-536

🤖 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/frontends/web/src/hooks/useRenderTerminal.ts` around lines 134 -
155, Replace the locally managed retry and stability timers in the terminal
render setup, including waitForRetry and the referenced timer paths, with the
shared attachment-recovery cancellation-aware scheduler. Configure bounded
deadlines, route cancellation through that abstraction, and flag the production
synchronization timers as required; add focused tests covering retry waits,
stability resets, and cancellation.

Sources: Coding guidelines, Path instructions

🤖 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/frontends/web/src/hooks/useRenderTerminal.ts`:
- Around line 134-155: Replace the locally managed retry and stability timers in
the terminal render setup, including waitForRetry and the referenced timer
paths, with the shared attachment-recovery cancellation-aware scheduler.
Configure bounded deadlines, route cancellation through that abstraction, and
flag the production synchronization timers as required; add focused tests
covering retry waits, stability resets, and cancellation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 25966626-80c8-443a-b44a-08cc9618c7a8

📥 Commits

Reviewing files that changed from the base of the PR and between b306078 and b1090b7.

📒 Files selected for processing (2)
  • cmux-tui/frontends/web/src/hooks/useRenderTerminal.ts
  • cmux-tui/frontends/web/test/render-terminal-sizing.test.tsx

@cursor

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

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/app.rs (1)

3084-3102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared state invalidation into a helper.

The plugin state reset block is duplicated four times across sync_sidebar_plugin and apply_sidebar_plugin_status (in both the config.sidebar.plugin.is_none() and visibility-check branches). Extracting this block into a clear_sidebar_plugin_state helper will DRY up the code and prevent missing a state field during future updates.

  • cmux-tui/crates/cmux-tui/src/app.rs#L3084-L3102: replace the inline state resets with calls to self.clear_sidebar_plugin_state() and add the helper definition above this method.
  • cmux-tui/crates/cmux-tui/src/app.rs#L3135-L3153: replace the inline state resets with calls to self.clear_sidebar_plugin_state().
♻️ Proposed refactor

In cmux-tui/crates/cmux-tui/src/app.rs#L3084-L3102:

+    fn clear_sidebar_plugin_state(&mut self) {
+        self.session.invalidate_sidebar_plugin_sync();
+        self.sidebar_plugin_surface = None;
+        self.sidebar_plugin_error = None;
+        self.sidebar_plugin_retry_after_ms = None;
+        self.sidebar_plugin_retry_at = None;
+        self.sidebar_focus_pending = false;
+    }
+
     fn sync_sidebar_plugin(&mut self, relaunch: bool) -> bool {
         if self.config.sidebar.plugin.is_none() {
-            self.session.invalidate_sidebar_plugin_sync();
-            self.sidebar_plugin_surface = None;
-            self.sidebar_plugin_error = None;
-            self.sidebar_plugin_retry_after_ms = None;
-            self.sidebar_plugin_retry_at = None;
-            self.sidebar_focus_pending = false;
+            self.clear_sidebar_plugin_state();
             return false;
         }
         if self.sidebar_width < 3 || !self.sidebar_visible {
-            self.session.invalidate_sidebar_plugin_sync();
-            self.sidebar_plugin_surface = None;
-            self.sidebar_plugin_error = None;
-            self.sidebar_plugin_retry_after_ms = None;
-            self.sidebar_plugin_retry_at = None;
+            self.clear_sidebar_plugin_state();
             self.sidebar_focused = false;
-            self.sidebar_focus_pending = false;
             return false;
         }

In cmux-tui/crates/cmux-tui/src/app.rs#L3135-L3153:

     fn apply_sidebar_plugin_status(&mut self, status: SidebarPluginSurface, relaunch: bool) {
         if self.config.sidebar.plugin.is_none() {
-            self.session.invalidate_sidebar_plugin_sync();
-            self.sidebar_plugin_surface = None;
-            self.sidebar_plugin_error = None;
-            self.sidebar_plugin_retry_after_ms = None;
-            self.sidebar_plugin_retry_at = None;
-            self.sidebar_focus_pending = false;
+            self.clear_sidebar_plugin_state();
             return;
         }
         if !self.sidebar_visible {
-            self.session.invalidate_sidebar_plugin_sync();
-            self.sidebar_plugin_surface = None;
-            self.sidebar_plugin_error = None;
-            self.sidebar_plugin_retry_after_ms = None;
-            self.sidebar_plugin_retry_at = None;
+            self.clear_sidebar_plugin_state();
             self.sidebar_focused = false;
-            self.sidebar_focus_pending = false;
             return;
         }
🤖 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 3084 - 3102, Extract the
duplicated sidebar plugin state reset into a clear_sidebar_plugin_state helper
defined above sync_sidebar_plugin in cmux-tui/crates/cmux-tui/src/app.rs.
Replace the inline resets in cmux-tui/crates/cmux-tui/src/app.rs lines 3084-3102
and lines 3135-3153 with calls to this helper, preserving each branch’s
additional focus-state updates.
🤖 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/scripts/smoke-tui.py`:
- Around line 220-228: Update wait_render_excludes to avoid accepting a
transient marker-free frame during reload: require the post-reload readiness
condition or keep observing until the absence of needle remains stable for a
short window before returning. Preserve the existing timeout and assertion
behavior, and ensure callers such as the exclusion check at line 417 validate
the finalized render state.

---

Outside diff comments:
In `@cmux-tui/crates/cmux-tui/src/app.rs`:
- Around line 3084-3102: Extract the duplicated sidebar plugin state reset into
a clear_sidebar_plugin_state helper defined above sync_sidebar_plugin in
cmux-tui/crates/cmux-tui/src/app.rs. Replace the inline resets in
cmux-tui/crates/cmux-tui/src/app.rs lines 3084-3102 and lines 3135-3153 with
calls to this helper, preserving each branch’s additional focus-state updates.
🪄 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: ed2ff376-e364-4069-aca5-f645647fa6a6

📥 Commits

Reviewing files that changed from the base of the PR and between 2dbcd65 and 87a6db0.

📒 Files selected for processing (2)
  • cmux-tui/crates/cmux-tui/src/app.rs
  • cmux-tui/scripts/smoke-tui.py

Comment thread cmux-tui/scripts/smoke-tui.py Outdated
@cursor

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

@lawrencecchen
lawrencecchen merged commit 3112484 into main Jul 18, 2026
8 checks passed
@lawrencecchen
lawrencecchen deleted the fix-tui-smallest-reconnect branch July 18, 2026 03:58
@lawrencecchen
lawrencecchen restored the fix-tui-smallest-reconnect branch July 18, 2026 10:19
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