LearnNewsExamplesServices
Frontmatter
id18058
titlemanager.Window takes Chrome''s screenLeft/Top for the viewport origin; Chrome reports the frame, so every cross-window rect is off by the window chrome
stateClosed
labels
bugaicore
assigneesneo-fable-clio
createdAtSep 2, 2026, 1:46 AM
updatedAtSep 2, 2026, 1:20 PM
githubUrlhttps://github.com/neomjs/neo/issues/18058
authorneo-opus-vega
commentsCount0
parentIssue15239
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 2, 2026, 1:20 PM

manager.Window takes Chrome's screenLeft/Top for the viewport origin; Chrome reports the frame, so every cross-window rect is off by the window chrome

neo-opus-vega
neo-opus-vega commented on Sep 2, 2026, 1:46 AM

Context

Found on the #18056 lane (native drop anchored on the popup's outer top-left corner) when the popup→popup e2e arm ran with viewport: null, the only configuration in which our popups carry real window chrome. Neo.manager.Window.calculateGeometry labels Chrome's screenLeft/screenTop as the viewport origin (comment: "Chrome/Edge: screenLeft/Top is Viewport position"). Two independent measurements say Chrome reports the frame origin:

  1. CDP probe, headed Chromium (this host, 2026-09-01): Browser.setWindowBounds({top: 120}) → window.screenY === 120, with outerHeight - innerHeight === 87. The frame is at 120; the viewport is at 207.
  2. The operator's live Chrome, read through the Neural Link (Workstation session 15926019…, window 73d7ff8c…): chrome.top: 87, innerRect.y: 33, outerRect.y: -54, innerRect.height: 997, outer height 1084, on a 1728×1117 display. A frame at y = 33 spanning to 1117 is a window filling the screen under the menu bar. A frame at y = −54 is above the screen, which macOS does not allow. So screenTop (33) is the frame top, and the real viewport starts at 120.

Every emulated-viewport Playwright rig has outerHeight === innerHeight (chrome 0), so innerRect === outerRect there and no witness we own can see the mislabel. This ticket separates observation (the two measurements above) from inference (the consumer impact below).

The Problem

With viewportTop = screenTop on Chrome, innerRect is a rect with the frame's origin and the content's size, and outerRect sits chrome.top px above the real frame. Consumers convert between windows by subtracting these rects, so:

  • Pointer cross-window path (DragCoordinator.onDragMove, src/manager/DragCoordinator.mjs :826, :922-923): localY = event.screenY - targetWindow.innerRect.y. MouseEvent.screenY is physical, innerRect.y is the target's frame top, so the target-local point is chrome.top of the target too low — ≈ 87 px into the main window. Inference from code; not yet witnessed on a real Chrome.
  • Native titlebar path (DragCoordinator.getNativeWindowDropCandidate, anchor = outerRect + inset after #18056): the anchor sits chrome.top(popup) - inset above the popup's real frame, and the target-local conversion subtracts the target's frame top, so the error is chrome.top(target) - chrome.top(popup): exact for popup→popup (equal chrome, the errors cancel), ≈ 20 px for popup→main (87 vs 67). Witnessed: the popup→popup arm's park receipt shows the source outerRect.y 67 px above its screenY.
  • Park / moveTo consumers (Workspace.parkTearOutVessel, resolveVesselConversionSourceRect, windowNativeMoveTo targets; src/ai/Client.mjs, src/ai/client/InteractionService.mjs): window.moveTo positions the frame, so code that feeds frame-based "inner" rects to it is accidentally consistent today and would need the matching conversion when the rects become true viewport rects.

History: the model was chosen on #8164 (2025-12-27) — "Chrome/Edge: screenLeft/Top = viewport position; Safari: frame position, so add chrome height to get viewport; Firefox: mozInnerScreenX/Y" — replacing an earlier headerHeight subtraction. The Safari branch is the one that matches what Chrome actually reports. Related coordinate-basis history: #15924 / #15934 (screenLeft basis, closed), #16472 (large-over-small overlap, open, suspects window chrome/geometry).

The Architectural Reality

  • src/manager/Window.mjs calculateGeometry(data) :69-117: viewportLeft/Top branches — Firefox (mozInnerScreenX/Y), Safari (screenLeft + sideBorder, screenTop + topChrome), default/Chrome (screenLeft, screenTop). innerRect = (viewportLeft, viewportTop, innerWidth, innerHeight), outerRect = (viewportLeft - sideBorder, viewportTop - topChrome, outerWidth, outerHeight).
  • Sensor: src/main/addon/WindowPosition.mjs publishes raw screenLeft/screenTop (:86, :144); Neo.Main.getWindowData supplies outerHeight/innerHeight and mozInnerScreenX/Y.
  • Consumers of innerRect/outerRect (tree-wide): src/manager/Window.mjs, src/manager/DragCoordinator.mjs, src/ai/Client.mjs, src/ai/client/InteractionService.mjs, apps/workstation/view/Workspace.mjs.
  • Rigs: every e2e spec without viewport: null emulates the screen at the viewport size and gets chrome 0; WorkstationNativePopupOverPopupNL (viewport: null) is the one arm that carries real chrome today.

The Fix

  1. calculateGeometry: treat Chrome/Edge like Safari — screenLeft/Top is the frame origin; viewport = frame + chrome. Keep the Firefox branch. Correct the JSDoc/comment that states the opposite. Decide the Safari branch by measurement, not by the current comment.
  2. Consumer audit, one row each, each either "unaffected" or converted: the two DragCoordinator conversions; the Workstation park/moveTo geometry; ai/Client and InteractionService window placement.
  3. Witnesses: a unit test on calculateGeometry per branch with the two probe payloads above; a viewport: null e2e receipt that manager.Window.get(popup).outerRect.y equals the CDP frame top and innerRect.y equals frame top + (outerHeight - innerHeight); the #18056 main arm's corner correction step reads a zero residual with no correction move.
  4. Operator hand check on a real Chrome: a cross-window pointer drag into the main window previews the zone under the pointer, and the native corner anchor's region overlay paints beside the popup's physical corner.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback Docs Evidence
Neo.manager.Window#calculateGeometry Chrome branch src/manager/Window.mjs:69 screenLeft/Top = frame origin; innerRect = frame + chrome Firefox branch unchanged JSDoc + inline comment unit witness with the two measured payloads
Neo.manager.Window innerRect / outerRect semantics same innerRect = true viewport on screen; outerRect = true frame — JSDoc viewport: null e2e receipt
Neo.manager.DragCoordinator local conversions src/manager/DragCoordinator.mjs:826, :922 unchanged code, corrected inputs — — pointer arm with real chrome + hand check
Workstation park / moveTo geometry apps/workstation/view/Workspace.mjs (parkTearOutVessel) audited: frame vs viewport made explicit at each moveTo — JSDoc popup→popup arm

Decision Record impact

aligned-with ADR 0029 (docking design: the multi-window model assumes one shared screen coordinate basis; this restores it). Amends the #8164 branch choice with measurements.

Acceptance Criteria

  • Unit witness on calculateGeometry: the CDP payload (screenTop 120, outerHeight - innerHeight 87) yields outerRect.y 120, innerRect.y 207; the operator payload (screenTop 33, chrome 87) yields outerRect.y 33, innerRect.y 120. Firefox branch unchanged.
  • viewport: null e2e receipt: for a popup with outerHeight > innerHeight, manager.Window.get(popup).outerRect.y equals the CDP frame top (±1) and innerRect.y equals frame top + chrome.
  • Consumer audit table in the PR body: each innerRect/outerRect consumer named, marked unaffected or converted, with its witness.
  • WorkstationNativeTitlebarDragNL: the corner-anchor correction step reports a zero residual without a correction move (the aimed DOM point and the coordinator's anchor coincide on real chrome).
  • Post-merge, operator hand check on a real Chrome: pointer cross-window drag into main previews under the pointer; native corner drop paints its region beside the popup's physical corner.

Out of Scope

  • The anchor choice and the zone grammar of the native drop (#18056).
  • Firefox behaviour (mozInnerScreenX/Y is already the viewport).
  • Multi-display / DPR questions beyond the single-basis correction.

Avoided Traps

  • Correcting in the sensor (WindowPosition / Neo.Main): the manager is the one place that interprets raw browser geometry (#8164's own separation); keep the sensor raw.
  • Adding an offset in one consumer (e.g. subtracting chrome.top in DragCoordinator only): that was the pre-#8164 headerHeight patch and it hid the model error; fix the model.
  • Trusting the emulated rigs: they cannot fail on this by construction; the witness must carry real chrome.

Related

#8164 (geometry model origin) · #15924 / #15934 (coordinate-basis family) · #16472 (open, chrome/geometry suspect) · #18056 (corner anchor; the lane that surfaced this) · #15239 (multi-window epic)

Live latest-open sweep: checked the latest 20 open issues at 2026-09-01T23:42Z; no equivalent. A2A claim sweep: no overlapping lane-claim in the last hour.

Origin Session ID: 93fd8de9-7231-4979-b906-c050bcfc25a4

Retrieval Hint: query_raw_memories("Chrome screenTop frame origin viewport manager.Window calculateGeometry chrome height cross-window rect")