Follow-up extracted from the #15252 Take 17 visual review. The popup-over-popup beat completes semantically, but its visual grammar is incomplete: when the dragged vessel converts over the target popup, the real popup parks/disappears and the target's docking zones illuminate, yet no in-app proxy embodies what is still being dragged.
This is not a new design request. #15395 explicitly contracted popup → semi-transparent drag proxy conversion, #15396 contracted park-not-close lifecycle, and #15906 composed the Workstation journey. The missing embodiment is therefore a contract-completion bug.
The Problem
The user sees the dragged popup vanish exactly when the drop zones become visible. The pointer and target preview continue, so the operation works, but the thing being dragged has no visual body. The result is weaker than both the Qt reference behavior (whole-window translucency during drag) and Neo's own infinite-canvas drag grammar.
Qt's native-window alpha is a reference, not an assumed browser capability. A Verify-Before-Assert sweep on macOS Chrome 150.0.7871.187 found:
Chromium's historical Chrome-App alphaEnabled path is not a normal browser-popup API and the current source limits transparency to Aura (USE_AURA), falling back to opaque elsewhere: app_window_api.cc.
This does not claim that no private native hack can ever alter a Chrome window. It proves that the supported web/extension/CDP surfaces available to this app provide no macOS popup-alpha contract. Correctness must therefore come from Neo's in-app proxy. A future supported native-alpha capability may be an optional optimization, never the only embodiment.
The Architectural Reality
Current dev at ae6b87c907260e917e64357d801e814b3d9202f8 carries the intended ownership split but stops one layer early:
src/dashboard/DockVesselConversion.mjs says convert-in is suspendWindowDrag territory: “the popup yields, the proxy embodies over the target”.
apps/workstation/view/Workspace.mjs#parkTearOutVessel() correctly keeps the exact OS vessel alive behind the target.
src/dashboard/CrossWindowDragTarget.mjs#onRemoteDragMove() only calls previewFor(payload) and stores currentPreview; it owns no target-side drag proxy.
DockCrossWindowParticipation forwards the same preview-only surface.
Workspace#readCrossWindowGestureSnapshot() proves claim, preview, park, and source-vessel survival, but never proves proxy presence. The headed journey can therefore pass while the dragged embodiment is absent.
The reusable lineage already exists: src/draggable/dashboard/SortZone.mjs#startRemoteDrag() creates a target-window DragProxyContainer around the live dragged widget. DockTabSortZone#getDragProxyConfig() already carries dock ownership, theme, and preview-language classes onto body-mounted proxies.
The fix should compose or extract that owner rather than create another drag system.
The Fix
One PR: complete the popup-to-proxy conversion by giving the registered dock target an explicit target-window proxy lifecycle.
On admitted convert-in, park the native vessel as today and create the in-app proxy in the winning target window.
The proxy follows the pointer in target-local coordinates, visibly represents the dragged dock item/stack, and remains translucent enough for the existing drop-zone preview beneath it to stay legible.
Reuse/factor the landed DragProxyContainer and dock proxy configuration; do not introduce a second drag coordinator, preview language, or model path.
Convert-out restores the same parked vessel and retires the target proxy. Commit, cancel, target removal, disconnect, and stale-generation paths retire it exactly once.
Extend semantic diagnostics/tests so “converted + parked + previewed” is not considered visually ready without the target proxy.
Acceptance Criteria
During popup-over-popup conversion, exactly one target-window in-app proxy visibly embodies the dragged dock item or stack; the native source vessel remains parked and connected.
The proxy follows the live pointer in target-local coordinates and the existing docking zones remain visible beneath it.
The proxy consumes the landed dock proxy styling/configuration (DragProxyContainer, ownership marker, active theme, preview language) or one factored equivalent; no parallel drag system is introduced.
Convert-out removes the proxy and re-shows the same OS vessel without window.open() re-acquisition; same-instance/store/heartbeat continuity remains green.
Commit, cancel, target unregister/disconnect, and stale-generation paths remove the proxy exactly once with no body-mounted residue.
A focused semantic witness fails on the current preview-only implementation and proves proxy identity/window/visibility across convert-in → move → convert-out/terminal. Pixel-only evidence is not sufficient.
A headed Workstation witness records the visual beat while retaining exactly-one claim, semantic/rendered preview equality, commit-before-close, and existing Scene 3–5 invariants.
No supported-browser alpha capability is required for correctness. If one is later discovered and feature-detected, it remains an optional platform optimization behind the in-app baseline.
Out of Scope
Workstation viewport sizing · the initial about:blank white flash · film recapture/composition · generic Chrome-native alpha R&D · Electron-shell materialization · changes to docking model operations or claim arbitration.
Decision Record impact
depends-on ADR 0029 §§2.3 and 2.8.2–2.8.3; no amendment expected because this completes the already-recorded DETACHED_MOVING ⇄ HOVERING_CLAIM embodiment and park lifecycle. If implementation discovers a new ownership state rather than a target-side rendering owner, stop and revalidate the ADR premise before coding.
Freshness evidence: semantic/local duplicate sweep plus latest 20 open issues and the latest 30 A2A messages checked immediately before filing; no current equivalent or competing claim.
Context
Follow-up extracted from the
#15252Take 17 visual review. The popup-over-popup beat completes semantically, but its visual grammar is incomplete: when the dragged vessel converts over the target popup, the real popup parks/disappears and the target's docking zones illuminate, yet no in-app proxy embodies what is still being dragged.This is not a new design request.
#15395explicitly contracted popup → semi-transparent drag proxy conversion,#15396contracted park-not-close lifecycle, and#15906composed the Workstation journey. The missing embodiment is therefore a contract-completion bug.The Problem
The user sees the dragged popup vanish exactly when the drop zones become visible. The pointer and target preview continue, so the operation works, but the thing being dragged has no visual body. The result is weaker than both the Qt reference behavior (whole-window translucency during drag) and Neo's own infinite-canvas drag grammar.
Qt's native-window alpha is a reference, not an assumed browser capability. A Verify-Before-Assert sweep on macOS Chrome
150.0.7871.187found:window.open()feature surface exposes popup selection and geometry, not top-level window alpha;chrome.windowsexposes type, bounds, state, focus, and attention, not opacity;Browserexposes bounds/state, not opacity;alphaEnabledpath is not a normal browser-popup API and the current source limits transparency to Aura (USE_AURA), falling back to opaque elsewhere:app_window_api.cc.This does not claim that no private native hack can ever alter a Chrome window. It proves that the supported web/extension/CDP surfaces available to this app provide no macOS popup-alpha contract. Correctness must therefore come from Neo's in-app proxy. A future supported native-alpha capability may be an optional optimization, never the only embodiment.
The Architectural Reality
Current
devatae6b87c907260e917e64357d801e814b3d9202f8carries the intended ownership split but stops one layer early:src/dashboard/DockVesselConversion.mjssays convert-in issuspendWindowDragterritory: “the popup yields, the proxy embodies over the target”.apps/workstation/view/Workspace.mjs#parkTearOutVessel()correctly keeps the exact OS vessel alive behind the target.src/dashboard/CrossWindowDragTarget.mjs#onRemoteDragMove()only callspreviewFor(payload)and storescurrentPreview; it owns no target-side drag proxy.DockCrossWindowParticipationforwards the same preview-only surface.Workspace#readCrossWindowGestureSnapshot()proves claim, preview, park, and source-vessel survival, but never proves proxy presence. The headed journey can therefore pass while the dragged embodiment is absent.src/draggable/dashboard/SortZone.mjs#startRemoteDrag()creates a target-windowDragProxyContaineraround the live dragged widget.DockTabSortZone#getDragProxyConfig()already carries dock ownership, theme, and preview-language classes onto body-mounted proxies.The fix should compose or extract that owner rather than create another drag system.
The Fix
One PR: complete the popup-to-proxy conversion by giving the registered dock target an explicit target-window proxy lifecycle.
DragProxyContainerand dock proxy configuration; do not introduce a second drag coordinator, preview language, or model path.Acceptance Criteria
DragProxyContainer, ownership marker, active theme, preview language) or one factored equivalent; no parallel drag system is introduced.window.open()re-acquisition; same-instance/store/heartbeat continuity remains green.Out of Scope
Workstation viewport sizing · the initial
about:blankwhite flash · film recapture/composition · generic Chrome-native alpha R&D · Electron-shell materialization · changes to docking model operations or claim arbitration.Decision Record impact
depends-on ADR 0029 §§2.3 and 2.8.2–2.8.3; no amendment expected because this completes the already-recorded
DETACHED_MOVING ⇄ HOVERING_CLAIMembodiment and park lifecycle. If implementation discovers a new ownership state rather than a target-side rendering owner, stop and revalidate the ADR premise before coding.Related
Parent
#15239· film#15252· contract#15395/ PR#15567· lifecycle#15396· Workstation composition#15906/ PR#16035· generic proxy lineage#7204.Freshness evidence: semantic/local duplicate sweep plus latest 20 open issues and the latest 30 A2A messages checked immediately before filing; no current equivalent or competing claim.
Origin Session ID:
019f9e1e-2ef1-72c3-a04d-6bc67a531a8bRetrieval Hint: "popup-to-proxy target-window DragProxyContainer missing dragged embodiment preview-only conversion"