LearnNewsExamplesServices
Frontmatter
id18088
titleA composition-style dock adopter arms resize but not movement: the crossWindow example is blind to native titlebar drags
stateClosed
labels
bugai
assigneesneo-fable
createdAtSep 2, 2026, 3:22 PM
updatedAtSep 2, 2026, 4:53 PM
githubUrlhttps://github.com/neomjs/neo/issues/18088
authorneo-fable-clio
commentsCount0
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 4:53 PM

A composition-style dock adopter arms resize but not movement: the crossWindow example is blind to native titlebar drags

neo-fable-clio
neo-fable-clio commented on Sep 2, 2026, 3:22 PM

Sub-issue of #13158. Grace's non-blocking finding on PR #18078 (#18077), V-B-A'd here and filed on the operator's standing word that verified product defects get a ticket.

Context

#18077 / PR #18078 made the engine dock host (Neo.dashboard.dock.Workspace) open the window geometry stream itself — observeWindowGeometry(windowId) arms WindowPosition movement AND resize observation for the host's own render target at construction and for each admitted vessel — so an adopter that extends the host inherits it and the Workstation's two hand copies retired. The JSDoc says "so no adopter has to know the addon exists".

The Problem

That sentence is true for adopters that extend the host. examples/dashboard/crossWindow/DemoBWorkspace.mjs:130 is class DemoBWorkspace extends Container — it composes the dock pieces (LayoutAdapter, DropIndicators, MotionSignal, PerspectiveLibrary) onto a plain container and never runs the host's constructor. Its neo-config.json loads WindowPosition deliberately (mainThreadAddons: [..., "WindowPosition"]), and it arms the addon by hand — but only half of it: DemoBWorkspace.mjs:512 and :2988 call setConfigs({observeResize: true, windowId}), never observeMovement. So a resize publishes, a movement does not, unless the pointer-owned poll happens to arm through a document-leaving mouseout — which a vessel born under the pointer (the header-action pop-out) never produces (#18040's finding). The example's popup→main native titlebar drag therefore runs on a manager row that does not follow the moving popup.

Composition-style adopters are exactly the ones who will not notice: the addon sitting in their config looks like the arming. Grace also measured DemoBCrossWindowDragNL failing 3/3 on clean dev headed and traced it to an unrelated example-local barItemCount guard counting header-action chrome — the missing movement stream is not claimed as a second cause of that red (untested), only as a second, independent reason the example is a poor cross-window witness today.

The Architectural Reality

  • src/main/addon/WindowPosition.mjs: observeMovement_ (the config-owned poll, #18040; arming now publishes one sample — #18077) and observeResize_ default false; the pointer path (mouseout leaving the document) arms the poll only for drags that start inside page content.
  • src/dashboard/dock/Workspace.mjs#observeWindowGeometry(windowId): the one place both configs are armed together — an instance method of the host, unreachable for a composition adopter.
  • DefaultConfig.mainThreadAddons does not include WindowPosition; an app loads it on purpose.

The Fix

Two shapes; the ticket does not pick, the claimer does with a falsifier:

  1. Example-local (smallest): DemoBWorkspace.mjs:512 and :2988 arm both — setConfigs({observeMovement: true, observeResize: true, windowId}) — the exact pair the Workstation carried until #18078. Two lines; the example stops being half-blind.
  2. Engine-side (structural): the addon arms itself when loaded — observeMovement / observeResize default true in WindowPosition — on the reading that loading the addon IS the intent to observe geometry (Grace's framing), which makes the pointer-owned mouseout arming a fallback that never has to fire. Cost: a 20ms poll in every app that loads the addon; the apps that do (Workstation, Fleet cockpit, this example, the pop-out fixture) all want the stream. Risk to weigh: the unit contract in WindowPosition.spec.mjs ("pointer-owned polling arms on a document-leaving mouseout and clears on element travel") describes a default-off world and would need restating, and #18040's config-owned semantics would become the default rather than an opt-in.

Whichever lands: a witness that reads the composition example's manager row after a CDP-moved popup (the WindowGeometryRealChromeNL shape) or, cheaper, a unit arm on the example's construction recording both configs.

Acceptance Criteria

  • The crossWindow example publishes movement and resize for its main window and for its vessels (unit: both configs recorded on the stubbed addon at construction and on vessel admission; red on dev — only observeResize is recorded).
  • If shape 2 is chosen: WindowPosition.spec.mjs restated for the default-on world, and the Workstation / Fleet cockpit / host arming keep working (no double arming side effect: setConfigs with the same values is a no-op).
  • The JSDoc of dock.Workspace#observeWindowGeometry states which adopters inherit the arming (extension) and which must call it or the addon themselves (composition) — the sentence Grace found narrower than it reads.

Out of Scope

  • The barItemCount guard behind DemoBCrossWindowDragNL's current red (example-local, separate).
  • The tooling gap Grace named (no mechanical surface notices a host that loads WindowPosition and never arms it) — a lint leaf if anyone wants it.

Related

Parent: #13158. #18077 / PR #18078 (the host-owned arming), #18040 (the config-owned poll), #18068 (frame-origin geometry), #17578 (the coordinator never engages a target zone — the cross-window drag's other open thread).

Live latest-open sweep: checked the latest 20 open issues at 2026-09-02T13:21Z — none on the crossWindow example's arming (#18084, #18082, #18080, #18074 are other dock leaves); A2A: the finding is Grace's review note on PR #18078, no claim; the local ticket corpus on crossWindow + WindowPosition returns the multi-window epics only.

Origin Session ID: 91f83b9c-df95-4f72-a68f-d33f470792ac

Retrieval Hint: "crossWindow DemoBWorkspace composition adopter observeMovement observeResize WindowPosition blind native titlebar drag"

📜 Clio · @neo-fable-clio · Claude Fable 5.1 · Claude Code · session 91f83b9c-df95-4f72-a68f-d33f470792ac

tobiu closed this issue on Sep 2, 2026, 4:53 PM
tobiu referenced in commit 488a5b2 - "fix(examples): the crossWindow host arms movement beside resize for its window and each vessel (#18088) (#18094) on Sep 2, 2026, 4:53 PM