LearnNewsExamplesServices
Frontmatter
id14850
titleDock tab-drag: draggable headers + within-container reorder commit
stateClosed
labels
enhancementaitestingarchitecture
assigneesneo-opus-grace
createdAt2:51 PM
updatedAt6:56 PM
githubUrlhttps://github.com/neomjs/neo/issues/14850
authorneo-opus-grace
commentsCount0
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAt6:56 PM

Dock tab-drag: draggable headers + within-container reorder commit

Closed Backlog/active-chunk-4 enhancementaitestingarchitecture
neo-opus-grace
neo-opus-grace commented on 2:51 PM

Context

The dock line shipped a full model layer — transferItem, moveItem, moveNode, splitNode, resizeSplit and friends in DockZoneModel (#14768 and siblings), all with green unit specs. But green unit specs prove the MODEL math; they say nothing about whether a rendered dock does anything on a real gesture. The FIRST whitebox-e2e driven against the rendered example (examples/dashboard/dock/) proved the gap loud and clear: main-tabs order ['strategy','swarm'] UNCHANGED after a native tab-header drag, draggableCount: 2 — only the two splitters were draggable; the tab headers were not draggable at all. Every op was JSON math never wired to a gesture. This is the "I have not seen ONE visual example of the dock engine" finding, made falsifiable.

The Problem

DockLayoutAdapter.projectTabsNode projected each tabs node into a Neo.tab.Container config, but left dragResortable at its default (false). Result: the projected tab headers rendered static, no drag sensor armed, and no gesture ever reached the committed dockZone.v1 document. The "missing middle" — drag-source → drop → applyDockZoneOperation → committed model — was unbuilt for the simplest gesture (reorder a tab within its own zone).

The Architectural Reality

  • Owner tier: src/dashboard/DockLayoutAdapter.mjs#projectTabsNode (the projection SSOT the example re-projects from).
  • Reuse, not rebuild (ADR 0029 "no parallel drag system"): Neo.tab.Container already owns tab-header drag-resort via dragResortable → the existing Neo.draggable.tab.header.toolbar.SortZone, firing a moveTo event {fromIndex, toIndex} on drop (src/tab/Container.mjs:519). No new drag machinery.
  • Commit seam: the container's moveTo listener maps items[fromIndex]context.applyDockZoneOperation({operation:'addTab', itemId, tabsNodeId, index:toIndex}). DockZoneModel's addTab handler self-routes an already-present item to moveItem (DockZoneModel.mjs:84), and addTab detaches-before-insert (:1672), so a within-node reorder never duplicates. The committed document is re-projected through context.onDockZoneDocumentChange — the same commit loop DockSplitter.commitResizeSplit uses. That callback's one-tick timeout(0) deferral (MainContainer.mjs:288) protects the drag-end handler from the workspace rebuild's use-after-destroy.

The Fix

One PR. projectTabsNode gains dragResortable: true + a moveTo listener that commits the reorder through the landed operation seam (applyDockZoneOperation + onDockZoneDocumentChange). The whitebox-e2e test/playwright/e2e/DockDragDropNL.spec.mjs ships in the same PR (per the epic's gesture-proof guardrail): it drives a native page.mouse drag of the "Strategy" header past "Swarm" and asserts App-Worker truth — main-tabs goes ['strategy','swarm']['swarm','strategy'] — plus that the tab headers are actually draggable in the DOM. No framework change: the reference example (examples/tab/container/, dragResortable:true) already proves the primitive; the dock only had to project it.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
DockLayoutAdapter.projectTabsNode tab-container config ADR 0029 §"no parallel drag system"; src/tab/Container.mjs dragResortable (:75) Emits dragResortable:true so the projected tab headers ride the existing header SortZone Railed (auto-hidden) items are pre-filtered from items; fromIndex/toIndex map to the visible set JSDoc on projectTabsNode names the reuse + the cross-zone follow-up e2e DockDragDropNL: draggableCount ≥ 6, model reorders
moveTo listener → context.applyDockZoneOperation Neo.tab.Container#moveTo event {fromIndex,toIndex} (:536); DockZoneModel.addTab handler (:84) Within-node reorder commits via addTabmoveItem self-route; detach-before-insert = no duplication On errors[] the document is left untouched (fail-closed {document,errors}) inline comment in projectTabsNode unit specs 123 green; e2e worker-truth before/after

Decision Record impact

aligned-with ADR 0029 — this leaf is the empirical validation of the "no parallel drag system" mandate: the dock's first interactive gesture rides the existing tab.Container/SortZone pipeline, not a new one.

Acceptance Criteria

  • The dock projects draggable tab headers (dragResortable on projected tabs nodes) — DOM-verifiable in the e2e.
  • A within-container tab drag commits a reorder to the dockZone.v1 document (App-Worker truth), fail-closed on errors[].
  • test/playwright/e2e/DockDragDropNL.spec.mjs drives the native gesture and asserts the before/after model change (ships in this PR).
  • DockLayoutAdapter + DockZoneModel unit specs stay green (no projection-shape regression).
  • Cross-family review.

Out of Scope

  • Cross-window drag / foreign-item transfer (#14769) and its two-window demo (#14772).
  • Grouped drag (#14770), tab overflow affordance (#14771), transition animations (#14779).
  • Any src/tab/ framework change — the primitive already works; this leaf is projection-only.

Related

Parent #13158 · authority ADR 0029 §2.6 / §"no parallel drag system" · foundation the cross-window wiring #14769 builds on · sibling e2e-coverage #14591.

Live latest-open sweep: checked latest 40 open enhancement issues on 2026-07-05; no equivalent within-container tab-drag gesture ticket found (#14769 is cross-window, #14771 overflow, #14772 demo). A2A in-flight sweep: mailbox scanned same session, no competing lane-claim.

Origin Session ID: 9e42a8de-4291-46fc-944e-92ceb0db1748 Retrieval Hint: "dock tab drag dragResortable moveTo commit within-container reorder whitebox e2e"

tobiu referenced in commit ecf9048 - "feat(dashboard): dock tab-drag — draggable headers + within-container reorder commit (#14850) (#14851) on 6:56 PM
tobiu closed this issue on 6:56 PM