Skip to content

Fix attached Web Inspector resize: bottom-dock divider, small-pane minimum-size policy, WebKit size sync#7771

Open
austinywang wants to merge 9 commits into
mainfrom
issue-7762-web-inspector-resize
Open

Fix attached Web Inspector resize: bottom-dock divider, small-pane minimum-size policy, WebKit size sync#7771
austinywang wants to merge 9 commits into
mainfrom
issue-7762-web-inspector-resize

Conversation

@austinywang

@austinywang austinywang commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #7762. Fixes #663. Related: #2933 (item 2 tracks this same symptom), #7037 (same hit-swallowing failure family — prior art for the pass-through/claim approach), #617.

Problem

Resizing the attached Web Inspector was unreliable: the divider was intermittently dead to drag, glitchy when it did work, and much worse in small panes / on small displays.

Root causes:

  1. Bottom dock had no cmux-owned divider at all. HostedInspectorDockSide only modeled .leading/.trailing. Bottom-dock resize relied on WebKit's tiny in-content resizer inside the inspector frontend, which cmux's portal hit-testing routes around/swallows → Cannot resize devtools console panel in built-in browser #663's completely dead divider.
  2. Small panes are forced into bottom dock. The local-inline host auto-redocks to bottom when a side dock would leave < 240pt of page width — landing small panes exactly in the dock mode with no divider. That's why the issue is worst on small panes/displays.
  3. Brittle candidate detection. Side-dock resolution used a 1pt epsilon and a fixed overlap > 8 guard, and mouseDown re-resolved the divider independently of hitTest, so transient WebKit layout churn silently dropped drags.
  4. Frame fighting. cmux wrote inspector/page frames directly but never told WebKit the user-chosen size, so WebKit's attachment relayout could snap the divider back after/on relayout → glitchy resize.

Fix

  • New Sources/Panels/HostedInspectorResizeGeometry.swift (pure, unit-tested): HostedInspectorDockSide moved out of BrowserWindowPortal.swift, gains .bottom, axis-generic divider position/hit-rect/clamp/frame math, tolerant resolve epsilon (4pt), container-scaled cross-axis overlap guard.
  • Explicit minimum-size policy (HostedInspectorMinimumSizePolicy): side dock keeps a 120pt inspector / 120pt page minimum; bottom dock 100pt inspector / 80pt page. When the pane can't fit both minimums, each minimum degrades to half the container, so the allowed divider range is never empty — the divider clamps gracefully instead of locking up.
  • Bottom-dock divider drag in both hosts (window-portal WindowBrowserHostView and local-inline WebViewRepresentable.HostContainerView): hitTest claims the seam band (±6pt portal / ±10pt inline) so neither the page WKWebView nor the inspector's own web content swallows the drag; resizeUpDown cursor; Y-axis drag; preferred-height record/reapply with the same fraction-of-container semantics as width.
  • New Sources/Panels/HostedInspectorAttachedSizeSync.swift: on mouseUp, pushes the final size into WebKit via InspectorFrontendHost.setAttachedWindowHeight/Width on the inspector frontend webview (typeof-guarded JS, nil-safe), so WebKit's attachment manager agrees with the user-chosen size and stops fighting cmux's layout.
  • Drag-vs-relayout races closed: adaptive bottom-dock redock and the portal-sync bottom-dock frame repair no longer run while a divider drag is active; mouseDown falls back to the hit cached by hitTest (0.1s lifetime, ±hit-band distance) so event-to-event layout churn can't lose the drag.

Commit structure (red/green)

  • Commit 1 (4275c014ab) adds cmuxTests/HostedInspectorBottomDockDividerTests.swift only — expected red: both hosts fail to claim the bottom-dock seam and the portal drag doesn't resize. Proof run on that SHA: test-e2e run — failed on exactly the three bottom-dock expectations. (smallPaneSideDockDragMovesIntoDegradedRange is a guard test for the clamp policy and may already pass on commit 1.)
  • Commit 2 (5efa87918c) is the fix plus cmuxTests/HostedInspectorResizeGeometryTests.swift (pure geometry: .bottom resolve, hit-rect bands, never-empty clamp range, frame complementarity, size-sync JS shape). Same filter on the fixed code: test-e2e run — passed (run @ e793f50, which also carries the review fixes: bottom-dock seam guard in the local-inline host, widened pinned test fixture; c690206 additionally stops already-bottom-docked layouts from re-triggering the adaptive WI._dockBottom() redock).

Test coverage honesty

Deterministic coverage: divider hit-testing/claiming in both hosts, drag→frame application, small-pane clamp policy, and the pure geometry — all via cmuxTests. Not covered by automated tests: the live feel of a real drag against a real WKWebView-hosted inspector (WebKit's actual attachment relayout timing, InspectorFrontendHost sync round-trip) — that needs manual dogfood: open a browser pane → open DevTools (attached, bottom and side dock) → drag the divider, including with the pane narrowed below ~240pt so the adaptive bottom dock engages.

Localization audit

No user-facing strings were added or changed (divider/cursor interaction only); no Localizable.xcstrings changes needed.

Swift file budgets: BrowserWindowPortal.swift 3960/3981, BrowserPanelView.swift 7924/7959 — both shrank; no TSV changes; new files are 260/34/158/98 lines.

🤖 Generated with Claude Code


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


Note

Medium Risk
Touches pointer routing, live NSView frame layout, and WebKit inspector attachment JS across portal and browser panel hosts; behavior is heavily unit-tested but real WKWebView drag feel still needs manual verification.

Overview
Attached Web Inspector resizing is reworked so cmux owns the divider in bottom dock as well as side dock, with clamping that still works in narrow panes and without fighting WebKit during drags.

Geometry and policyHostedInspectorDockSide moves into HostedInspectorResizeGeometry.swift and gains .bottom, axis-generic divider math, and HostedInspectorMinimumSizePolicy so side/bottom docks clamp inspector vs page size without an empty drag range on tiny containers.

Hosts (portal + local inline) — Divider hit-testing claims the seam (vertical or horizontal cursor), drags use extent (width or height) with stored fractions, a short-lived hit cache bridges hitTestmouseDown, and adaptive bottom-dock / frame repair are skipped while dragging. HostedInspectorDragFrameNotificationSilencer suppresses inspected-view frame notifications during drag; one InspectorFrontendHost.setAttachedWindowWidth/Height sync runs on mouseUp via new HostedInspectorAttachedSizeSync.

FrontendHostedInspectorDockControlScript disables the in-page #docked-resizer so WebKit’s built-in handle doesn’t compete with cmux.

Tests cover bottom-dock claiming/drag, geometry, dock-control script, and a widened side-dock panel test fixture.

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

Summary by CodeRabbit

  • New Features
    • Dock-side–aware Web Inspector divider resizing using preferred extents (including bottom-docked behavior) with improved clamping and minimum-size handling.
    • Added attached inspector window size synchronization during divider drags.
    • Enhanced dock control behavior by disabling the frontend’s docked resizer when cmux owns resizing.
  • Bug Fixes
    • Stabilized hosted divider hit-testing/cursor routing and reduced unnecessary frame updates; improved hosted rendering refresh and teardown safety.
    • Prevented background “webframe repair” while a hosted inspector divider drag is active.
  • Tests
    • Added coverage for bottom-dock divider interactions and hosted inspector resize geometry (including JS generation).

austinywang and others added 2 commits July 9, 2026 16:27
…, #663)

Regression tests only — expected RED on this commit:
- portal host does not claim the bottom-dock page/inspector seam in hitTest
- portal host mouseDown/mouseDragged at the seam does not resize the inspector
- local-inline host does not claim the bottom-dock seam

smallPaneSideDockDragMovesIntoDegradedRange is a guard test for the
small-pane clamp policy and may already pass on this commit.

The fix lands in the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…licy, WebKit size sync

Fixes #7762. Fixes #663.

The attached Web Inspector divider was intermittently dead and glitchy,
worst in small panes: bottom dock (which cmux forces when a pane is too
narrow for a side dock) had no cmux-owned divider at all, side-dock
candidate detection used brittle 1pt epsilons, and cmux never told
WebKit the user-chosen size so WebKit's attachment relayout snapped it
back.

- Extract HostedInspectorDockSide into HostedInspectorResizeGeometry.swift,
  add .bottom, make the geometry axis-generic, raise resolve epsilon to 4,
  and scale the cross-axis overlap guard with the container.
- HostedInspectorMinimumSizePolicy: proportional-degrade clamp whose
  divider range is never empty, so small panes degrade to a half/half
  split instead of a locked divider.
- Wire bottom-dock hit-testing, resizeUpDown cursor, Y-axis drag,
  preferred-height record/reapply (fraction semantics) through both the
  window-portal host and the local-inline host.
- On mouseUp, push the final size into WebKit via
  InspectorFrontendHost.setAttachedWindowHeight/Width on the inspector
  frontend webview so WebKit stops fighting the layout.
- Cache the hitTest divider candidate briefly so mouseDown cannot lose
  the drag to layout churn between events.
- Never run adaptive bottom-dock or portal bottom-dock frame repair
  while a divider drag is active.

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

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 10, 2026 4:32pm
cmux-staging Building Building Preview, Comment Jul 10, 2026 4:32pm

@coderabbitai

coderabbitai Bot commented Jul 9, 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

Hosted Web Inspector resizing now supports leading, trailing, and bottom dock orientations through extent-based geometry. Divider hit caching, preferred width/height persistence, attached-size synchronization, lifecycle refreshes, and geometry and interaction tests are added.

Changes

Hosted Inspector resize flow

Layer / File(s) Summary
Dock-side resize geometry
Sources/Panels/HostedInspectorResizeGeometry.swift, cmuxTests/HostedInspectorResizeGeometryTests.swift
Adds dock-side resolution, orientation-aware divider geometry, minimum-size clamping, frame resizing, and geometry tests.
Hosted inspector layout and interaction
Sources/Panels/BrowserPanelView.swift
Reworks preferred sizing, layout reapplication, hit caching, cursor selection, candidate scoring, rendering refresh, lifecycle handling, and drag behavior around horizontal or vertical extents.
Portal drag integration and persistence
Sources/BrowserWindowPortal.swift, Sources/Panels/HostedInspectorAttachedSizeSync.swift
Uses dock-side extents during portal drags, persists width or height preferences, synchronizes attached inspector size, and suppresses conflicting repair during active drags.
Frontend dock-control ownership
Sources/App/HostedInspectorDockControlScript.swift, cmuxTests/HostedInspectorDockControlScriptTests.swift
Disables the frontend docked resizer for attached hosting and verifies the injected control behavior.
Project wiring and interaction coverage
cmux.xcodeproj/project.pbxproj, cmuxTests/HostedInspectorBottomDockDividerTests.swift, cmuxTests/BrowserPanelTests.swift
Includes hosted inspector sources and tests in the Xcode project and covers portal, inline bottom-divider, geometry, and widened right-docked layouts.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BrowserPanelView
  participant HostedInspectorResizeGeometry
  participant WindowBrowserSlotView
  participant HostedInspectorAttachedSizeSync
  participant WKWebView

  User->>BrowserPanelView: Drag hosted inspector divider
  BrowserPanelView->>HostedInspectorResizeGeometry: Resolve dock side and clamp extent
  HostedInspectorResizeGeometry-->>BrowserPanelView: Updated page and inspector frames
  BrowserPanelView->>WindowBrowserSlotView: Record preferred extent
  BrowserPanelView->>HostedInspectorAttachedSizeSync: Sync final inspector extent
  HostedInspectorAttachedSizeSync->>WKWebView: Evaluate attached-size JavaScript
Loading

Possibly related issues

Possibly related PRs

  • manaflow-ai/cmux#712 — Both changes update hosted inspector divider preference persistence and reapplication.
  • manaflow-ai/cmux#1230 — Both changes modify hosted inspector divider geometry, sizing constraints, and frame application.

Suggested reviewers: lawrencecchen


Important

Pre-merge checks failed

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

❌ Failed checks (3 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error New user-facing keys were added to Resources/Localizable.xcstrings and web/messages/en.json,ja.json, but the catalog/routing support 20 locales and these entries only have en/ja. Add translations for every existing xcstrings locale and replicate the new web message keys across all locales listed in web/i18n/routing.ts.
Cmux No Test Or Debug Seam In Production Source ❌ Error BrowserPanelView.swift adds #if DEBUG func localInlineSlotViewForDebug(), a debug-only accessor in production code used only for logging and with no production caller. Move the observation into Tests via @testable import or into a dedicated debug file/folder, and remove the #if DEBUG seam from production source.
Cmux No Ambient Global State ❌ Error Sources/Panels/HostedInspectorAttachedSizeSync.swift adds a static-only helper namespace (struct ... { static func ... }), which this rule forbids. Move sync/javaScript onto an owning injectable type (e.g. HostContainerView or WindowBrowserHostView) as instance methods, or make them private file-scope helpers if truly pure.
Docstring Coverage ⚠️ Warning Docstring coverage is 1.80% 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 PR description is detailed, but it does not follow the required template sections for Summary, Testing, Demo Video, Review Trigger, or Checklist. Reformat the description to include the required headings and fill in Summary, Testing, Demo Video (or note none), Review Trigger block, and Checklist items.
✅ Passed checks (20 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 PASS: New UI helpers are main-actor scoped, pure value types stay nonisolated, and the only @unchecked Sendable ref is private with a documented main-thread restore path.
Cmux Swift Blocking Runtime ✅ Passed No new blocking waits, sleeps, semaphores, locks, or main-thread sync were added; the change only cancels drag state and asynchronously restores teardown state.
Cmux Browser Automation Off-Main ✅ Passed No browser.* socket command routing changed; the diff only adds hosted inspector resize/layout helpers and @MainActor/AppKit-side sync code.
Cmux Expensive Synchronous Load ✅ Passed PASS: The PR only changes inspector drag/geometry code; diff search found no new synchronous agent-history loads or JSONL scans on interactive/main-actor paths.
Cmux Cache Substitution Correctness ✅ Passed Cache use is transient UI-only (0.1s hit reuse, cleared on use/teardown) and extent prefs have cold/fallback handling; no persistence/history/undo/snapshot path.
Cmux No Hacky Sleeps ✅ Passed PASS: PR touches Swift runtime and tests only; no changed TS/JS/shell/build-runtime files add fixed sleeps/timers, and the only JS delays are test-only scaffolding.
Cmux Algorithmic Complexity ✅ Passed Diff only adds teardown helpers that clear drag state and end the silencer; no new collection scans or repeated filtering were introduced.
Cmux Swift Concurrency ✅ Passed PASS: The new async code is limited to a WKWebView callback boundary and one small DispatchQueue.main.async UI hop; no new background queues, Combine, or unmanaged Tasks.
Cmux Swift @Concurrent ✅ Passed No changed Swift code adds invalid @concurrent or nonisolated async; the new size-sync helper is @MainActor UI-bound, and the only nonisolated helper is synchronous pure JS generation.
Cmux Swift File And Package Boundaries ✅ Passed PASS: New helper is 313 lines and coherent; oversized BrowserPanelView/BrowserWindowPortal shrank or stayed flat, and the new code is AppKit/UI glue plus tests, not a boundary violation.
Cmux Swiftpm Lockfiles ✅ Passed The diff touches only two Swift source files; no .gitignore, workflow, Package.resolved, or Xcode package-reference changes are present.
Cmux Swift Logging ✅ Passed PASS: The only new logging is cmuxDebugLog inside #if DEBUG in HostedInspectorAttachedSizeSync; no new print/NSLog/Logger` in runtime sources.
Cmux User-Facing Error Privacy ✅ Passed Touched production files add no user-facing error/alert text; only a DEBUG-only internal log mentions evaluateJavaScript results/errors.
Cmux Swiftui State Layout ✅ Passed The PR only adjusts contained AppKit bridge views; it adds no new ObservableObject/@published state, render-time state writes, or problematic GeometryReader layout measurement.
Cmux Architecture Rethink ✅ Passed The PR centralizes resize geometry and per-host drag ownership, with no sleeps/polling/locks; the only async restore is a deinit-safe platform bridge.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only changes view geometry and test fixtures; no new standalone NSWindow/NSPanel/WindowGroup code or cmux.* identifier registration changes.
Cmux Source Artifacts ✅ Passed All changed paths are hand-written source/test/config files; no logs, screenshots, caches, temp, DerivedData, or other artifact dirs appear in the PR diff.
Title check ✅ Passed The title clearly summarizes the core change: fixing attached Web Inspector resizing, especially bottom-dock behavior and size syncing.
✨ 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-7762-web-inspector-resize

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 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR reworks attached Web Inspector resizing for side and bottom docks. The main changes are:

  • Shared resize geometry for leading, trailing, and bottom dock positions.
  • Bottom-dock divider hit handling in the portal and inline hosts.
  • Minimum-size clamping for small panes.
  • Drag cleanup, frame-notification silencing, and WebKit attached-size sync on mouse release.
  • Tests for geometry, bottom-dock dragging, and dock-control script behavior.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
Sources/Panels/HostedInspectorResizeGeometry.swift Adds shared dock-side geometry, frame tiling, minimum-size policy, hit rectangles, and drag frame notification silencing.
Sources/BrowserWindowPortal.swift Updates portal-hosted inspector resizing with bottom-dock dragging, axis-aware preferences, drag cleanup, and mouse-up size sync.
Sources/Panels/BrowserPanelView.swift Updates the inline host with matching bottom-dock divider behavior, height preferences, adaptive redock gating, and frontend size sync.
Sources/Panels/HostedInspectorAttachedSizeSync.swift Adds guarded WebKit attached-window width and height synchronization after divider drags.
Sources/App/HostedInspectorDockControlScript.swift Disables the inspector frontend docked resizer so cmux-owned divider handling receives the drag.
cmux.xcodeproj/project.pbxproj Adds the new production and test Swift files to the Xcode project.

Reviews (8): Last reviewed commit: "Portal: capture cross-axis preference wh..." | Re-trigger Greptile

Comment on lines +5 to +6
struct HostedInspectorAttachedSizeSync {
static func sync(frontendWebView: WKWebView?, dockSide: HostedInspectorDockSide, extent: CGFloat) {

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 Static Inspector Sync Namespace

This new production type is only a static helper surface, so the WebKit size-sync behavior has no owning host instance to construct or inject. The repo rule asks production Swift to keep behavior on the scoped owner; here the operation is always called from the inspector-hosting views after a drag, so it should live behind that owner instead of a new ambient namespace.

Rule Used: Flag new ambient global state in production Swift:... (source)

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!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional: the sync is invoked from two independent hosts (window-portal WindowBrowserHostView and local-inline HostContainerView) after their drag loops, and per the repo's shared-behavior policy that must be one shared mutation path, not per-host copies. It is stateless (pure JS construction + fire-and-forget evaluate), takes the frontend webview explicitly rather than reaching into ambient state, and follows the existing pattern of BrowserDeveloperToolsDockControlNormalizer/HostedInspectorDockControlScript. Happy to fold it into a host-owned instance if a future refactor gives the two hosts a common owner.

Comment thread Sources/Panels/BrowserPanelView.swift
…est fixture

- Local-inline host hitTest now skips the native-hit branch for horizontal
  (bottom-dock) dividers, mirroring the portal host, so the inspector's own
  web content cannot swallow the seam band.
- testBrowserPanelHostAllowsRightDockedInspectorToExpandLeftAfterPromotion
  used a 240pt host, which the new proportional-degrade clamp pins to a
  single divider position (120/120); widen the fixture to 420pt so the
  expand-left drag has real range ([120, 300], 148 -> 188).
- Clear the cached hitTest divider candidate on mouseUp in both hosts so it
  cannot outlive the interaction holding view references.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread Sources/Panels/BrowserPanelView.swift
austinywang and others added 2 commits July 9, 2026 17:06
…ttom redock

With .bottom now resolving as a divider candidate, the side-dock width
math in shouldForceHostedInspectorBottomDock computed ~0 effective page
width for bottom-docked layouts, so layout() kept requesting
WI._dockBottom() (0.25s cooldown) and skipped the stored-extent reapply
path. Bottom-dock hits can never need a redock to bottom; bail out.

Reported by Cursor Bugbot on #7771.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three targeted fixes for PR #7771 dogfood findings (resize could still go
under minimum sometimes; drag still glitched intermittently):

- Neutralize the inspector frontend's own docked resizer
  (#docked-resizer gets pointer-events: none via the dock-control
  script). In side dock, cmux's divider band deliberately lets clicks
  through to inspector content, so WebKit's 3px in-content resizer was
  still reachable and resized with WebKit's window-based clamps —
  bypassing cmux's pane minimums entirely, then fighting the reapply
  pass. All attached resizes now go through cmux's clamped divider.
- Sync the attachment size to WebKit on every drag event (deduped on
  integer extent), not just mouseUp. WebKit's inspectedViewFrameDidChange
  relayout re-applied the stale pre-drag size between cmux's frame
  writes, making the divider visibly fight the cursor mid-drag.
- Decide adaptive bottom-dock from container capacity instead of live
  frame widths, so a user drag that leaves the page under 240pt no
  longer yanks the inspector to the bottom right after mouseUp.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread Sources/Panels/BrowserPanelView.swift

@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)
Sources/Panels/BrowserPanelView.swift (1)

6520-6557: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Resync after the final reapply. mouseUp sends the attached size before reapplyHostedInspectorDividerToStoredExtentIfNeeded(reason: "drag.end"), but applyHostedInspectorDividerExtent only updates frames. If the reapply changes the layout, InspectorFrontendHost stays on the pre-reapply extent. Sync from the applied frames, or clear lastSyncedHostedInspectorDragExtent before the final sync so the browser state matches the last layout.

🤖 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 `@Sources/Panels/BrowserPanelView.swift` around lines 6520 - 6557, Update
mouseUp and the final drag-end reapply flow so the attached inspector size is
synchronized after reapplyHostedInspectorDividerToStoredExtentIfNeeded, using
the resulting applied frames rather than only the pre-reapply extent. Ensure the
final sync is not skipped by lastSyncedHostedInspectorDragExtent, either by
deriving the extent from the applied layout or clearing that cache before
syncing; use syncAttachedSizeDuringDrag and the hosted inspector frame/dock-side
state.
🤖 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 `@cmuxTests/HostedInspectorDockControlScriptTests.swift`:
- Around line 129-156: In disablesFrontendDockedResizerSoCmuxOwnsAttachedResize,
simplify the mock DOM setup by declaring the docked resizer element upfront and
making document.getElementById directly return it for "docked-resizer", removing
the initial function assignment, lookup, and reassignment sequence.

---

Outside diff comments:
In `@Sources/Panels/BrowserPanelView.swift`:
- Around line 6520-6557: Update mouseUp and the final drag-end reapply flow so
the attached inspector size is synchronized after
reapplyHostedInspectorDividerToStoredExtentIfNeeded, using the resulting applied
frames rather than only the pre-reapply extent. Ensure the final sync is not
skipped by lastSyncedHostedInspectorDragExtent, either by deriving the extent
from the applied layout or clearing that cache before syncing; use
syncAttachedSizeDuringDrag and the hosted inspector frame/dock-side state.
🪄 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: 111ed9fd-175f-42d7-89ba-295df7c9485f

📥 Commits

Reviewing files that changed from the base of the PR and between c690206 and ac5e54d.

📒 Files selected for processing (4)
  • Sources/App/HostedInspectorDockControlScript.swift
  • Sources/BrowserWindowPortal.swift
  • Sources/Panels/BrowserPanelView.swift
  • cmuxTests/HostedInspectorDockControlScriptTests.swift

Comment thread cmuxTests/HostedInspectorDockControlScriptTests.swift
…ess review

Debug-log evidence from dogfood (browser.portal.manualInspectorDrag)
showed WebKit's WebInspectorUIProxy asynchronously re-applying its stored
attachment size between almost every pair of drag events — oldPageFrame
kept reverting to the exact pre-drag layout, and when its reset landed
last the whole drag ended where it started (the dead-feeling drag).
It observes the inspected webview's frame-change notifications, which our
per-event frame writes were posting.

- HostedInspectorDragFrameNotificationSilencer: turn off the inspected
  view's postsFrameChangedNotifications for exactly the drag's duration
  (both hosts, all drag-exit paths), so cmux is mechanically the only
  layout writer while the mouse is down. mouseUp restores notifications
  and the existing InspectorFrontendHost size sync reconciles WebKit.
- Instrument HostedInspectorAttachedSizeSync completions under DEBUG so
  the debug log records whether setAttachedWindowHeight/Width lands.
- Review: a stored side-dock width no longer blocks adopting the current
  bottom-dock layout as the height preference (capture fallback when the
  stored extent is for the other dock axis; Cursor finding).
- Review: simplify the mock document in the docked-resizer script test
  (CodeRabbit finding).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread Sources/Panels/HostedInspectorResizeGeometry.swift
…nit-safe

Dogfood log analysis of the previous build showed the per-drag-event
InspectorFrontendHost.setAttachedWindow* calls were themselves the
remaining glitch: during fast scrubs the inspector frontend's JS queue
backs up and each queued call echoes back as a WebKit frame apply for a
stale extent (befores of consecutive drag applies matched integer
extents synced hundreds of milliseconds earlier), yanking the divider
backwards. cmux now writes frames alone during the drag — the
frame-notification silencer keeps WebKit's observer passive — and WebKit
gets exactly one setAttachedWindow* reconciliation on mouseUp
(instrumentation confirmed the call lands: result=1 for both axes).

Also address review: the silencer now restores through a deinit-safe
token, so an owner torn down mid-drag can never leave the inspected
view's frame notifications permanently off (Cursor finding).

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

@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)
Sources/Panels/BrowserPanelView.swift (1)

6521-6550: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Synchronize WebKit only after applying the final stored extent.

Line 6533 queues finalExtent, but line 6550 may synchronously change the inspector frame. The queued JavaScript can therefore reapply a stale dimension and recreate the mouse-up snap. Reapply while notifications remain silenced, measure the resulting frame, restore notifications, and then synchronize.

Apply the same ordering through a shared finalization path for BrowserWindowPortal, whose Lines 517-533 repeat this sequence.

Proposed sequencing fix
             isHostedInspectorDividerDragActive = false
-            hostedInspectorDragFrameSilencer.end()
             updateDividerCursor(at: convert(event.locationInWindow, from: nil))
             if let finalDragState {
+                reapplyHostedInspectorDividerToStoredExtentIfNeeded(reason: "drag.end")
                 let finalExtent = finalDragState.dockSide.inspectorExtent(
                     inspectorFrame: finalDragState.inspectorView.frame,
                     in: finalDragState.containerView.bounds
                 )
+                hostedInspectorDragFrameSilencer.end()
                 HostedInspectorAttachedSizeSync.sync(
                     frontendWebView: hostedInspectorFrontendWebView,
                     dockSide: finalDragState.dockSide,
                     extent: finalExtent
                 )
-                reapplyHostedInspectorDividerToStoredExtentIfNeeded(reason: "drag.end")
+            } else {
+                hostedInspectorDragFrameSilencer.end()
             }

As per coding guidelines, “Do not wire the same behavior separately through multiple surfaces; use one shared action path.”

🤖 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 `@Sources/Panels/BrowserPanelView.swift` around lines 6521 - 6550, Synchronize
the frontend only after the final stored extent has been reapplied: in the
hosted inspector mouse-up finalization, move extent measurement and
HostedInspectorAttachedSizeSync.sync after
reapplyHostedInspectorDividerToStoredExtentIfNeeded, while keeping drag-frame
notifications silenced until reapplication completes, then measure the resulting
inspector frame and restore notifications before syncing. Extract this ordering
into a shared finalization helper and use it from both BrowserPanelView’s
mouseUp path and BrowserWindowPortal’s equivalent sequence instead of
maintaining separate implementations.

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.

Inline comments:
In `@Sources/Panels/HostedInspectorResizeGeometry.swift`:
- Around line 47-64: Replace Restore.run’s delayed DispatchQueue.main fallback
with synchronous MainActor-owned teardown. Add explicit MainActor
drag-cancellation/teardown methods in both host views that restore
postsFrameChangedNotifications immediately and clear the active Restore token;
invoke them from all drag-ending and lifecycle paths. Keep Restore.deinit as
assertion-only fallback, remove its `@unchecked` Sendable ownership of NSView
state, and ensure restoration is managed by the single MainActor lifecycle
owner.

---

Outside diff comments:
In `@Sources/Panels/BrowserPanelView.swift`:
- Around line 6521-6550: Synchronize the frontend only after the final stored
extent has been reapplied: in the hosted inspector mouse-up finalization, move
extent measurement and HostedInspectorAttachedSizeSync.sync after
reapplyHostedInspectorDividerToStoredExtentIfNeeded, while keeping drag-frame
notifications silenced until reapplication completes, then measure the resulting
inspector frame and restore notifications before syncing. Extract this ordering
into a shared finalization helper and use it from both BrowserPanelView’s
mouseUp path and BrowserWindowPortal’s equivalent sequence instead of
maintaining separate implementations.
🪄 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: 775f146f-2180-4a7e-8869-657b739be5d8

📥 Commits

Reviewing files that changed from the base of the PR and between 04b13e9 and fe46db8.

📒 Files selected for processing (3)
  • Sources/BrowserWindowPortal.swift
  • Sources/Panels/BrowserPanelView.swift
  • Sources/Panels/HostedInspectorResizeGeometry.swift

Comment thread Sources/Panels/HostedInspectorResizeGeometry.swift
Review follow-up: restore the drag-frame silencer through an explicit
MainActor teardown path — both hosts now cancel an in-flight divider
drag (drag state, cached hit, drag-active flag, silencer) in their
viewDidMoveToWindow nil branch, so the deinit token is a last-resort
fallback rather than the restoration path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread Sources/BrowserWindowPortal.swift Outdated
…ther dock axis

Mirror of the inline host's capture fallback (Cursor finding): the
portal's reapplyHostedInspectorDividerIfNeeded returned early when the
resolved dock side had no stored extent (e.g. bottom dock right after a
side-dock width was stored), so bottom-dock height was never adopted and
later relayout could fight the divider. Adopt the current layout as that
axis's preference instead.

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

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 61aea05. Configure here.

HostedInspectorAttachedSizeSync.sync(
pageWebView: dragState.slotView.hostedInspectorPageWebViewForAttachedSizeSync,
dockSide: dragState.dockSide, extent: finalExtent
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silencer ends before size sync

High Severity

On divider mouseUp, both hosts call hostedInspectorDragFrameSilencer.end() before HostedInspectorAttachedSizeSync.sync. The silencer exists specifically to stop WebKit from reacting to frame notifications with a stale attachment size, but lifting it first re-enables that observer while the sync is still an async evaluateJavaScript round-trip. Any frame write or deferred notification in that window can snap the divider back—the same frame-fighting this change set out to close.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 61aea05. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant