Context
Neo.component.Splitter still uses the proxy-and-commit interaction introduced with the original component in 2021: the source splitter fades, a cloned proxy moves, and the chosen sibling receives its new size only after drag:end. The underlying drag and rendering layers have since gained eager zone registration, gesture-wide selection suppression, focused VDOM updates, and buffered heavy widgets.
This leaf introduces an opt-in live-resize contract for the generic component primitive. It does not assume that modern browsers alone make every heavy surface fast; the real Grid/TreeGrid witness decides whether the mode is ready beyond opt-in use.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-27T19:50:45.126Z; no equivalent found. The latest 30 A2A messages contained no overlapping claim. Knowledge Base, Memory Core, live search, and local active/archive/discussion sweeps found the original Splitter tickets and the docking epic, but no proxy-free live-resize leaf.
The Problem
The current implementation has no live-resize path. Neo.draggable.DragZone#dragStart always creates a proxy, and Neo.main.addon.DragDrop#onDragMove moves that proxy in the main thread. Splitter#onDragEnd then performs an asynchronous parent measurement and mutates sibling.style once.
The first implementation exploration proved that a no-proxy DragZone can forward every drag:move to the App Worker, but that is the wrong hot path: it crosses the worker boundary and performs a reactive VDOM update for every pointer frame even though DragDrop already owns main-thread gesture motion.
The old shape also predates newer drag invariants:
- the DragZone is created inside the first
drag:start, after the eager registry needed for exact first-gesture cancellation;
- layout-item sizing belongs on
wrapperStyle, while style can address an inner component root;
- Escape suppresses the later
drag:end, so exact transient-style restoration needs a first-class cancel path;
- the Splitter does not explicitly destroy its DragZone.
The Architectural Reality
src/main/addon/DragDrop.mjs already owns physical pointer-frame work for DragZones.
src/main/draggable/Resize.mjs can keep one cohesive gesture-local resize state machine beneath that addon; it is not a second addon or gesture system.
- Transient DOM geometry belongs to the main thread. Durable layout authority remains the App Worker's
wrapperStyle.
- The main thread must return one frozen
{axis, size, targetId} terminal tuple; reactive Splitter config changes during a gesture must not redirect the commit.
- Merged PR
#16797 established that DragZones must register before the first gesture; delayed DragZone construction would reopen that race.
Neo.manager.VDomUpdate coalesces updates, but avoiding worker traffic entirely is the stronger live-resize hot path.
The Fix
- Add public reactive
Neo.component.Splitter#liveResize, defaulting to false for compatibility.
- Add
Neo.draggable.DragZone#useProxy, defaulting to true, plus an internal main-thread resize descriptor carried by the existing eager zone registration.
- Create the Splitter's DragZone eagerly and refresh direction, boundary, cursor, mode, target wrapper, and bounds before each gesture.
- Let
Neo.main.addon.DragDrop delegate registered live-resize frames to one Neo.main.draggable.Resize controller. Capture transform-immune layout geometry once, update the real outer wrapper synchronously, and send no App-Worker move frames.
- On successful end, return one frozen
{axis, size, targetId} tuple and commit it once through the target component's wrapperStyle. Deferred mode uses the same terminal math while retaining its proxy presentation.
- On Escape, unregister, reset, or destruction, restore the exact inline
flex and axis style captured at start. Destroying the Splitter destroys/unregisters its DragZone.
- Expose the mode in the existing Splitter example and add real-browser gesture coverage.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
New Neo.component.Splitter#liveResize |
This leaf under parent #13158 |
true resizes the selected sibling throughout a real drag while the actual splitter follows Flexbox |
Default false retains proxy movement and one final size commit |
JSDoc + existing Splitter example |
Real-browser horizontal/vertical and previous/next gesture matrix |
New Neo.draggable.DragZone#useProxy |
Existing createDragProxy() ownership plus main-thread DragDrop |
false skips proxy construction without creating a second gesture system |
Default true; existing DragZone consumers are unchanged |
JSDoc |
Unit contract plus rendered proxy-count controls |
| Main-thread transient resize |
DragDrop physical gesture ownership + App-Worker wrapperStyle durability |
Pointer frames mutate only the captured outer DOM wrapper; release emits one frozen terminal tuple |
Missing/stale target fails closed; Escape restores exact inline style |
JSDoc on the resize controller |
Zero App-Worker move messages + rendered held-button geometry |
| Splitter cancel/destruction lifecycle |
Eager registry contract from #16797 and current core.Base#destroy semantics |
First gesture is zone-addressed; Escape restores exact pre-drag sizing; destruction unregisters the zone |
Cancel commits no resize and leaves no proxy/disabled/dragging residue |
JSDoc |
First-gesture Escape and destroy/remount witnesses |
Decision Record impact
none — additive behavior within existing generic Splitter, DragZone, and main-thread DragDrop ownership boundaries.
Acceptance Criteria
Out of Scope
- Changing the default to live resize.
Neo.dashboard.DockSplitter and persisted dock documents; that is a dependent leaf.
- A parallel main-thread addon or gesture system.
- Reopening the historical sibling min/max request from
#4723.
- Keyboard resizing or broader accessibility redesign.
Avoided Traps
- Worker-per-frame resizing: it is functionally viable but pays cross-worker and VDOM costs on every pointer frame.
- Lazy DragZone construction: both modes need first-gesture routing, and delayed construction conflicts with eager zone registration.
- Moving the source splitter with coordinates: the real splitter should move because Flexbox recomputes after sibling sizing.
- Mutating
style durably: wrapped components require the layout-owned wrapperStyle; direct DOM styles are transient main-thread preview only.
- Claiming performance by architecture: the buffered-widget browser witness remains the proof.
Related
Parent: #13158
Related: #16758
Related: #16797
Origin Session ID: 38d9509c-f70b-4ece-bab9-d810e86175ea
Retrieval Hint: query_raw_memories("component Splitter main-thread live resize DragDrop useProxy eager registry wrapperStyle")
Context
Neo.component.Splitterstill uses the proxy-and-commit interaction introduced with the original component in 2021: the source splitter fades, a cloned proxy moves, and the chosen sibling receives its new size only afterdrag:end. The underlying drag and rendering layers have since gained eager zone registration, gesture-wide selection suppression, focused VDOM updates, and buffered heavy widgets.This leaf introduces an opt-in live-resize contract for the generic component primitive. It does not assume that modern browsers alone make every heavy surface fast; the real Grid/TreeGrid witness decides whether the mode is ready beyond opt-in use.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-27T19:50:45.126Z; no equivalent found. The latest 30 A2A messages contained no overlapping claim. Knowledge Base, Memory Core, live search, and local active/archive/discussion sweeps found the original Splitter tickets and the docking epic, but no proxy-free live-resize leaf.
The Problem
The current implementation has no live-resize path.
Neo.draggable.DragZone#dragStartalways creates a proxy, andNeo.main.addon.DragDrop#onDragMovemoves that proxy in the main thread.Splitter#onDragEndthen performs an asynchronous parent measurement and mutatessibling.styleonce.The first implementation exploration proved that a no-proxy DragZone can forward every
drag:moveto the App Worker, but that is the wrong hot path: it crosses the worker boundary and performs a reactive VDOM update for every pointer frame even thoughDragDropalready owns main-thread gesture motion.The old shape also predates newer drag invariants:
drag:start, after the eager registry needed for exact first-gesture cancellation;wrapperStyle, whilestylecan address an inner component root;drag:end, so exact transient-style restoration needs a first-class cancel path;The Architectural Reality
src/main/addon/DragDrop.mjsalready owns physical pointer-frame work for DragZones.src/main/draggable/Resize.mjscan keep one cohesive gesture-local resize state machine beneath that addon; it is not a second addon or gesture system.wrapperStyle.{axis, size, targetId}terminal tuple; reactive Splitter config changes during a gesture must not redirect the commit.#16797established that DragZones must register before the first gesture; delayed DragZone construction would reopen that race.Neo.manager.VDomUpdatecoalesces updates, but avoiding worker traffic entirely is the stronger live-resize hot path.The Fix
Neo.component.Splitter#liveResize, defaulting tofalsefor compatibility.Neo.draggable.DragZone#useProxy, defaulting totrue, plus an internal main-thread resize descriptor carried by the existing eager zone registration.Neo.main.addon.DragDropdelegate registered live-resize frames to oneNeo.main.draggable.Resizecontroller. Capture transform-immune layout geometry once, update the real outer wrapper synchronously, and send no App-Worker move frames.{axis, size, targetId}tuple and commit it once through the target component'swrapperStyle. Deferred mode uses the same terminal math while retaining its proxy presentation.flexand axis style captured at start. Destroying the Splitter destroys/unregisters its DragZone.Contract Ledger
Neo.component.Splitter#liveResizetrueresizes the selected sibling throughout a real drag while the actual splitter follows Flexboxfalseretains proxy movement and one final size commitNeo.draggable.DragZone#useProxycreateDragProxy()ownership plus main-threadDragDropfalseskips proxy construction without creating a second gesture systemtrue; existing DragZone consumers are unchangedDragDropphysical gesture ownership + App-WorkerwrapperStyledurability#16797and currentcore.Base#destroysemanticsDecision Record impact
none — additive behavior within existing generic Splitter, DragZone, and main-thread DragDrop ownership boundaries.
Acceptance Criteria
liveResize: falsepreserves the current proxy lifecycle and commit-on-end behavior.liveResize: truecreates no drag proxy, keeps the real splitter visible, and updates the selected sibling continuously through real pointer movement.wrapperStyleupdate.wrapperStyleboundary, including a component with a distinct wrapper/root.Out of Scope
Neo.dashboard.DockSplitterand persisted dock documents; that is a dependent leaf.#4723.Avoided Traps
styledurably: wrapped components require the layout-ownedwrapperStyle; direct DOM styles are transient main-thread preview only.Related
Parent: #13158
Related: #16758
Related: #16797
Origin Session ID: 38d9509c-f70b-4ece-bab9-d810e86175ea
Retrieval Hint:
query_raw_memories("component Splitter main-thread live resize DragDrop useProxy eager registry wrapperStyle")