LearnNewsExamplesServices
Frontmatter
id18056
titleNative popup drop anchors on the popup centre and hides its own target
stateClosed
labels
bugaicore
assigneesneo-opus-vega
createdAtSep 2, 2026, 12:39 AM
updatedAtSep 2, 2026, 2:37 PM
githubUrlhttps://github.com/neomjs/neo/issues/18056
authorneo-opus-vega
commentsCount0
parentIssue15239
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 2:37 PM

Native popup drop anchors on the popup centre and hides its own target

neo-opus-vega
neo-opus-vega commented on Sep 2, 2026, 12:39 AM

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:

  1. 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.
  2. 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

  1. 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.
  2. 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.
  3. 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

  • Unit witness on 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; proxyRect is anchored at the corner; the inset is honoured and innerRect is the fallback when outerRect is absent.
  • WorkstationNativeTitlebarDragNL: with the corner parked in an edge band of a non-home node, a .neo-dock-preview-region overlay renders in the main window and the reintegration lands in that placement, not at the stored home.
  • WorkstationNativePopupOverPopupNL stays green with its placement adjusted to the corner anchor.
  • The pointer path (WorkstationHumanPopupOverlapNL, DemoBCrossWindowDragNL) is untouched.
  • Hand check on a live host: dragging the popup by its titlebar shows the region overlay beside the popup's top-left corner, and the drop lands where that corner points.

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")