Context
Operator hand test on dev@2da8389d4c (2026-09-01), dragging a popped-out Workstation pane by its OS titlebar over the main window: "the arrow icons show. the background overlays do NOT show … dropping a non-transparent popup over an arrow behind it => close to impossible." Read against source and against the operator's live instance (Neural Link, session 15926019…): the drop is matched on the popup's geometric centre, the one point of an opaque window that is guaranteed to be covered by the window itself. Operator ruling, same session: "geometric center is a FAILURE. this ensures that the real drop item is ALWAYS invisible" — anchor on the top-left corner instead.
Sibling of #18054 (the park refusal, PR #18055), surfaced by the same test. Live latest-open sweep at 2026-09-01T22:38Z (newest #18054) plus keyword sweeps (native drop anchor centre corner, popup centre hit test native, stored home zone native preview region): no equivalent.
The Problem
Two independent choices make the native drop unreadable to a human:
- The anchor.
getNativeWindowDropCandidate hit-tests popupRect.x + width/2, popupRect.y + height/2. The hand is on the titlebar; the anchor is 160×120 px away for the smallest popup and further for a real one, and the region it selects lies under the popup, so the region overlay is never visible. A corner exposes two sides of the selected region (the strips left of and above it stay visible, the popup covers only the below-right quadrant); an edge midpoint exposes one; the centre exposes none.
- The grammar. On the main window the semantic preview offers exactly one zone — the pane's stored-home tabs node (
targetNodeId = storedHome ?? first tabs node, zones: [zone]) — so region overlays (split/edge placements) can only appear over that node, and a drop lands there no matter what the indicator arrows suggested. Meanwhile the indicator tier (dragAffordances.onDragMove(..., writeRenderer: false)) evaluates every zone under the anchor, which is why arrows render everywhere and regions nowhere.
The Architectural Reality
src/manager/DragCoordinator.mjs:590-591 — the anchor: centre of Window.get(data.windowId).innerRect; :635-636 converts it to target-local coordinates; :649-651 builds offsetX/offsetY = width/2, height/2 and a proxyRect centred on the anchor, so the target's proxy embodiment is also centred.
apps/workstation/view/Workspace.mjs:1498-1531 — renderCrossWindowPreview for isMain: one zone from the stored home (or the first tabs node), previewPointer = pointer, producer.produce({pointer, zones: [zone]}), stored-home tab-into fallback when the anchor is outside that zone; :1052-1058 feeds the indicator tier with the same anchor across all zones.
- The pointer path resolves its zone through the affordance layer's hit test over every projected node (
dragAffordances → candidates → producer.produce with the full placement grammar); the native path bypasses that and hardcodes one zone.
manager.Window rows carry both innerRect and outerRect (chrome computed in calculateGeometry), so the frame's top-left corner is available without a new signal.
The Fix
- Anchor (
DragCoordinator.getNativeWindowDropCandidate): hit-test the popup's outer top-left corner inset by nativeWindowDropAnchorInset (new config, default 8 px; falls back to innerRect when outerRect is absent). offsetX/offsetY become the inset, and proxyRect is the popup's rect placed so its corner sits on the anchor, so the target-side proxy embodiment lands where the popup is. Multi-window (popup → popup) resolution is unchanged in shape — only the point moves.
- Grammar (
Workspace.renderCrossWindowPreview, isMain): resolve the semantic zone the way the pointer path does — through the affordance layer's hit test over every projected tabs node — and run the full placement grammar for that zone (split, edge, tab), keeping the stored-home tab-into as the fallback for an anchor inside the window but outside every zone. Region overlays then render for split/edge placements at the anchor, next to the popup's corner, visible.
- Arms:
WorkstationNativeTitlebarDragNL and WorkstationNativePopupOverPopupNL place the popup so its corner, not its centre, lands on the target; the main-window arm gains one assertion that a corner parked in an edge band renders .neo-dock-preview-region.
Contract Ledger
| Target surface |
Source of authority |
Proposed behavior |
Fallback |
Docs |
Evidence |
Neo.manager.DragCoordinator#getNativeWindowDropCandidate anchor |
src/manager/DragCoordinator.mjs:590 |
outer top-left corner + nativeWindowDropAnchorInset |
innerRect corner when outerRect is null |
JSDoc |
unit witness |
Neo.manager.DragCoordinator#nativeWindowDropAnchorInset |
new config |
px inset from the frame corner, default 8 |
— |
JSDoc |
unit witness |
Workstation.view.Workspace#renderCrossWindowPreview (main) |
apps/workstation/view/Workspace.mjs:1485 |
full zone grammar via the affordance hit test; stored-home tab-into as the off-zone fallback |
unchanged fallback |
JSDoc |
e2e arm assertion |
Decision Record impact
aligned-with ADR 0029 §2.8 (native-titlebar reintegration keeps dwell/settle and the claim protocol; only the match point and the target grammar change).
Acceptance Criteria
Out of Scope
- The park refusal under a live OS drag (#18054 / PR #18055).
- Making the popup translucent during a drag: OS window opacity is not controllable from web content.
- Rendering a target hint inside the popup itself (a possible later refinement, not needed once the region is visible).
Avoided Traps
- An edge midpoint (top-centre) as the anchor: it stays under the hand but exposes only one side of the region; a corner exposes two and matches the window-snapping mental model.
- Deriving the pointer position: no pointer event reaches the page during an OS titlebar drag; the window rect is the only signal (operator's doubt confirmed).
- Widening the single stored-home zone by measuring every node in
ensureCrossWindowPreviewGeometry: the affordance layer already hit-tests every zone for the indicator tier; reuse it rather than growing a second measurement path.
Related
Refs #15239 (multi-window choreography epic) · #18054 / PR #18055 (park retry) · #18029 / PR #18040 · #18047 / PR #18051
Origin Session ID: 93fd8de9-7231-4979-b906-c050bcfc25a4
Retrieval Hint: query_raw_memories("native popup drop anchor geometric centre top-left corner region overlay stored home zone grammar")
Context
Operator hand test on
dev@2da8389d4c(2026-09-01), dragging a popped-out Workstation pane by its OS titlebar over the main window: "the arrow icons show. the background overlays do NOT show … dropping a non-transparent popup over an arrow behind it => close to impossible." Read against source and against the operator's live instance (Neural Link, session15926019…): the drop is matched on the popup's geometric centre, the one point of an opaque window that is guaranteed to be covered by the window itself. Operator ruling, same session: "geometric center is a FAILURE. this ensures that the real drop item is ALWAYS invisible" — anchor on the top-left corner instead.Sibling of #18054 (the park refusal, PR #18055), surfaced by the same test. Live latest-open sweep at 2026-09-01T22:38Z (newest #18054) plus keyword sweeps (
native drop anchor centre corner,popup centre hit test native,stored home zone native preview region): no equivalent.The Problem
Two independent choices make the native drop unreadable to a human:
getNativeWindowDropCandidatehit-testspopupRect.x + width/2, popupRect.y + height/2. The hand is on the titlebar; the anchor is 160×120 px away for the smallest popup and further for a real one, and the region it selects lies under the popup, so the region overlay is never visible. A corner exposes two sides of the selected region (the strips left of and above it stay visible, the popup covers only the below-right quadrant); an edge midpoint exposes one; the centre exposes none.targetNodeId = storedHome ?? first tabs node,zones: [zone]) — so region overlays (split/edge placements) can only appear over that node, and a drop lands there no matter what the indicator arrows suggested. Meanwhile the indicator tier (dragAffordances.onDragMove(..., writeRenderer: false)) evaluates every zone under the anchor, which is why arrows render everywhere and regions nowhere.The Architectural Reality
src/manager/DragCoordinator.mjs:590-591— the anchor: centre ofWindow.get(data.windowId).innerRect;:635-636converts it to target-local coordinates;:649-651buildsoffsetX/offsetY = width/2, height/2and aproxyRectcentred on the anchor, so the target's proxy embodiment is also centred.apps/workstation/view/Workspace.mjs:1498-1531—renderCrossWindowPreviewforisMain: onezonefrom the stored home (or the first tabs node),previewPointer = pointer,producer.produce({pointer, zones: [zone]}), stored-home tab-into fallback when the anchor is outside that zone;:1052-1058feeds the indicator tier with the same anchor across all zones.dragAffordances→ candidates →producer.producewith the full placement grammar); the native path bypasses that and hardcodes one zone.manager.Windowrows carry bothinnerRectandouterRect(chrome computed incalculateGeometry), so the frame's top-left corner is available without a new signal.The Fix
DragCoordinator.getNativeWindowDropCandidate): hit-test the popup's outer top-left corner inset bynativeWindowDropAnchorInset(new config, default 8 px; falls back toinnerRectwhenouterRectis absent).offsetX/offsetYbecome the inset, andproxyRectis the popup's rect placed so its corner sits on the anchor, so the target-side proxy embodiment lands where the popup is. Multi-window (popup → popup) resolution is unchanged in shape — only the point moves.Workspace.renderCrossWindowPreview,isMain): resolve the semantic zone the way the pointer path does — through the affordance layer's hit test over every projected tabs node — and run the full placement grammar for that zone (split, edge, tab), keeping the stored-home tab-into as the fallback for an anchor inside the window but outside every zone. Region overlays then render for split/edge placements at the anchor, next to the popup's corner, visible.WorkstationNativeTitlebarDragNLandWorkstationNativePopupOverPopupNLplace the popup so its corner, not its centre, lands on the target; the main-window arm gains one assertion that a corner parked in an edge band renders.neo-dock-preview-region.Contract Ledger
Neo.manager.DragCoordinator#getNativeWindowDropCandidateanchorsrc/manager/DragCoordinator.mjs:590nativeWindowDropAnchorInsetinnerRectcorner whenouterRectis nullNeo.manager.DragCoordinator#nativeWindowDropAnchorInsetWorkstation.view.Workspace#renderCrossWindowPreview(main)apps/workstation/view/Workspace.mjs:1485Decision Record impact
aligned-with ADR 0029 §2.8 (native-titlebar reintegration keeps dwell/settle and the claim protocol; only the match point and the target grammar change).
Acceptance Criteria
DragCoordinator: with a popup whose centre lies over one target and whose top-left corner lies over another, the candidate names the corner's target;proxyRectis anchored at the corner; the inset is honoured andinnerRectis the fallback whenouterRectis absent.WorkstationNativeTitlebarDragNL: with the corner parked in an edge band of a non-home node, a.neo-dock-preview-regionoverlay renders in the main window and the reintegration lands in that placement, not at the stored home.WorkstationNativePopupOverPopupNLstays green with its placement adjusted to the corner anchor.WorkstationHumanPopupOverlapNL,DemoBCrossWindowDragNL) is untouched.Out of Scope
Avoided Traps
ensureCrossWindowPreviewGeometry: the affordance layer already hit-tests every zone for the indicator tier; reuse it rather than growing a second measurement path.Related
Refs #15239 (multi-window choreography epic) · #18054 / PR #18055 (park retry) · #18029 / PR #18040 · #18047 / PR #18051
Origin Session ID: 93fd8de9-7231-4979-b906-c050bcfc25a4
Retrieval Hint:
query_raw_memories("native popup drop anchor geometric centre top-left corner region overlay stored home zone grammar")