Skip to content

Keep split-divider drag state off document.body - #2073

Merged
ymichael merged 2 commits into
mainfrom
bb/post-merge-fix-split-divider-app-root-restyles-thr_tre46644vy
Aug 20, 2026
Merged

Keep split-divider drag state off document.body#2073
ymichael merged 2 commits into
mainfrom
bb/post-merge-fix-split-divider-app-root-restyles-thr_tre46644vy

Conversation

@ymichael

@ymichael ymichael commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

What was wrong

SidebarSplitDivider, introduced by #1601, wrote both cursor and user-select to document.body at drag start and cleared them at drag finish. Both properties are inherited, so each write invalidated styles across the full app tree. That repeats the root cause measured in #1992, where inherited body/root mutations cost hundreds of milliseconds per recalculation on a roughly 20k-element long-thread DOM. The divider also had no unmount cleanup for its native pointer listeners or temporary flex values.

What changed

  • Keep the active split-divider cursor in local SidebarSplitContainer state instead of mutating document.body or the document root.
  • Mount the existing IframeDragGuardOverlay after the split content, with col-resize or row-resize for the active divider. This keeps the drag captured across iframe/browser-backed pane content without inserting a guard before a large sibling subtree.
  • Continue relying on the divider's existing pointer-down preventDefault() to prevent text selection, matching Keep sidebar resize off the app-root style path #1992.
  • Cancel the in-flight resize on divider unmount: remove native pointer listeners, release capture when held, clear drag chrome, and restore uncommitted flex values.
  • Remove the now-unused global body cursor helpers, while preserving the panel-library takeover used by main.tsx.

This is an app-only change with no host-daemon protocol or other wire changes. It reuses the existing drag guard and straightforward component state; it does not introduce another split framework or alter the split layout model.

How you verified

The focused regression coverage failed before the production change and passed after it. Before the fix, the new assertions observed cursor: col-resize; user-select: none on body during pointer-down and a stuck data-dragging="true" state after unmount.

  • pnpm exec turbo run test --filter=@bb/app --force -- src/components/secondary-panel/SidebarSplitContainer.test.tsx src/components/layout/AppLayout.sidebar-resize.test.tsx — 2 files, 20 tests passed.
  • pnpm exec turbo run build typecheck lint --filter=@bb/app — build and typecheck passed; lint passed with 0 errors and 156 existing warnings.
  • git diff --check — passed.

Coverage proves that split drag does not change body/document-root inline styles, pointer-down is default-prevented, the guard mounts after split content with both row and column cursors, pointer-up/cancel remove it, and unmount cancels the drag and removes its native listeners.

Relationship to #1992 and #2059

This applies #1992's measured performance fix to the internal right-panel divider path that #1992 did not cover. Draft #2059 fixes the separate mirrored split-tree correctness problem. Its current head (4aca1e08) still contains both body mutations, so it does not supersede this fix. The changes overlap only in SidebarSplitContainer.tsx and its test because #2059 rewrites that component; if #2059 lands first, the rebase should keep its single-tree rendering while retaining this PR's container-local cursor state, final drag guard, and divider cleanup.

AGENT GENERATED: by GPT-5.6-Sol

@bb-slop-cop

bb-slop-cop Bot commented Aug 20, 2026

Copy link
Copy Markdown

🚨 SLOP COP 🚨 · review

I am the SlopCop. I am now reviewing this pull request for security, code quality, performance, architecture, and user behavior.

I will post one final review after the checks finish.

@ymichael
ymichael force-pushed the bb/post-merge-fix-split-divider-app-root-restyles-thr_tre46644vy branch from f3a4649 to eec4f4b Compare August 20, 2026 21:07
Comment thread apps/app/src/components/secondary-panel/SidebarSplitContainer.tsx

@bb-slop-cop bb-slop-cop 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.

🚨 SLOP COP 🚨 · review

Plain-English summary: This change keeps a split-panel drag from changing styles across the full page. It uses a local overlay for cursor feedback. It also clears drag listeners and temporary pane sizes when a divider disappears.

I found one small cleanup issue. The change removes the final use of applyResizeCursor and clearResizeCursor, but resizeCursor.ts still exports them. I left an inline comment with the exact cleanup.

The security review found no security or safety issue. The cursor value uses a closed type. The code checks the pointer ID and clears pointer capture, listeners, the overlay, and temporary styles.

The performance review found no issue. The pointer-move path changes only the adjacent flex styles. The overlay mounts after the large pane trees, so it avoids a full subtree style update.

The architecture review supports reuse of IframeDragGuardOverlay. SplitThreadArea has a similar divider, but it has different timeline and resize rules. A shared divider refactor would make this focused change too large.

Verification:

  • The focused Turbo test passed all 20 tests.
  • git diff --check passed.
  • A real browser test created two right-panel panes and dragged their divider.
  • During the drag, the overlay showed cursor-col-resize, both pane sizes changed, and the body and root styles did not change.
  • After pointer release, the overlay and drag marker disappeared.

I reviewed the requested SHA f3a4649d521b6334bb2658b5489713a800514da5. The branch moved to eec4f4b461655b22049ca64365663e425236350c during the review. Both changed files have identical content at those two SHAs.

@ymichael
ymichael force-pushed the bb/post-merge-fix-split-divider-app-root-restyles-thr_tre46644vy branch from eec4f4b to e3be25d Compare August 20, 2026 21:24
@ymichael
ymichael merged commit bfb8f87 into main Aug 20, 2026
13 checks passed
@ymichael
ymichael deleted the bb/post-merge-fix-split-divider-app-root-restyles-thr_tre46644vy branch August 20, 2026 21:33
ymichael added a commit that referenced this pull request Aug 20, 2026
## What was wrong

The split right panel rendered the same layout twice: a shared
tab-header tree and a separate pane-body tree. The header copy always
laid its children out horizontally, so stacked splits detached each tab
strip from its pane, and the paired resize handlers could mutate the
mirrored tree on the wrong axis. Tab rendering was also split across
parallel `fileTabs` and `tabModels` representations, while root-compose
and plugin fixed tabs used separate adapters, making pane-local
selection and content inconsistent.

## What changed

- Render each pane's tab strip, controls, divider, and body as one leaf
in a single split tree. Vertical and horizontal splits now resize only
their own axis.
- Preserve #2073’s divider performance fix: resize cursor state stays
container-local, the iframe drag guard is the final container child, and
pointer cancel/unmount restores the temporary flex values without
writing inherited styles to `document.body`.
- Preserve #2083’s file-opener routing contract in every unified
renderer: persisted params own path and route identity, while the owner
record supplies native preview presentation state.
- Remove the mirrored header tree and its resize-peer synchronization,
plus unused sidebar pane maximize/swap/close state.
- Route thread tabs, root/new-thread tabs, plugin nav fixed tabs,
Browser, and Terminal through the same split container and pane-local
renderer.
- Replace the parallel `fileTabs`/`tabModels` join with one renderable
tab descriptor that owns its model, chrome, actions, content renderer,
and layout behavior.
- Document that `navPanel.experimental_fixedTabs` can mount once per
active visible split pane; the updated contract remains covered by the
unpublished plugin SDK `0.4.10` already reserved on main.

There are no host-daemon protocol or other wire changes.

## How you verified

- Reproduced the detached header and cross-axis resize behavior with
dev-browser before the fix, then manually verified thread-panel,
root-compose, plugin fixed-tab, Browser, Terminal, horizontal, and
vertical split combinations after it.
- Added regression coverage for pane-local tab rendering, split
orientation, resizing, persisted-layout reconciliation, plugin fixed
tabs, and root-compose content.
- `pnpm exec turbo run typecheck --filter=@bb/app
--filter=@get-bb/plugin-sdk`
- `pnpm exec turbo run lint --filter=@bb/app
--filter=@get-bb/plugin-sdk` (0 errors; existing warnings only)
- `node packages/plugin-sdk/scripts/check-npm-version-guard.mjs`
- Post-rebase divider tests: 2 files, 22 tests passed.
- Post-#2083 focused app tests: 8 files, 159 tests passed.
- Plugin SDK tests: 13 files, 105 tests passed.
- Full app suite: 403 files, 3,097 tests passed, 3 skipped.

### Before and after

#### Thread panel split

| Before | After |
| --- | --- |
| ![Thread panel split
before](https://github.com/user-attachments/assets/2f54cbe0-e910-4bf3-b9e3-3d7a672b1b93)
| ![Thread panel split
after](https://github.com/user-attachments/assets/ed413ef5-65c6-4f73-83ff-210109064c25)
|

#### Root/new-thread split

| Before | After |
| --- | --- |
| ![Root compose split
before](https://github.com/user-attachments/assets/82d18183-97cf-478a-b09f-8d22a5bfc0ba)
| ![Root compose split
after](https://github.com/user-attachments/assets/aa4c74e4-9302-48cf-aa93-3614a91e284e)
|

Fixes the side-panel split regression introduced by
[`4e13e3f`](4e13e3f).

> AGENT GENERATED: by GPT-5.6
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