Skip to content

Add equal-grid snapping for split panes - #2488

Open
brsbl wants to merge 16 commits into
bb/fix-tab-split-actions-thr_q8degf2y66from
bb/split-resize-snap-targets-thr_q8degf2y66
Open

Add equal-grid snapping for split panes#2488
brsbl wants to merge 16 commits into
bb/fix-tab-split-actions-thr_q8degf2y66from
bb/split-resize-snap-targets-thr_q8degf2y66

Conversation

@brsbl

@brsbl brsbl commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Why

bb already makes sibling panes equal when a split is created or removed, because equal-sized grids are the common layout users want. Resizing made it easy to drift into arbitrary proportions but hard to return precisely to that canonical layout. Heavy Diff and Info panes could also leave resize input stuck when a drag lost pointer capture.

User-visible behavior

  • Every split node now owns an equal-sibling grid, across both right-panel tab splits and workspace page/thread splits.
  • The outer divider between a thread workspace and its right panel is also a two-pane grid, so it snaps to an equal 50/50 layout in both normal pages and split workspaces.
  • That outer divider now tracks the pointer immediately: panel-size transitions pause only for the active drag and resume for normal open/close animation.
  • Two siblings snap to halves, three to thirds, four to quarters, and larger sibling groups follow the same rule.
  • Nested row and column split nodes calculate their own equal grid independently, so alignment follows the actual pane hierarchy rather than one global midpoint.
  • Side-by-side panes snap to equal widths; stacked panes snap to equal heights. There is no diagonal snapping.
  • Approaching within 12 px—or crossing a target during a fast drag—captures the equal-grid boundary.
  • A fast crossing still captures when the browser's next pointer sample lands beyond the normal release zone; resistance anchors at that sample so pointer-up cannot silently discard the snap.
  • Once captured, every divider holds through the same 30 px release band; capture remains 12 px everywhere.
  • Leaving the release band arms an exit; a second consecutive out-of-band pointer sample confirms it. Returning to either snap band cancels that pending exit, so coalesced fast motion cannot make the snap disappear before it is perceptible.
  • After a skipped sample, the applicable release band begins at the landing point. Pulling through that resistance preserves arbitrary sizing for users who want it.
  • A temporary guide shows the active boundary inside the owning split node.
  • Existing 15–85% resize bounds are unchanged.

Reliability and performance

  • Grid and divider geometry are captured once when the drag begins; pointer movement performs no layout reads.
  • The shared resolver owns one explicit free → captured → released state machine for center, tab, page, and thread dividers; individual consumers do not tune their own resistance.
  • The outer-divider bridge captures pointer movement before react-resizable-panels, resolves every sample through the shared snap session, and previews the drag through local flex writes to the adjacent panels. It commits once through the panel API on release, matching internal split performance while keyboard resizing, collapse, bounds, drag lifecycle, and width persistence remain library-owned.
  • The inherited 220 ms flex-size transition is temporarily set to zero during pointer dragging and restored on release, cancellation, blur, or unmount.
  • A compatibility mouseup fallback and a zero-button pointer sample both terminate the bridge, so a panel-library-consumed pointerup cannot leave resizing active after release.
  • Release, cancellation, lost pointer capture, a replacement drag, and unmount all remove the guide and resize overlay.
  • Exact divider-seam math accounts for every one-pixel divider when calculating equal pane sizes, then converts the target back into the adjacent pair's persisted fraction without drift.

Non-goals

  • No diagonal snapping.
  • No user-defined grid presets or fixed arbitrary proportions.
  • No snapping between unrelated split nodes.

Screenshot evidence

Fresh before/after evidence is pending. Full browser QA and screenshot capture were intentionally not run, so this layer is not yet screenshot-verified.

Validation

  • 67 final-head focused tests pass across the shared snap contract, single-writer outer bridge, normal panel host, and workspace split layout.
  • Coverage includes local pointer previews with one final panel-library commit, consumed-pointerup release, zero-button release, single-writer pointer ownership, zero-duration drag transitions with restoration, equal boundaries for two-, three-, and four-pane groups, both axes, outer-grid registration, fast crossings, confirmed coarse-sample release, shared 12/30 px hysteresis, guide cleanup, and once-per-drag geometry reads.
  • A matched 240-sample Chrome for Testing trace on the branch app reduced center-divider style-recalculation time from 1.195 s to 0.120 s, script time from 0.848 s to 0.473 s, and total task time from 3.114 s to 1.390 s; the matched center-to-center trace dropped from two frames over 20 ms to zero.
  • A targeted Chrome for Testing fast-crossing trace on the exact branch app held the center divider and guide at 50/50 through consecutive coarse samples at −80 px, +80 px, and +160 px from the grid; the next consecutive out-of-band sample released cleanly.
  • A non-center release persisted the exact 57.8125% panel width and restored it after a hard reload.
  • pnpm exec turbo run typecheck --filter=@bb/app passes.
  • pnpm exec turbo run lint --filter=@bb/app passes with the repository's existing warnings and no errors.
  • git diff --check passes.
  • Full browser QA was not run; validation was limited to the targeted center-divider performance, persistence, and fast-crossing reproductions.

BB-Thread-ID: thr_q8degf2y66

AGENT GENERATED: by GPT-5.6-Sol

@brsbl
brsbl force-pushed the bb/split-resize-snap-targets-thr_q8degf2y66 branch from 0d39493 to 074e03f Compare August 26, 2026 22:26
@brsbl
brsbl force-pushed the bb/split-resize-snap-targets-thr_q8degf2y66 branch from 074e03f to 956030f Compare August 27, 2026 00:17
@brsbl brsbl changed the title Add split resize snap targets Snap split resizing to a midpoint grid Aug 27, 2026
@brsbl

brsbl commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Closing this feature layer because split-resize snapping has been cut from the stack. PRs #2486 and #2487 remain unchanged.

@brsbl brsbl closed this Aug 27, 2026
@brsbl brsbl reopened this Aug 27, 2026
@brsbl brsbl changed the title Snap split resizing to a midpoint grid Add magnetic split grid snapping Aug 27, 2026
@brsbl

brsbl commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Reopened after correcting the interaction model: this now uses one shared midpoint grid per split surface with fast-crossing capture and magnetic release hysteresis.

@brsbl brsbl changed the title Add magnetic split grid snapping Add magnetic midpoint snapping for split panes Aug 27, 2026
@brsbl brsbl changed the title Add magnetic midpoint snapping for split panes Add equal-grid snapping for split panes Aug 27, 2026
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