Skip to content

Fix Dock sidebar render after reopen - #5437

Merged
austinywang merged 3 commits into
mainfrom
issue-5435-dock-blank-reopen
Jun 5, 2026
Merged

austinywang merged 3 commits into
mainfrom
issue-5435-dock-blank-reopen

Conversation

@austinywang

@austinywang austinywang commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a regression UI test for Dock terminal rendering after right sidebar hide/show
  • route right-sidebar Dock visibility through one lifecycle sync so reopening reactivates and redraws the portal-hosted terminal

Fixes #5435

Verification

  • CMUX_SKIP_ZIG_BUILD=1 ./scripts/reload.sh --tag issue-5435-dock-blank-reopen
  • Manual dogfood: Dock mode with top -s 1, hide/reopen right sidebar, confirmed terminal renders after reopen and portal returns to entryVisible=1 hostedHidden=0
  • GitHub e2e: FeedSidebarUITests/testDockTerminalRerendersAfterRightSidebarHideShow via test-e2e.yml passed in run https://github.com/manaflow-ai/cmux/actions/runs/26997746067

Test policy

  • Commit f2a9088c3 adds the failing regression test only
  • Commit e17d0405b adds the fix
  • Commit 405e8257e updates the Swift file length budget metadata

Localization

  • No user-facing strings changed; only test strings and lifecycle wiring changed.

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


Note

Low Risk
Changes are limited to Dock/right-sidebar lifecycle wiring and UI tests; no auth, data, or user-facing copy changes.

Overview
Fixes Dock terminals going blank after toggling the right sidebar off and on by driving activation from sidebar state instead of DockPanelView lifecycle hooks.

DockControlsStore gains synchronizeSidebarLifecycle, which deactivates when the sidebar is hidden or not in Dock mode and otherwise reactivates with the current root directory and workspace. RightSidebarPanelView calls this on appear/disappear and when visibility, mode, dockRootDirectory, or workspaceId change; DockPanelView no longer runs its own onAppear / onDisappear / onChange activate logic. A UI regression test hides/shows the sidebar (⌥⌘B), keeps the Dock render PID alive, and checks the terminal redraws via a bright-pixel screenshot crop.

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


Summary by cubic

Fixes the Dock terminal staying blank after hiding and reopening the right sidebar. The Dock lifecycle now reactivates on reopen, keeps the render process alive, and redraws the terminal. Fixes #5435.

  • Bug Fixes

    • Centralized Dock lifecycle in RightSidebarPanelView via synchronizeDockLifecycle(...), which calls DockControlsStore.synchronizeSidebarLifecycle(...) on appear/disappear and when sidebar visibility, mode, rootDirectory, or workspaceId change; deactivates when hidden or not in Dock mode, and re-syncs when mode availability refreshes.
    • Removed local lifecycle hooks from DockPanelView; lifecycle is driven by sidebar state so reopen reactivates and redraws.
    • Added a UI regression test that hide/shows the sidebar (⌥⌘B), asserts the render PID stays alive, and verifies redraw via a bright-pixel screenshot check.
  • Refactors

    • Updated .github/swift-file-length-budget.tsv for Sources/DockPanelView.swift.

Written for commit 405e825. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Dock controls now reliably follow the right sidebar’s visibility and mode so the Dock activates and deactivates correctly.
    • Dock terminal reliably re-renders after hiding and showing the right sidebar.
  • Refactor

    • Sidebar lifecycle handling centralized to improve stability and avoid duplicate lifecycle triggers.
  • Tests

    • Added end-to-end UI test verifying Dock render readiness, process liveness, and visual re-rendering when toggling the right sidebar.

@vercel

vercel Bot commented Jun 5, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled Jun 5, 2026 5:43am
cmux-staging Building Building Preview, Comment Jun 5, 2026 5:43am

@coderabbitai

coderabbitai Bot commented Jun 5, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Centralizes Dock activation into DockControlsStore.synchronizeSidebarLifecycle, moves lifecycle synchronization into RightSidebarPanelView, removes DockPanelView lifecycle hooks, and adds a UI test that verifies Dock re-renders after hiding and re-showing the right sidebar.

Changes

Dock Sidebar Lifecycle Synchronization and Rerender Fix

Layer / File(s) Summary
Dock controls lifecycle synchronization contract
Sources/DockPanelView.swift
DockControlsStore.synchronizeSidebarLifecycle(isRightSidebarVisible:mode:rootDirectory:workspaceId:) centralizes activation/deactivation: deactivates when sidebar is hidden or mode is not .dock, otherwise activates with provided identifiers.
Right sidebar Dock lifecycle integration
Sources/RightSidebarPanelView.swift
RightSidebarPanelView calls synchronizeDockLifecycle() on appear and in onChange handlers (mode, visibility, rootDirectory, workspaceId); refreshModeAvailabilityAndFocusIfNeeded() triggers synchronization early.
DockPanelView lifecycle unwiring
Sources/DockPanelView.swift
Removes .onAppear, .onDisappear, and .onChange handlers that directly called store.activate()/deactivate().
Test imports, readiness marker and config generation
cmuxUITests/FeedSidebarUITests.swift
Adds CoreGraphics/ImageIO imports, dockRenderReadyPath property, setUp init/cleanup for the readiness marker, and writeDockRenderConfig() to emit a render control that writes PID/readiness payloads.
Dock render readiness and process monitoring
cmuxUITests/FeedSidebarUITests.swift
Adds waitForDockRenderReady() polling, dockRenderProcessIsAlive(pid:) liveness checking, loadDockRenderReadyMarker() and loadDockRenderReadyPayload() parsing, and dockRenderProcessPID() extraction.
Visual verification and UI detection helpers
cmuxUITests/FeedSidebarUITests.swift
Adds waitForRightSidebarHidden(), waitForDockTerminalBrightPixels(), dockTerminalBrightPixelCount(), and brightPixelCount() that decode screenshots and count bright pixels in the DockPanel region.
End-to-end Dock hide/show rerender test
cmuxUITests/FeedSidebarUITests.swift
testDockTerminalRerendersAfterRightSidebarHideShow() launches with Dock enabled, waits for readiness, verifies initial bright pixels, hides sidebar via Opt-Cmd-B and asserts process liveness, re-shows sidebar and verifies process liveness and re-render via bright-pixel threshold.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#5342: Touches RightSidebarPanelView docking wiring and workspace-derived dockRootDirectory; related at lifecycle/context wiring points.
  • manaflow-ai/cmux#5174: Also updates RightSidebarPanelView mode availability and refresh flow that overlaps integration in refreshModeAvailabilityAndFocusIfNeeded().

Possibly related issues

  • #5435: Right sidebar Dock section renders blank after hiding + reopening the sidebar — This PR implements the synchronization and adds the hide→show UI test specifically to address that reproduction and symptom.

Poem

🐰 I hopped by the sidebar, it blinked out of sight,
I nudged a synchronize and brought back the light,
The portal rebinds, pixels dance in their rows,
No more blank terminals—hip hop, here it goes! 🎉

🚥 Pre-merge checks | ✅ 17 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (17 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix Dock sidebar render after reopen' clearly and specifically summarizes the main fix: addressing the blank Dock rendering issue after hiding and reopening the right sidebar.
Linked Issues check ✅ Passed The code changes directly address all requirements from issue #5435: centralizes Dock lifecycle through synchronizeSidebarLifecycle (routing visibility changes through the same activation path), adds a UI regression test verifying hide→show preserves render PID and redraws the terminal, and removes the incomplete lifecycle hooks from DockPanelView.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing #5435: DockControlsStore/RightSidebarPanelView lifecycle wiring, DockPanelView hook removal, and UI regression test. No unrelated changes detected.
Cmux Swift Actor Isolation ✅ Passed DockControlsStore properly marked @MainActor, RightSidebarMode nonisolated Sendable, UI stores accessed only from MainActor context, no background access violations.
Cmux Swift Blocking Runtime ✅ Passed Production code introduces no blocking patterns. synchronizeSidebarLifecycle() uses only synchronous calls. Test polling with Thread.sleep is allowed per check policy.
Cmux No Hacky Sleeps ✅ Passed PR changes only in Swift files. Check scope excludes Swift code per its own rules; no TypeScript/JavaScript/shell changes present.
Cmux Algorithmic Complexity ✅ Passed No algorithmic complexity violations: controls array bounded by dock config (~10 items max), onChange handlers avoid O(n²), test code uses fixed-size screen region processing.
Cmux Swift Concurrency ✅ Passed PR refactors Dock lifecycle routing with synchronous methods; no legacy async patterns (Task, DispatchQueue, fire-and-forget, new Combine) introduced in cmux-owned code.
Cmux Swift @Concurrent ✅ Passed All changes are synchronous methods with no async/await, @concurrent annotations, or nonisolated async patterns. No violations of swift-concurrent-annotation.md rules detected.
Cmux Swift File And Package Boundaries ✅ Passed DockPanelView (924 lines, +13 net) is existing oversized file with focused bug fix under 250-line threshold. RightSidebarPanelView (686 lines) under threshold. FeedSidebarUITests test code allowed.
Cmux Swift Logging ✅ Passed No logging violations found. Production code has zero print/debugPrint/dump/NSLog statements; test file is explicitly allowed per rules.
Cmux User-Facing Error Privacy ✅ Passed PR changes are internal lifecycle methods and test code; no new user-facing strings, error messages, or sensitive data exposure detected.
Cmux Full Internationalization ✅ Passed PR introduces no new user-facing strings; all changes are internal method reorganization and test code. Existing user-facing strings use proper localization APIs with complete catalog translations.
Cmux Swiftui State Layout ✅ Passed Adds method to existing ObservableObject without new state patterns, @Published, GeometryReader layout changes, store in lazy lists, or render-time mutations.
Cmux Architecture Rethink ✅ Passed Centralizes Dock lifecycle via single synchronizeSidebarLifecycle entrypoint, making blank-portal bad state unrepresentable; no timing repairs, duplicate wiring, or new state flags; test-only polling.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed Changes to SwiftUI View structs and test fixtures only; no NSWindow, NSPanel, NSWindowController, Window, or WindowGroup created. Internal lifecycle refactoring explicitly allowed by rule.
Description check ✅ Passed The pull request description comprehensively covers all required sections from the template: Summary, Testing/Verification, and Checklist items are addressed.

✏️ 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 issue-5435-dock-blank-reopen

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.

@austinywang
austinywang force-pushed the issue-5435-dock-blank-reopen branch from 7d35407 to f565e52 Compare June 5, 2026 05:30
@greptile-apps

greptile-apps Bot commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes Dock terminals staying blank after hiding and reopening the right sidebar (⌥⌘B) by moving lifecycle ownership from the portal-hosted DockPanelView to RightSidebarPanelView, which participates in standard SwiftUI visibility signals.

  • DockControlsStore.synchronizeSidebarLifecycle is a new single entry point that activates when the sidebar is visible and in dock mode, deactivates otherwise; DockPanelView no longer runs its own onAppear/onDisappear activate-deactivate hooks.
  • RightSidebarPanelView drives the dock lifecycle from onAppear, onDisappear, and onChange observers for isVisible, mode, dockRootDirectory, and workspaceId; refreshModeAvailabilityAndFocusIfNeeded also syncs eagerly when the mode does not change after a feature-flag toggle.
  • testDockTerminalRerendersAfterRightSidebarHideShow is a new end-to-end UI regression test that writes a PID-marker dock control, asserts bright pixels in the dock region, toggles the sidebar twice, and verifies both process liveness and visual redraw.

Confidence Score: 5/5

Safe to merge. The change centralises dock lifecycle in the sidebar view and adds a direct regression test that verifies the exact bug scenario.

The production fix is logically sound: synchronizeSidebarLifecycle is idempotent (activate/deactivate both safe to call multiple times), all four call sites in RightSidebarPanelView pass the current SwiftUI-owned state, and removing DockPanelView's own hooks eliminates the original source-of-truth split. The regression test exercises process liveness and visual redraw end-to-end. No new data paths, auth boundaries, or persistence changes are introduced.

No files require special attention.

Important Files Changed

Filename Overview
Sources/DockPanelView.swift Adds synchronizeSidebarLifecycle to DockControlsStore and removes onAppear/onDisappear/onChange lifecycle hooks from DockPanelView, delegating lifecycle ownership to RightSidebarPanelView.
Sources/RightSidebarPanelView.swift Adds synchronizeDockLifecycle wrapper and wires it into sidebar onAppear/onDisappear and onChange observers for mode, visibility, rootDirectory, workspaceId; also adds eager sync in refreshModeAvailabilityAndFocusIfNeeded when mode stays unchanged after refresh.
cmuxUITests/FeedSidebarUITests.swift Adds testDockTerminalRerendersAfterRightSidebarHideShow with process-liveness checks, a bright-pixel screenshot assertion, and supporting helpers (writeDockRenderConfig, dockRenderProcessIsAlive, brightPixelCount, waitForRightSidebarHidden).
.github/swift-file-length-budget.tsv Bumps DockPanelView.swift line budget from 923 to 924 to account for the new synchronizeSidebarLifecycle method.

Sequence Diagram

sequenceDiagram
    participant User
    participant SwiftUI
    participant RSPV as RightSidebarPanelView
    participant DCS as DockControlsStore
    participant DPV as DockPanelView

    Note over User,DPV: Sidebar Hide (⌥⌘B)
    User->>SwiftUI: press ⌥⌘B
    SwiftUI->>RSPV: onChange(isVisible: false)
    RSPV->>DCS: synchronizeSidebarLifecycle(isVisible:false, ...)
    DCS->>DCS: deactivate()
    Note over RSPV,DPV: DockPanelView stays in portal hierarchy

    Note over User,DPV: Sidebar Reopen (⌥⌘B)
    User->>SwiftUI: press ⌥⌘B
    SwiftUI->>RSPV: onChange(isVisible: true)
    RSPV->>DCS: synchronizeSidebarLifecycle(isVisible:true, mode:.dock, ...)
    DCS->>DCS: activate(rootDirectory:, workspaceId:)
    DCS->>DPV: redraws portal-hosted terminal
Loading

Reviews (2): Last reviewed commit: "chore: update dock panel length budget" | Re-trigger Greptile

@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 `@cmuxUITests/FeedSidebarUITests.swift`:
- Around line 391-399: The test is sampling the rightmost 12% of the window
which can include main terminal pixels; update dockTerminalBrightPixelCount(in:)
to locate the Dock panel element (e.g., the accessibility identifier or element
named "DockPanel") and take the screenshot/crop based on that element's frame
instead of using xFractionStart:0.88; compute the crop bounds from the DockPanel
XCUIElement (or its firstMatch) and pass that image region to brightPixelCount
so the check only inspects the Dock panel; apply the same change to the other
occurrences referenced in the range that currently use fractional window
cropping.
🪄 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: 763da0d6-a148-412e-bd56-57adff5968c0

📥 Commits

Reviewing files that changed from the base of the PR and between e716def and f565e52.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (3)
  • Sources/DockPanelView.swift
  • Sources/RightSidebarPanelView.swift
  • cmuxUITests/FeedSidebarUITests.swift

Comment thread cmuxUITests/FeedSidebarUITests.swift
@austinywang
austinywang merged commit 218057d into main Jun 5, 2026
21 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 29, 2026
5 tasks done

This branch was successfully deployed

1 active deployment
Preview – cmux — 405e8257 Deployed Jun 5, 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.

Right sidebar Dock section renders blank after hiding + reopening the sidebar (opt-cmd-B); mode-switch is the workaround

1 participant