@realitycollective/iwsdk-uiextensions 0.1.1-preview.0 → 0.1.1-preview.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
8
8
 
9
9
  ### Added
10
10
 
11
+ - `@realitycollective/webxr-uiextensions` - hover per element as one core decision (`core/hover.ts`, `HoverTracker`): each pointer raises `pointerenter` on the element it moves onto and `pointerleave` on the one it left, once per change, and an element's hover style holds while any pointer is over it, as IWSDK's pointer events drive uikit's `:hover`. The three.js binding raises hover from it, and the native binding decides hover from its pointer samples and tells the host through the new `NativeUiHost.setHover`; a `pointerenter` or `pointerleave` a native host raises itself is ignored.
12
+ - `@realitycollective/webxr-uiextensions` - scrolling as a core rule (`core/scroll.ts`, `ScrollState`, `SCROLL_DEFAULTS`): uikit's drag, coast (0.9 damping per frame, stopping below 10 px/s), rubber band (100 px, 30 % return per frame) and wheel, restated so a native host only draws the offset it is handed (`NativeUiHost.setScroll`; a node declares `scroll` and a ray sample carries `localPoint`). Text entry as a core rule (`core/text-entry.ts`, `TextEntry`) over a platform keyboard facility (`TextInputFacility`): a click on a text field asks for the keyboard with the field's value, `multiline` and `type`; every reported value replaces the field's and raises `valueChanged`. The web default is uikit's `Input` over a hidden HTML input and the browser's keyboard; the native default is the system keyboard (`NativeUiHost.showKeyboard`/`hideKeyboard`, host events `input` and `keyboardclosed`). Kit cases: `ui/hover is decided by the binding...`, `ui/the host scrolls an element's content exactly as told`, `ui/the host shows its keyboard for a text field when told...`.
13
+ - `@realitycollective/webxr-uiextensions` - `WindowManager.setFollow(id, options)` and `OpenWindowOptions.follow`: a following window's offset (in the viewer's yaw frame, metres), speed, dead zone and angle live on the `WindowRecord` (`follow`, `DEFAULT_WINDOW_FOLLOW` unless set) and can change after spawn; the `followChanged` event carries the previous values. Every binding steps the core follow rule from the record: the IWSDK dock system writes the live `Follower`, the three.js and native hosts re-aim at the new offset.
14
+ - `@realitycollective/threejs-uiextensions` - a plain three.js / WebXR binding. `UixWindowHost`, `UixPanelDocument`, `configureRendererForUikit` and `fitScale` moved here from `@realitycollective/xrblocks-uiextensions` unchanged (the XR Blocks scene graph is plain three.js already, so its host had no XR-Blocks-specific code in it), plus a new `pointerBridgeFactory` option on `UixWindowHost` so a platform's own interaction source can drive the same host - `@realitycollective/xrblocks-uiextensions`'s `UixWindowHost` is now a thin subclass that supplies `XrBlocksPointerBridge` in place of this package's own bridge, and every other export keeps its old import path through a re-export. New in this package: `ScenePointerBridge` (`pointer-bridge.ts`), which resolves a hit-tested ray, a proximity-tested grab or touch, into the same `pointerdown`/`pointerup`/`click`/`pointerenter`/`pointerleave` events and `TouchPress` samples `XrBlocksPointerBridge` produces from XR Blocks' own callbacks; `connectWebXrPointerInput` (`webxr-input.ts`), which wires that bridge straight to a live WebXR session through `renderer.xr` - `selectstart`/`selectend`/`squeezestart`/`squeezeend` on three's own controller objects for ray and near/grab, and a per-frame `index-finger-tip` joint poll for touch - with no engine SDK beyond three.js itself; and `connectUIExtensions` (`setup.ts`), the package's one setup entry point, mirroring `xrblocks-uiextensions`'s own `connectUIExtensions` for a scene with no XR Blocks Script. `webxrRayInputAccess` (`ray-input.ts`) reads a title-bar ray drag's live ray from three's own `getController(i)` objects, so a drag rides the ray at a fixed grab distance (`beginDrag`/`dragPosition`), the same laser-distance math every other platform uses. Runs the shared `windowHostContractCases`, `sceneTargetContractCases` and `uixElementContractCases`, a core-parity suite (follow equals `stepFollow`, region and hand-menu defaults, title default, control upgrades), and a rule-by-rule suite mirroring `xrblocks-uiextensions/test/pointer-bridge.test.ts` for touch press, controller poke, ray click on release, hover, hold-to-drag with a per-window `dragDelay`, near drag, laser-distance ray drag, billboard while dragging, drop capture and focus bias including a docked window. `CursorVisual` (`cursor-visual.ts`) closes `ui/pointer-cursor` for this binding: a disc drawn at each ray's current hit on a panel, oriented to the hit surface (or the ray's own orientation with no surface normal to read), shown and hidden only by whether the ray currently hits something - never by hover or focus state. XR Blocks is excused from this rule (its SDK draws its own reticle with no seam to observe) but plain three.js owns rendering, so nothing drew one here until now. `ScenePointerBridge.rayCast` reports the world-space hit point and surface normal a cursor needs, alongside the existing `rayHit` (now a thin projection of it); `connectWebXrPointerInput` draws one `CursorVisual` per controller slot from the same per-frame ray cast hover already runs, given a `scene` (`connectUIExtensions` passes its own automatically).
15
+ - `@realitycollective/webxr-uiextensions` - `core/cursor.ts`'s `cursorPlacement` and `DEFAULT_CURSOR_OFFSET` (0.004 m): the one placement rule `ui/pointer-cursor` states as pure logic - a cursor disc's hit point nudged a fixed distance off the surface along its own facing, so it never z-fights that surface, restated from IWSDK's `@iwsdk/xr-input` `CursorVisual.updateFromIntersection`'s `zOffset`. The orientation itself reuses the core's existing `faceViewer`/`rotate` primitives rather than adding a new one. `@realitycollective/threejs-uiextensions`'s `CursorVisual` is the first binding to run it.
16
+ - `stepFollow()`, the follow rule every following window and region uses, as pure logic in the core: IWSDK's `FollowSystem` in its `PivotY` behaviour restated step for step, with `enterFollow()`, `followOffsetFromPose()`, `resolveFollow()`, the yaw helpers (`yawOf`, `yawQuaternion`, `rotateByYaw`, `yawToward`, `strictFollowTarget`) and the defaults `DEFAULT_WINDOW_FOLLOW` (offset `[0, -0.15, -1.2]`, speed 3, tolerance 0.35 m, 30 degrees) and `DEFAULT_REGION_FOLLOW` (IWSDK's `Follower` defaults: offset `[0, -0.2, -1.4]`, speed 1, tolerance 0.4 m). A new IWSDK test drives IWSDK's own `FollowSystem` headlessly beside `stepFollow` over the same head motion and requires the same position and facing every frame, so the core rule cannot drift from the reference.
17
+ - Placement rules as pure logic: `focusBiasAmount()` and `applyFocusBias()` (the focused window is drawn 0.02 m nearer per window behind it), `regionSlotPose()`, and the window defaults `DEFAULT_FOCUS_BIAS` (0.02 m), `DEFAULT_DRAG_DELAY` (0.3 s) and `DEFAULT_BILLBOARD_WHILE_DRAGGING` (true). `RayTuple` is re-exported with the other geometry names.
18
+ - `@realitycollective/native-uiextensions` - `nativeUiHostConformanceCases()`, the host conformance kit: ten runner-free host cases, named `ui/<master row>`, that a native app runs on its device against its real `ui` slice. They check that the host draws a window at the pose it is handed, follows the core rule in `body-follow` and `head-locked`, hides a hand menu with no tracked hand, applies the title, chrome and labels, hides and restores, applies focus bias, and measures a fingertip's signed distance positive in front. The host provides the test readbacks of the new `NativeUiTestHost` type (`windowPose`, `windowHidden`, `elementProperties`, `measureTouch`) in a test build. Against the reference fake every case passes, and the kit is shown to fail a host that misplaces a window or drops the sign of a distance.
11
19
  - `uixElementContractCases()`, a shared conformance suite for `UixElement`, the interface the core's controls drive panel elements through, with `CONTRACT_PANEL_MARKUP` and the `UixElementContractSubject`, `UixElementContractDriver` and `UixElementContractCase` types. Every platform builds the same small panel from the markup its own way, and six cases check it: children follow the markup in order, `tagOf` reads a `uix-*` tag and nothing for a built-in one, `data-uix-*` attributes arrive camelCased in `userData`, `setProperties` reaches what the platform shows, one event reaches every listener on its element and no other, and a core stepper upgraded from the markup follows clicks to its maximum. It runs against real uikit elements on IWSDK and on XR Blocks and three.js, built headlessly the way each platform builds them, and against `NativeWindowHost` over the fake native app. It found the native defect under Fixed.
12
20
  - `dataAttributeKey()`, the one rule for where a `data-*` attribute lands in `userData`: `data-uix-min` becomes `uixMin`, and an already camelCased `dataUixMin` gives the same key. The IWSDK component set and the native proxy elements both use it; the IWSDK copy was private.
13
21
  - `sceneTargetContractCases()`, a shared conformance suite for `SceneTarget`, shipped as data beside `windowHostContractCases()`, with `CONTRACT_SCENE` and the `SceneTargetContractSetup` and `SceneTargetContractCase` types. A client builds its UI with the core's `applyScene()` and never asks which platform draws it, so every host must turn the same descriptor into the same windows. The suite checks that through the host's `WindowManager`: every descriptor window is opened under its id and title, a window named into a region is in that region, a window keeps its dock mode and chrome, and a window given both a region and a dock mode is world-locked in that region. The IWSDK, XR Blocks and native hosts all pass it.
@@ -15,20 +23,40 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
15
23
  - `dispose()` on `WindowHost`, implemented by every platform host: `IwsdkSceneHost`, `UixWindowHost` (XR Blocks and three.js) and `NativeWindowHost`. It means the same thing everywhere: close every window the host opened through its `WindowManager`, so each goes down the normal close path, then release the host's own subscriptions and listeners, so `onPanelReady` replays nothing afterwards. It is safe to call twice. The XR Blocks host also removes the region groups it added to the scene. The IWSDK host silences its readiness system, which stays registered on the world, and forgets itself, so a later `createSceneHost(world)` builds a fresh host. No host could be torn down before, so an app that rebuilt its UI leaked listeners and scene objects on every platform. `windowHostContractCases()` gains a seventh case that holds every host to it. This adds a required member to the `WindowHost` interface, so a host written outside this repository must implement it.
16
24
  - `@realitycollective/native-uiextensions` - the native app adapter, for an OpenXR or visionOS app that embeds a JavaScript engine such as Hermes, renders panels itself and installs `globalThis.__rcHost`, per the Reality Collective native host contract shared across the WebXR family repositories. `NativeWindowHost` implements the core `WindowHost` and `SceneTarget`, as the IWSDK and XR Blocks hosts do, so `applyScene` builds the same scene on native; regions go to the host as `createRegion` and are removed on `dispose`. Bare panels and windows build a tree of internal proxy `UixElement`s over the host's `NativeElementNode` tree, so the existing `data-uix` control upgraders work unchanged; `setProperties` forwards to `host.setProperties`, and `addEventListener` is routed from the host's single `onElementEvent` callback by `(panelId, elementHandle)`. Every `WindowManager` state change (open, focus, minimize, hide, dock, chrome, hand menu) mirrors to the host as `applyWindow(record)`; closing a window disposes its panel (once attached) and calls `closeWindow`. No engine dependency - the package reads and writes only plain data across the host boundary. Ships with an in-memory fake of the `ui` slice under `test/helpers` and runs the shared `windowHostContractCases()` against it.
17
25
  - `demos/webxr-multiplatform` - `?uix-engine=<engine>&uix-autostart=1` boots that pipeline at once instead of showing the launch screen. It exists for the post-deploy smoke test, which now loads the desktop and IWSDK pipelines of the deployed lab as well as its launch screen; before this a pipeline that failed to start was invisible to CI, because nothing pressed START. The XR Blocks pipeline is not smoked: xrblocks renders into `<body>` rather than the mount point and logs a `console.error` about the three.js revision it wants, both of which the smoke test counts as failures.
26
+ - `dragDelay` on `WindowOptionsBase`: seconds a title-bar ray press is held before it becomes a drag, per window, default `DEFAULT_DRAG_DELAY` (0.3 s). Every platform already had the constant; only IWSDK's `UIWindow` component read it per window, and even there `createUIWindow` never passed the option through, so it was unreachable everywhere. Native and XR Blocks now take it too.
27
+ - `@realitycollective/webxr-uiextensions` - two core modules extracted from `NativeWindowHost`, the only binding that had this orchestration before: `pointer-events.ts` (`EdgePress`, the press/release/click state machine for a boolean-active ray or grab pointer, and `dispatchTouchUpdate`, which turns a `TouchPress` update into the same three events) and `titlebar-drag.ts` (`TitlebarDragController`, hold-to-drag timing, drag math, billboard-while-dragging and drop capture as one class a binding drives per window). `NativeWindowHost` now runs both with no behaviour change; `@realitycollective/xrblocks-uiextensions` runs `TitlebarDragController` too (see below). `EdgePress` is not needed by XR Blocks: its own select/grab callbacks are already edge-triggered, one start/end pair per gesture, so there is no boolean to edge-detect there.
28
+ - `@realitycollective/xrblocks-uiextensions` - `pointer-bridge.ts`'s `XrBlocksPointerBridge`, attached automatically to every panel `UixWindowHost` creates. It runs the IWSDK pointer rules: a poke (touch) and a controller tip both press and release through the core `TouchPress`, from `onObjectTouching`/`onObjectTouchEnd`; a ray clicks on `onSelectEnd` (release), never on `onSelectStart` (intersection); hover forwards `onHoverEnter`/`onHoverExit` to `pointerenter`/`pointerleave`; a title-bar press runs the core `TitlebarDragController` - a ray waits out `dragDelay`, a grab (near drag, gated to the title bar) starts at once, both billboard while dragging by default (`billboardWhileDragging`), and a drop within a region's snap radius docks the window into it. The demo pipeline's `onSelectStart` override that raycast and clicked on intersection is gone; `pointer-forward.ts` stays for a caller driving its own raycaster outside the host.
29
+ - `@realitycollective/xrblocks-uiextensions` - `ray-input.ts`'s `rayOf`/`XrBlocksRayInputAccess`, read from a new `rayInput` option (`input` on `connectUIExtensions`, mirroring `input: xb.input` on `@realitycollective/xrblocks-interactions`' own provider). A title-bar RAY drag now rides the controller's live ray at a fixed grab distance - the core `beginDrag`/`dragPosition` laser math IWSDK and native use, via a new core helper, `intersectRayPlane` (`drag-math.ts`), which seeds the drag session from where the ray meets the title bar's own plane, since XR Blocks' `SelectEvent` carries no hit point of its own. With no `rayInput` wired (or no ray source for that controller yet), a ray drag falls back to the controller's own position delta - correct, but not laser-distance - the same fallback this host used before `rayInput` existed. A grab (near drag) is unaffected: it already rode the hand's own position, which is exactly how native's grab pointer works too.
30
+ - `@realitycollective/xrblocks-uiextensions` - focus bias (`applyFocusBias`): the focused window is now drawn 0.02 m nearer the viewer per window behind it, every frame, as IWSDK and native already do - including a DOCKED window. `layoutRegion` now uses the core `regionSlotPose` (rotating the slot offset by the region's own orientation, which the previous hand-written version did not) and runs every frame, not only on a dock change, so a docked window's pre-bias position is always fresh before bias nudges it - `update()`'s three passes (drag, then regions, then placement and bias) now match `NativeWindowHost`'s order.
18
31
 
19
32
  ### Changed
20
33
 
34
+ - The reference is now IWSDK 1.0. `@realitycollective/iwsdk-uiextensions` peers on `@iwsdk/core >=1.0.0 <2.0.0` (was `>=0.5.0 <0.6.0`) and is built and tested against 1.0.0. IWSDK 1.0's FollowSystem honours the height of the follow offset, where 0.5 set a following window at head height, so the core `stepFollow` now does the same on every platform: a body-follow or head-locked window sits 0.15 m below eye level by default, and a following region 0.2 m below. `follow-reference.test.ts` proves the core equal to IWSDK 1.0's own FollowSystem frame by frame.
35
+ - `@realitycollective/xrblocks-uiextensions` - follows by the core rule. `body-follow` and `head-locked` windows now move by `stepFollow`, the same lazy yaw follow IWSDK applies: a snap on entering the mode, the 0.35 m dead zone with a 30 degree angle limit, a lerp of `dt * speed`, and following from where the window was left when it is unpinned. Before this XR Blocks eased with its own exponential approach, had no angle limit or snap, and did not move a `head-locked` window at all. A following region now uses IWSDK's region defaults (speed 1, tolerance 0.4 m) rather than speed 3 and 0.2 m. A window with no title is titled with its id, as on every platform, and every panel's `data-uix` controls are upgraded automatically, as IWSDK's `UIControlsSystem` does (`controls: false` opts out). `follow-math.ts` stays exported and unchanged for existing callers.
36
+ - **Breaking for native hosts:** `@realitycollective/native-uiextensions` now applies every UI rule itself, and a native host is handed results. Before this, the binding mirrored `WindowManager` records to the host and left the host to place windows, write chrome, run the touch press and decide clicks, and a native host that re-derived those rules drifted from the web on chrome, dock modes, touch press, click timing and more. Now the binding runs, from the core, what IWSDK's `UIWindowSystem`, `UIDockSystem`, `FollowSystem`, `UIDockRegionSystem`, `UIDragSystem`, `UITouchGuardSystem` and `UIControlsSystem` do. What a native host must absorb:
37
+ - Implement `setWindowPose(windowId, pose, depthOrder)` and draw and hit-test each window exactly there. Never place a window yourself; the pose already carries the follow rule, hand-menu anchor, region slot, drag and focus bias.
38
+ - Implement `onPointerSample(cb)` and report what each pointer measures: `ray` (every frame, with `ray` and `active` for select), `touch` (while near a panel, with `signedDistance` along the panel normal, positive in front) and `grab` (every frame, with `point` and `active` for squeeze or pinch). Stop raising `pointerdown`, `pointerup` and `click` yourself; the binding raises them, and a click now fires on RELEASE for rays too, as on IWSDK. `onElementEvent` stays for hover and value changes.
39
+ - `applyWindow(record)` carries only two fields a host applies: `hidden`, which now means "not shown" (hidden, or a hand menu whose palm gate is closed), and `dockMode`. Title, chrome button `display`, the PIN and MIN labels and the minimized content arrive through `setProperties`.
40
+ - `createRegion` and `removeRegion` are gone from the `ui` slice: regions are the binding's.
41
+ - Give the binding the `input` slice (`getHeadPose()`, and `sample()` for grip poses) and frames (`update(dt)`, or `attachToHost: true` over `__rcHost.onFrame`), as `createNativeInteractions` takes them.
42
+ - `@realitycollective/native-uiextensions` - a window's `WindowManager` record opens when its panel attaches, as IWSDK's `UIWindowSystem` opens it on adoption, not synchronously in `createWindow`. `manager.has(id)` is false until `onReady` fires. `NativeWindowHostOptions` gains the `registerUIExtensions` switches (`drag`, `nearDrag`, `regions`, `controls`, all on) and `touchPress` thresholds, and `CreateWindowOptions` gains `billboardWhileDragging`.
43
+ - `head-locked` is defined: inside an immersive session it follows exactly as `body-follow` does, as IWSDK does because its `ScreenSpaceUISystem` hands the panel back to world space while presenting. Only outside a session is it pinned to the screen. The core's dock-state comments said "rigidly attached to the view", which no platform does in a session.
44
+ - `@realitycollective/iwsdk-uiextensions` - the `UIWindow` and `UIDockRegion` component defaults and the `createUIWindow` and `createDockRegion` factory defaults now read the core constants (`DEFAULT_REGION`, `DEFAULT_WINDOW_FOLLOW`, `DEFAULT_REGION_FOLLOW`, `DEFAULT_FOCUS_BIAS`, `DEFAULT_DRAG_DELAY`, `DEFAULT_BILLBOARD_WHILE_DRAGGING`), so there is one set of defaults, in the core, for every platform. No value changed: the region defaults stay `column`, pitch 0.35 m, 2 columns, capacity 0, snap radius 0.5 m, as published in 0.1.1-preview.0.
21
45
  - `tagOf()` reads nothing for a built-in element on every platform, as its doc always said. The XR Blocks and three.js parser records a built-in tag such as `span` the same way as a custom one, so `tagOf` read `"span"` there and nothing on IWSDK. A custom element name always holds a hyphen, so `tagOf` now returns a name only when it has one.
22
46
  - `WindowManager` holds one rule for regions: a window in a region is always world-locked, because the region places it. Opening a window into a region, or `dockTo`, makes it world-locked whatever dock mode it asked for; `dockTo` emits `regionChanged` and then, if the mode changed, `dockChanged`. Setting a follow mode with `setDockMode` or `togglePin` takes a docked window out of its region first. The hosts disagreed before this: the XR Blocks host forced a docked window to world-locked on its own, while the IWSDK and native hosts left the manager's record in the requested follow mode. The same scene descriptor therefore laid out differently by platform. The XR Blocks host's own copy of the rule is gone. The native host now sends the native app the manager's dock mode for a new window, not the one the options asked for.
23
47
  - `@realitycollective/iwsdk-uiextensions` - `TouchPointerLike` (the parameter type of `sampleOf`) is exported, and `UITouchGuardSystem` uses the core's `Hand` type rather than a private duplicate. `@realitycollective/xrblocks-uiextensions` exports `UikitComponentLike` (`UixPanelDocument.rootComponent`) and `InteractiveLike` (the return type of `pickInteractive`); `@realitycollective/uix-devtools` exports `CompileOptions` (the options of `compilePanelSource`). Each was a module-private type that appeared in an exported signature, so a consumer had nothing to name and TypeDoc reported it as referenced but undocumented.
48
+ - **Breaking:** `HandPoseSource.hasHands()` is gone. It existed only to let `@realitycollective/xrblocks-uiextensions` keep a hand-locked window shown, by body-follow, when no hand was tracked - a behaviour IWSDK and native never had. A hand-locked window with no tracked hand is now hidden on every platform, as `evaluateHandMenu` already decides: a platform never decides a behaviour the reference has not. `webxrHandPoseSource` no longer implements the method.
24
49
 
25
50
  ### Fixed
26
51
 
52
+ - `@realitycollective/native-uiextensions` - a window spawned without a title showed a blank title bar until a later record change. The plain options the host received defaulted the title to `''` while the record defaulted it to the window id. Both now use the id, and the binding writes it to `uix-title` itself.
53
+ - `@realitycollective/native-uiextensions` - `data-uix` controls were never upgraded on native: nothing called `upgradePanel`, so steppers, toggles, expandable labels and log views rendered but did nothing. Every panel, window or bare, is now upgraded when it is built.
27
54
  - `@realitycollective/native-uiextensions` - the `data-uix-*` attributes of a native panel never reached the controls. `NativeElementNode` had no field for them, so on native every control ran on its defaults: a stepper lost its range, step and starting value, a toggle its labels and colours, and every control its `data-uix-id`, so `controls.stepper("count")` found nothing. `NativeElementNode` gains `attributes`, the element's `data-*` attributes as the markup wrote them, and the proxy lifts them into `userData` with `dataAttributeKey()`. The native app must send them.
28
55
 
29
56
  - `demos/webxr-multiplatform` - the IWSDK pipeline failed to start on the deployed lab, for every visitor, with `Failed to resolve module specifier "three-mesh-bvh"`. The Vite config externalised `three-mesh-bvh` alongside the optional integrations xrblocks imports lazily, but `@iwsdk/core` imports it for real, so the IWSDK chunk shipped a bare import the browser cannot resolve. It resolves in the workspace through `@iwsdk/core`'s own dependency and is now bundled like `three-pathfinding`. Found while photographing the live lab for the Reality Collective site.
30
57
  - `@realitycollective/iwsdk-uiextensions` - the README and `Examples/controls` still showed the `data-uix="stepper"` / `data-uix-role` attribute form that the core stopped upgrading when controls became custom elements, so the shipped example silently produced no controls. Both now use `<uix-stepper>`, `<uix-toggle>`, `<uix-expandable-label>` and `<uix-log-view>` with their part elements, and both register `uixComponentSet` when creating the world, which IWSDK 0.5 needs before it will parse a panel that uses a control. The README says why.
31
58
  - `docs/developer-cycle.md` - the publishing section described GitHub Packages ("no npmjs.com for now"); the packages publish to npmjs.com under the `@realitycollective` scope through `publish-npm.yml`, and the section now describes that workflow, its two dist-tags and the consumer install commands.
59
+ - `@realitycollective/iwsdk-uiextensions` - `createUIWindow` never read `options.dragDelay`: the `UIWindow` component always opened with `DEFAULT_DRAG_DELAY` regardless of what a caller asked for, even though the component field and `UIDragSystem` already honoured a per-window value. It is now read through, alongside the new `WindowOptionsBase.dragDelay`.
32
60
 
33
61
  ## [0.1.0] - 2026-09-17
34
62
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ Everything is authored in plain UIKitML (HTML/CSS-like) - no new markup language
21
21
 
22
22
  ```bash
23
23
  npm install @realitycollective/iwsdk-uiextensions
24
- # peers: @iwsdk/core >=0.5.0 <0.6.0 and three >=0.170.0 (every IWSDK app already has both)
24
+ # peers: @iwsdk/core >=1.0.0 <2.0.0 and three >=0.170.0 (every IWSDK app already has both)
25
25
  ```
26
26
 
27
27
  ## Quick start
@@ -235,7 +235,7 @@ export declare const UIDockRegion: import("elics").Component<{
235
235
  readonly Column: "column";
236
236
  readonly Grid: "grid";
237
237
  };
238
- default: "column";
238
+ default: import("@realitycollective/webxr-uiextensions").RegionFlow;
239
239
  };
240
240
  /** Distance between slot origins (meters). */
241
241
  pitch: {
@@ -8,7 +8,7 @@
8
8
  * components to realise dock modes rather than re-implementing them.
9
9
  */
10
10
  import { Types, createComponent } from '@iwsdk/core';
11
- import { DEFAULT_HAND_MENU, DockMode } from '@realitycollective/webxr-uiextensions';
11
+ import { DEFAULT_BILLBOARD_WHILE_DRAGGING, DEFAULT_DRAG_DELAY, DEFAULT_FOCUS_BIAS, DEFAULT_HAND_MENU, DEFAULT_REGION, DEFAULT_WINDOW_FOLLOW, DockMode, } from '@realitycollective/webxr-uiextensions';
12
12
  /** `HandMenuOptions.hand` as an enum object for the component schema. */
13
13
  export const HandChoice = { Left: 'left', Right: 'right', Either: 'either' };
14
14
  /** `HandMenuOptions.anchor` as an enum object for the component schema. */
@@ -50,20 +50,20 @@ export const UIWindow = createComponent('UIWindow', {
50
50
  /** The DOCK affordance, which returns the window to where it spawned. */
51
51
  dockable: { type: Types.Boolean, default: false },
52
52
  /** Keep the window yawed toward the viewer while it is being dragged. */
53
- billboardWhileDragging: { type: Types.Boolean, default: true },
53
+ billboardWhileDragging: { type: Types.Boolean, default: DEFAULT_BILLBOARD_WHILE_DRAGGING },
54
54
  /**
55
55
  * Seconds a title-bar press must be held before it becomes a drag.
56
56
  * Shorter presses stay clicks, so title-bar buttons don't fight the drag.
57
57
  */
58
- dragDelay: { type: Types.Float32, default: 0.3 },
58
+ dragDelay: { type: Types.Float32, default: DEFAULT_DRAG_DELAY },
59
59
  /** Head-relative offset used in `body-follow` mode (meters). */
60
- followOffset: { type: Types.Vec3, default: [0, -0.15, -1.2] },
60
+ followOffset: { type: Types.Vec3, default: [...DEFAULT_WINDOW_FOLLOW.offset] },
61
61
  /** `Follower` lerp speed for body-follow. */
62
- followSpeed: { type: Types.Float32, default: 3 },
62
+ followSpeed: { type: Types.Float32, default: DEFAULT_WINDOW_FOLLOW.speed },
63
63
  /** `Follower` positional deadzone for body-follow (meters). */
64
- followTolerance: { type: Types.Float32, default: 0.35 },
64
+ followTolerance: { type: Types.Float32, default: DEFAULT_WINDOW_FOLLOW.tolerance },
65
65
  /** Meters the focused window is nudged toward the viewer per focus depth. */
66
- focusBias: { type: Types.Float32, default: 0.02 },
66
+ focusBias: { type: Types.Float32, default: DEFAULT_FOCUS_BIAS },
67
67
  /**
68
68
  * Hand-menu placement, used while `dockMode` is `hand-locked`. These seed
69
69
  * the record's `handMenu` on adoption; after that
@@ -115,15 +115,15 @@ export const UIWindowState = createComponent('UIWindowState', {
115
115
  */
116
116
  export const UIDockRegion = createComponent('UIDockRegion', {
117
117
  regionId: { type: Types.String, default: '' },
118
- flow: { type: Types.Enum, enum: RegionFlowType, default: RegionFlowType.Column },
118
+ flow: { type: Types.Enum, enum: RegionFlowType, default: DEFAULT_REGION.flow },
119
119
  /** Distance between slot origins (meters). */
120
- pitch: { type: Types.Float32, default: 0.35 },
120
+ pitch: { type: Types.Float32, default: DEFAULT_REGION.pitch },
121
121
  /** Columns per row (grid flow only). */
122
- columns: { type: Types.Float32, default: 2 },
122
+ columns: { type: Types.Float32, default: DEFAULT_REGION.columns },
123
123
  /** Max docked windows (0 = unlimited). */
124
- capacity: { type: Types.Float32, default: 0 },
124
+ capacity: { type: Types.Float32, default: DEFAULT_REGION.capacity },
125
125
  /** Drop-capture radius (meters). */
126
- snapRadius: { type: Types.Float32, default: 0.5 },
126
+ snapRadius: { type: Types.Float32, default: DEFAULT_REGION.snapRadius },
127
127
  }, 'UI Extensions layout region for docking windows');
128
128
  /** Present on a window entity while it is docked into a region. */
129
129
  export const UIDockedTo = createComponent('UIDockedTo', {
@@ -1 +1 @@
1
- {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ECS components for UI Extensions.\n *\n * These compose with the IWSDK's own components - a window entity typically\n * carries `PanelUI` (the IWSDK loads/renders the panel), `UIWindow` (ours),\n * and the IWSDK interaction tags (`RayInteractable`, `PokeInteractable`).\n * The systems in this package add/remove the IWSDK `Follower` / `ScreenSpace`\n * components to realise dock modes rather than re-implementing them.\n */\nimport { Types, createComponent } from '@iwsdk/core';\nimport { DEFAULT_HAND_MENU, DockMode } from '@realitycollective/webxr-uiextensions';\n\n/** `HandMenuOptions.hand` as an enum object for the component schema. */\nexport const HandChoice = { Left: 'left', Right: 'right', Either: 'either' } as const;\n/** `HandMenuOptions.anchor` as an enum object for the component schema. */\nexport const HandAnchor = {\n Above: 'above',\n Inside: 'inside',\n Outside: 'outside',\n Wrist: 'wrist',\n} as const;\n\n/** Region flow options mirrored as an enum object for the component schema. */\nexport const RegionFlowType = {\n Row: 'row',\n Column: 'column',\n Grid: 'grid',\n} as const;\n\n/**\n * Marks a panel entity as a managed window with chrome, focus, docking and\n * drag behaviour. Pair with `PanelUI` whose markup contains the window chrome\n * elements (see `WINDOW_CHROME_IDS` / the Examples folder).\n */\nexport const UIWindow = createComponent(\n 'UIWindow',\n {\n /** Stable id; auto-generated when empty. */\n windowId: { type: Types.String, default: '' },\n /** Title text written into the `uix-title` element. */\n title: { type: Types.String, default: '' },\n /** Current dock state. */\n dockMode: { type: Types.Enum, enum: DockMode, default: DockMode.WorldLocked },\n /** Whether the title bar drags the window. */\n movable: { type: Types.Boolean, default: true },\n /**\n * Title-bar buttons, all OFF by default. These seed the window's chrome\n * state on adoption; after that `WindowManager.setChrome` is the way to\n * change them, and the values here are kept in step with it.\n */\n closable: { type: Types.Boolean, default: false },\n minimizable: { type: Types.Boolean, default: false },\n /** The pin (body-follow ⇄ world-locked) affordance. */\n pinnable: { type: Types.Boolean, default: false },\n /** The DOCK affordance, which returns the window to where it spawned. */\n dockable: { type: Types.Boolean, default: false },\n /** Keep the window yawed toward the viewer while it is being dragged. */\n billboardWhileDragging: { type: Types.Boolean, default: true },\n /**\n * Seconds a title-bar press must be held before it becomes a drag.\n * Shorter presses stay clicks, so title-bar buttons don't fight the drag.\n */\n dragDelay: { type: Types.Float32, default: 0.3 },\n /** Head-relative offset used in `body-follow` mode (meters). */\n followOffset: { type: Types.Vec3, default: [0, -0.15, -1.2] },\n /** `Follower` lerp speed for body-follow. */\n followSpeed: { type: Types.Float32, default: 3 },\n /** `Follower` positional deadzone for body-follow (meters). */\n followTolerance: { type: Types.Float32, default: 0.35 },\n /** Meters the focused window is nudged toward the viewer per focus depth. */\n focusBias: { type: Types.Float32, default: 0.02 },\n /**\n * Hand-menu placement, used while `dockMode` is `hand-locked`. These seed\n * the record's `handMenu` on adoption; after that\n * `WindowManager.setHandMenu` is the way to change them, and the values\n * here are kept in step with it. See `hand-menu.ts` in the core for the\n * hand frame and what each anchor means.\n */\n hand: { type: Types.Enum, enum: HandChoice, default: DEFAULT_HAND_MENU.hand },\n handAnchor: { type: Types.Enum, enum: HandAnchor, default: DEFAULT_HAND_MENU.anchor },\n handAnchorDistance: { type: Types.Float32, default: DEFAULT_HAND_MENU.anchorDistance },\n handOffset: { type: Types.Vec3, default: [0, 0, 0] },\n palmGate: { type: Types.Boolean, default: DEFAULT_HAND_MENU.palmGate },\n palmAngle: { type: Types.Float32, default: DEFAULT_HAND_MENU.palmAngle },\n /**\n * World size box in meters, applied via `UIKitDocument.setTargetDimensions`\n * once the document loads. IWSDK 0.5 removed `PanelUI.maxWidth/maxHeight`;\n * this is the supported replacement. `0` leaves the document at its\n * intrinsic markup size, scaled only by the entity transform.\n */\n targetWidth: { type: Types.Float32, default: 0 },\n targetHeight: { type: Types.Float32, default: 0 },\n },\n 'UI Extensions managed window (chrome, focus, docking, drag)',\n);\n\n/**\n * Internal bookkeeping the systems keep on window entities - which dock mode\n * has actually been applied to engine components, and whether chrome wiring\n * has run for the current PanelDocument.\n */\nexport const UIWindowState = createComponent(\n 'UIWindowState',\n {\n appliedDockMode: { type: Types.String, default: '' },\n chromeWired: { type: Types.Boolean, default: false },\n /**\n * Whether the hand-menu palm gate is open this frame (always true in the\n * other dock modes). `UIDockSystem` writes it; `UIWindowSystem` combines\n * it with the record's `hidden` to decide what is drawn and hittable.\n */\n gateOpen: { type: Types.Boolean, default: true },\n /** What `UIWindowSystem` last applied: drawn and hittable, or not. */\n presented: { type: Types.Boolean, default: true },\n /** \"Home\" snapshot captured on adoption - the DOCK button returns here. */\n homeDockMode: { type: Types.String, default: '' },\n homeRegion: { type: Types.String, default: '' },\n homePosition: { type: Types.Vec3, default: [0, 0, 0] },\n homeYaw: { type: Types.Float32, default: 0 },\n },\n 'Internal UI Extensions window bookkeeping',\n);\n\n/**\n * A named layout region that docked windows snap to and are laid out within.\n * Place the entity where the region should live (it can itself carry a\n * `Follower` to make a body-locked region).\n */\nexport const UIDockRegion = createComponent(\n 'UIDockRegion',\n {\n regionId: { type: Types.String, default: '' },\n flow: { type: Types.Enum, enum: RegionFlowType, default: RegionFlowType.Column },\n /** Distance between slot origins (meters). */\n pitch: { type: Types.Float32, default: 0.35 },\n /** Columns per row (grid flow only). */\n columns: { type: Types.Float32, default: 2 },\n /** Max docked windows (0 = unlimited). */\n capacity: { type: Types.Float32, default: 0 },\n /** Drop-capture radius (meters). */\n snapRadius: { type: Types.Float32, default: 0.5 },\n },\n 'UI Extensions layout region for docking windows',\n);\n\n/** Present on a window entity while it is docked into a region. */\nexport const UIDockedTo = createComponent(\n 'UIDockedTo',\n {\n regionId: { type: Types.String, default: '' },\n },\n 'Marks a UI Extensions window as docked into a region',\n);\n"]}
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ECS components for UI Extensions.\n *\n * These compose with the IWSDK's own components - a window entity typically\n * carries `PanelUI` (the IWSDK loads/renders the panel), `UIWindow` (ours),\n * and the IWSDK interaction tags (`RayInteractable`, `PokeInteractable`).\n * The systems in this package add/remove the IWSDK `Follower` / `ScreenSpace`\n * components to realise dock modes rather than re-implementing them.\n */\nimport { Types, createComponent } from '@iwsdk/core';\nimport {\n DEFAULT_BILLBOARD_WHILE_DRAGGING,\n DEFAULT_DRAG_DELAY,\n DEFAULT_FOCUS_BIAS,\n DEFAULT_HAND_MENU,\n DEFAULT_REGION,\n DEFAULT_WINDOW_FOLLOW,\n DockMode,\n} from '@realitycollective/webxr-uiextensions';\n\n/** `HandMenuOptions.hand` as an enum object for the component schema. */\nexport const HandChoice = { Left: 'left', Right: 'right', Either: 'either' } as const;\n/** `HandMenuOptions.anchor` as an enum object for the component schema. */\nexport const HandAnchor = {\n Above: 'above',\n Inside: 'inside',\n Outside: 'outside',\n Wrist: 'wrist',\n} as const;\n\n/** Region flow options mirrored as an enum object for the component schema. */\nexport const RegionFlowType = {\n Row: 'row',\n Column: 'column',\n Grid: 'grid',\n} as const;\n\n/**\n * Marks a panel entity as a managed window with chrome, focus, docking and\n * drag behaviour. Pair with `PanelUI` whose markup contains the window chrome\n * elements (see `WINDOW_CHROME_IDS` / the Examples folder).\n */\nexport const UIWindow = createComponent(\n 'UIWindow',\n {\n /** Stable id; auto-generated when empty. */\n windowId: { type: Types.String, default: '' },\n /** Title text written into the `uix-title` element. */\n title: { type: Types.String, default: '' },\n /** Current dock state. */\n dockMode: { type: Types.Enum, enum: DockMode, default: DockMode.WorldLocked },\n /** Whether the title bar drags the window. */\n movable: { type: Types.Boolean, default: true },\n /**\n * Title-bar buttons, all OFF by default. These seed the window's chrome\n * state on adoption; after that `WindowManager.setChrome` is the way to\n * change them, and the values here are kept in step with it.\n */\n closable: { type: Types.Boolean, default: false },\n minimizable: { type: Types.Boolean, default: false },\n /** The pin (body-follow ⇄ world-locked) affordance. */\n pinnable: { type: Types.Boolean, default: false },\n /** The DOCK affordance, which returns the window to where it spawned. */\n dockable: { type: Types.Boolean, default: false },\n /** Keep the window yawed toward the viewer while it is being dragged. */\n billboardWhileDragging: { type: Types.Boolean, default: DEFAULT_BILLBOARD_WHILE_DRAGGING },\n /**\n * Seconds a title-bar press must be held before it becomes a drag.\n * Shorter presses stay clicks, so title-bar buttons don't fight the drag.\n */\n dragDelay: { type: Types.Float32, default: DEFAULT_DRAG_DELAY },\n /** Head-relative offset used in `body-follow` mode (meters). */\n followOffset: { type: Types.Vec3, default: [...DEFAULT_WINDOW_FOLLOW.offset] },\n /** `Follower` lerp speed for body-follow. */\n followSpeed: { type: Types.Float32, default: DEFAULT_WINDOW_FOLLOW.speed },\n /** `Follower` positional deadzone for body-follow (meters). */\n followTolerance: { type: Types.Float32, default: DEFAULT_WINDOW_FOLLOW.tolerance },\n /** Meters the focused window is nudged toward the viewer per focus depth. */\n focusBias: { type: Types.Float32, default: DEFAULT_FOCUS_BIAS },\n /**\n * Hand-menu placement, used while `dockMode` is `hand-locked`. These seed\n * the record's `handMenu` on adoption; after that\n * `WindowManager.setHandMenu` is the way to change them, and the values\n * here are kept in step with it. See `hand-menu.ts` in the core for the\n * hand frame and what each anchor means.\n */\n hand: { type: Types.Enum, enum: HandChoice, default: DEFAULT_HAND_MENU.hand },\n handAnchor: { type: Types.Enum, enum: HandAnchor, default: DEFAULT_HAND_MENU.anchor },\n handAnchorDistance: { type: Types.Float32, default: DEFAULT_HAND_MENU.anchorDistance },\n handOffset: { type: Types.Vec3, default: [0, 0, 0] },\n palmGate: { type: Types.Boolean, default: DEFAULT_HAND_MENU.palmGate },\n palmAngle: { type: Types.Float32, default: DEFAULT_HAND_MENU.palmAngle },\n /**\n * World size box in meters, applied via `UIKitDocument.setTargetDimensions`\n * once the document loads. IWSDK 0.5 removed `PanelUI.maxWidth/maxHeight`;\n * this is the supported replacement. `0` leaves the document at its\n * intrinsic markup size, scaled only by the entity transform.\n */\n targetWidth: { type: Types.Float32, default: 0 },\n targetHeight: { type: Types.Float32, default: 0 },\n },\n 'UI Extensions managed window (chrome, focus, docking, drag)',\n);\n\n/**\n * Internal bookkeeping the systems keep on window entities - which dock mode\n * has actually been applied to engine components, and whether chrome wiring\n * has run for the current PanelDocument.\n */\nexport const UIWindowState = createComponent(\n 'UIWindowState',\n {\n appliedDockMode: { type: Types.String, default: '' },\n chromeWired: { type: Types.Boolean, default: false },\n /**\n * Whether the hand-menu palm gate is open this frame (always true in the\n * other dock modes). `UIDockSystem` writes it; `UIWindowSystem` combines\n * it with the record's `hidden` to decide what is drawn and hittable.\n */\n gateOpen: { type: Types.Boolean, default: true },\n /** What `UIWindowSystem` last applied: drawn and hittable, or not. */\n presented: { type: Types.Boolean, default: true },\n /** \"Home\" snapshot captured on adoption - the DOCK button returns here. */\n homeDockMode: { type: Types.String, default: '' },\n homeRegion: { type: Types.String, default: '' },\n homePosition: { type: Types.Vec3, default: [0, 0, 0] },\n homeYaw: { type: Types.Float32, default: 0 },\n },\n 'Internal UI Extensions window bookkeeping',\n);\n\n/**\n * A named layout region that docked windows snap to and are laid out within.\n * Place the entity where the region should live (it can itself carry a\n * `Follower` to make a body-locked region).\n */\nexport const UIDockRegion = createComponent(\n 'UIDockRegion',\n {\n regionId: { type: Types.String, default: '' },\n flow: { type: Types.Enum, enum: RegionFlowType, default: DEFAULT_REGION.flow },\n /** Distance between slot origins (meters). */\n pitch: { type: Types.Float32, default: DEFAULT_REGION.pitch },\n /** Columns per row (grid flow only). */\n columns: { type: Types.Float32, default: DEFAULT_REGION.columns },\n /** Max docked windows (0 = unlimited). */\n capacity: { type: Types.Float32, default: DEFAULT_REGION.capacity },\n /** Drop-capture radius (meters). */\n snapRadius: { type: Types.Float32, default: DEFAULT_REGION.snapRadius },\n },\n 'UI Extensions layout region for docking windows',\n);\n\n/** Present on a window entity while it is docked into a region. */\nexport const UIDockedTo = createComponent(\n 'UIDockedTo',\n {\n regionId: { type: Types.String, default: '' },\n },\n 'Marks a UI Extensions window as docked into a region',\n);\n"]}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `createSystem` from `@iwsdk/core`, with its return type written out.
3
3
  *
4
- * Why this file exists. IWSDK 0.5.3 declares `createSystem` in
4
+ * Why this file exists. IWSDK (0.5.3, and still 1.0.0) declares `createSystem` in
5
5
  * `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,
6
6
  * and that file imports `World` from './world' with no extension, the only
7
7
  * extensionless relative import in the package. This repository builds with
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `createSystem` from `@iwsdk/core`, with its return type written out.
3
3
  *
4
- * Why this file exists. IWSDK 0.5.3 declares `createSystem` in
4
+ * Why this file exists. IWSDK (0.5.3, and still 1.0.0) declares `createSystem` in
5
5
  * `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,
6
6
  * and that file imports `World` from './world' with no extension, the only
7
7
  * extensionless relative import in the package. This repository builds with
@@ -1 +1 @@
1
- {"version":3,"file":"create-system.js","sourceRoot":"","sources":["../src/create-system.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AAGzF,MAAM,UAAU,YAAY,CAC1B,OAAW,EACX,MAAU;IAEV,OAAO,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * `createSystem` from `@iwsdk/core`, with its return type written out.\n *\n * Why this file exists. IWSDK 0.5.3 declares `createSystem` in\n * `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,\n * and that file imports `World` from './world' with no extension, the only\n * extensionless relative import in the package. This repository builds with\n * `moduleResolution: NodeNext`, under which an extensionless relative import\n * in an ES module does not resolve (TS2835), so inside the host's own\n * declaration `World` is an unresolved name. `skipLibCheck` hides the error,\n * and TypeScript's declaration emitter preserves an unresolved name verbatim,\n * so every `export class X extends createSystem(...)` in this package was\n * emitted with a bare `World` in its base type. A consumer that checks\n * library files (`skipLibCheck: false`, or a package emitting declarations on\n * top of ours) fails with TS2304 on its first import of this package. A file\n * that happened to import `World` for another signature resolved the bare\n * name by accident, which is how the sibling adapters looked clean.\n *\n * The annotation below names `World` through `@iwsdk/core`'s barrel, whose\n * imports all carry extensions, so the emitter writes\n * `import(\"@iwsdk/core\").World` wherever the result is extended. Every system\n * in this package takes `createSystem` from here, never from `@iwsdk/core`.\n * `elics` is imported for its types only: it is `@iwsdk/core`'s own\n * dependency, the import is erased at build time, and the emitted\n * declarations already name `import(\"elics\").SystemConstructor`.\n *\n * Delete this file, and import `createSystem` from `@iwsdk/core` again, once\n * IWSDK ships `./world.js` there. `scripts/declaration-check.mjs`, run by\n * `verify:pack`, is the check that fails while the bare name is emitted.\n */\nimport { createSystem as createIWSDKSystem, type System, type World } from '@iwsdk/core';\nimport type { SystemConstructor, SystemQueries, SystemSchema } from 'elics';\n\nexport function createSystem<S extends SystemSchema, Q extends SystemQueries>(\n queries?: Q,\n schema?: S,\n): SystemConstructor<S, Q, World, System<S, Q>> {\n return createIWSDKSystem(queries, schema);\n}\n"]}
1
+ {"version":3,"file":"create-system.js","sourceRoot":"","sources":["../src/create-system.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AAGzF,MAAM,UAAU,YAAY,CAC1B,OAAW,EACX,MAAU;IAEV,OAAO,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * `createSystem` from `@iwsdk/core`, with its return type written out.\n *\n * Why this file exists. IWSDK (0.5.3, and still 1.0.0) declares `createSystem` in\n * `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,\n * and that file imports `World` from './world' with no extension, the only\n * extensionless relative import in the package. This repository builds with\n * `moduleResolution: NodeNext`, under which an extensionless relative import\n * in an ES module does not resolve (TS2835), so inside the host's own\n * declaration `World` is an unresolved name. `skipLibCheck` hides the error,\n * and TypeScript's declaration emitter preserves an unresolved name verbatim,\n * so every `export class X extends createSystem(...)` in this package was\n * emitted with a bare `World` in its base type. A consumer that checks\n * library files (`skipLibCheck: false`, or a package emitting declarations on\n * top of ours) fails with TS2304 on its first import of this package. A file\n * that happened to import `World` for another signature resolved the bare\n * name by accident, which is how the sibling adapters looked clean.\n *\n * The annotation below names `World` through `@iwsdk/core`'s barrel, whose\n * imports all carry extensions, so the emitter writes\n * `import(\"@iwsdk/core\").World` wherever the result is extended. Every system\n * in this package takes `createSystem` from here, never from `@iwsdk/core`.\n * `elics` is imported for its types only: it is `@iwsdk/core`'s own\n * dependency, the import is erased at build time, and the emitted\n * declarations already name `import(\"elics\").SystemConstructor`.\n *\n * Delete this file, and import `createSystem` from `@iwsdk/core` again, once\n * IWSDK ships `./world.js` there. `scripts/declaration-check.mjs`, run by\n * `verify:pack`, is the check that fails while the bare name is emitted.\n */\nimport { createSystem as createIWSDKSystem, type System, type World } from '@iwsdk/core';\nimport type { SystemConstructor, SystemQueries, SystemSchema } from 'elics';\n\nexport function createSystem<S extends SystemSchema, Q extends SystemQueries>(\n queries?: Q,\n schema?: S,\n): SystemConstructor<S, Q, World, System<S, Q>> {\n return createIWSDKSystem(queries, schema);\n}\n"]}
package/dist/factory.js CHANGED
@@ -4,8 +4,8 @@
4
4
  * they do can be done manually with the components.
5
5
  */
6
6
  import { Follower, FollowBehavior, PanelUI, PokeInteractable, RayInteractable, } from '@iwsdk/core';
7
- import { UIDockRegion, UIDockedTo, UIWindow, RegionFlowType } from './components.js';
8
- import { DockMode, resolveHandMenu, } from '@realitycollective/webxr-uiextensions';
7
+ import { UIDockRegion, UIDockedTo, UIWindow } from './components.js';
8
+ import { DEFAULT_BILLBOARD_WHILE_DRAGGING, DEFAULT_DRAG_DELAY, DEFAULT_REGION, DEFAULT_REGION_FOLLOW, DEFAULT_WINDOW_FOLLOW, DockMode, resolveHandMenu, } from '@realitycollective/webxr-uiextensions';
9
9
  export function createUIWindow(world, options) {
10
10
  const handMenu = resolveHandMenu(options.handMenu);
11
11
  const entity = world
@@ -26,10 +26,11 @@ export function createUIWindow(world, options) {
26
26
  minimizable: options.minimizable ?? false,
27
27
  pinnable: options.pinnable ?? false,
28
28
  dockable: options.dockable ?? false,
29
- billboardWhileDragging: options.billboardWhileDragging ?? true,
30
- followOffset: options.followOffset ?? [0, -0.15, -1.2],
31
- followSpeed: options.followSpeed ?? 3,
32
- followTolerance: options.followTolerance ?? 0.35,
29
+ billboardWhileDragging: options.billboardWhileDragging ?? DEFAULT_BILLBOARD_WHILE_DRAGGING,
30
+ dragDelay: options.dragDelay ?? DEFAULT_DRAG_DELAY,
31
+ followOffset: options.followOffset ?? [...DEFAULT_WINDOW_FOLLOW.offset],
32
+ followSpeed: options.followSpeed ?? DEFAULT_WINDOW_FOLLOW.speed,
33
+ followTolerance: options.followTolerance ?? DEFAULT_WINDOW_FOLLOW.tolerance,
33
34
  hand: handMenu.hand,
34
35
  handAnchor: handMenu.anchor,
35
36
  handAnchorDistance: handMenu.anchorDistance,
@@ -50,11 +51,12 @@ export function createUIWindow(world, options) {
50
51
  export function createDockRegion(world, options) {
51
52
  const entity = world.createTransformEntity().addComponent(UIDockRegion, {
52
53
  regionId: options.id,
53
- flow: options.flow ?? RegionFlowType.Column,
54
- pitch: options.pitch ?? 0.35,
55
- columns: options.columns ?? 2,
56
- capacity: options.capacity ?? 0,
57
- snapRadius: options.snapRadius ?? 0.5,
54
+ // One set of region defaults: the core's DEFAULT_REGION.
55
+ flow: options.flow ?? DEFAULT_REGION.flow,
56
+ pitch: options.pitch ?? DEFAULT_REGION.pitch,
57
+ columns: options.columns ?? DEFAULT_REGION.columns,
58
+ capacity: options.capacity ?? DEFAULT_REGION.capacity,
59
+ snapRadius: options.snapRadius ?? DEFAULT_REGION.snapRadius,
58
60
  });
59
61
  if (options.position) {
60
62
  entity.object3D?.position.set(...options.position);
@@ -63,7 +65,7 @@ export function createDockRegion(world, options) {
63
65
  entity.addComponent(Follower, {
64
66
  // Camera, not player.head - the head rig is origin-locked outside XR.
65
67
  target: world.camera,
66
- offsetPosition: options.followOffset ?? [0, -0.2, -1.4],
68
+ offsetPosition: options.followOffset ?? [...DEFAULT_REGION_FOLLOW.offset],
67
69
  behavior: FollowBehavior.PivotY,
68
70
  });
69
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Convenience factories - one call to spawn a managed window or dock region.\n * Thin sugar over `createTransformEntity().addComponent(...)`; everything\n * they do can be done manually with the components.\n */\nimport {\n Follower,\n FollowBehavior,\n PanelUI,\n PokeInteractable,\n RayInteractable,\n type Entity,\n type World,\n} from '@iwsdk/core';\nimport { UIDockRegion, UIDockedTo, UIWindow, RegionFlowType } from './components.js';\nimport {\n DockMode,\n resolveHandMenu,\n type WindowOptionsBase,\n} from '@realitycollective/webxr-uiextensions';\n\n/**\n * Options for {@link createUIWindow}.\n *\n * Everything but `config` comes from the portable {@link WindowOptionsBase},\n * so the same option names mean the same thing on every adapter. `maxWidth`\n * and `maxHeight` route to `UIWindow.targetWidth/Height` here, because IWSDK\n * 0.5 removed those fields from `PanelUI`; omit them to keep the markup's\n * intrinsic size and scale through the entity transform. The title-bar\n * buttons (`closable`, `minimizable`, `pinnable`, `dockable`) are all off\n * unless asked for.\n */\nexport interface CreateWindowOptions extends WindowOptionsBase {\n /**\n * URL of the UIKitML source (e.g. \"/ui/my-window.uikitml\"). IWSDK 0.5 parses\n * UIKitML at runtime - the build-time plugin and its generated JSON are gone.\n */\n config: string;\n /** Keep the window yawed toward the viewer while it is being dragged. */\n billboardWhileDragging?: boolean;\n}\n\nexport function createUIWindow(world: World, options: CreateWindowOptions): Entity {\n const handMenu = resolveHandMenu(options.handMenu);\n const entity = world\n .createTransformEntity()\n .addComponent(PanelUI, {\n config: options.config,\n })\n .addComponent(UIWindow, {\n // Applied by UIWindowSystem once the document loads - see components.ts.\n targetWidth: options.maxWidth ?? 0,\n targetHeight: options.maxHeight ?? 0,\n windowId: options.id ?? '',\n title: options.title ?? '',\n dockMode: options.dockMode ?? DockMode.WorldLocked,\n movable: options.movable ?? true,\n // Chrome buttons are opt-in - see WindowOptionsBase.\n closable: options.closable ?? false,\n minimizable: options.minimizable ?? false,\n pinnable: options.pinnable ?? false,\n dockable: options.dockable ?? false,\n billboardWhileDragging: options.billboardWhileDragging ?? true,\n followOffset: options.followOffset ?? [0, -0.15, -1.2],\n followSpeed: options.followSpeed ?? 3,\n followTolerance: options.followTolerance ?? 0.35,\n hand: handMenu.hand,\n handAnchor: handMenu.anchor,\n handAnchorDistance: handMenu.anchorDistance,\n handOffset: handMenu.offset,\n palmGate: handMenu.palmGate,\n palmAngle: handMenu.palmAngle,\n })\n .addComponent(RayInteractable)\n .addComponent(PokeInteractable);\n\n if (options.position) {\n entity.object3D?.position.set(...options.position);\n }\n if (options.region) {\n entity.addComponent(UIDockedTo, { regionId: options.region });\n }\n return entity;\n}\n\nexport interface CreateDockRegionOptions {\n id: string;\n flow?: (typeof RegionFlowType)[keyof typeof RegionFlowType];\n pitch?: number;\n columns?: number;\n capacity?: number;\n snapRadius?: number;\n /** World position of the region origin. */\n position?: [number, number, number];\n /**\n * Body-lock the region itself: it follows the player's head at\n * `followOffset`, and every window docked into it comes along.\n */\n follow?: boolean;\n followOffset?: [number, number, number];\n}\n\nexport function createDockRegion(world: World, options: CreateDockRegionOptions): Entity {\n const entity = world.createTransformEntity().addComponent(UIDockRegion, {\n regionId: options.id,\n flow: options.flow ?? RegionFlowType.Column,\n pitch: options.pitch ?? 0.35,\n columns: options.columns ?? 2,\n capacity: options.capacity ?? 0,\n snapRadius: options.snapRadius ?? 0.5,\n });\n if (options.position) {\n entity.object3D?.position.set(...options.position);\n }\n if (options.follow) {\n entity.addComponent(Follower, {\n // Camera, not player.head - the head rig is origin-locked outside XR.\n target: world.camera,\n offsetPosition: options.followOffset ?? [0, -0.2, -1.4],\n behavior: FollowBehavior.PivotY,\n });\n }\n return entity;\n}\n"]}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Convenience factories - one call to spawn a managed window or dock region.\n * Thin sugar over `createTransformEntity().addComponent(...)`; everything\n * they do can be done manually with the components.\n */\nimport {\n Follower,\n FollowBehavior,\n PanelUI,\n PokeInteractable,\n RayInteractable,\n type Entity,\n type World,\n} from '@iwsdk/core';\nimport { UIDockRegion, UIDockedTo, UIWindow, RegionFlowType } from './components.js';\nimport {\n DEFAULT_BILLBOARD_WHILE_DRAGGING,\n DEFAULT_DRAG_DELAY,\n DEFAULT_REGION,\n DEFAULT_REGION_FOLLOW,\n DEFAULT_WINDOW_FOLLOW,\n DockMode,\n resolveHandMenu,\n type WindowOptionsBase,\n} from '@realitycollective/webxr-uiextensions';\n\n/**\n * Options for {@link createUIWindow}.\n *\n * Everything but `config` comes from the portable {@link WindowOptionsBase},\n * so the same option names mean the same thing on every adapter. `maxWidth`\n * and `maxHeight` route to `UIWindow.targetWidth/Height` here, because IWSDK\n * 0.5 removed those fields from `PanelUI`; omit them to keep the markup's\n * intrinsic size and scale through the entity transform. The title-bar\n * buttons (`closable`, `minimizable`, `pinnable`, `dockable`) are all off\n * unless asked for.\n */\nexport interface CreateWindowOptions extends WindowOptionsBase {\n /**\n * URL of the UIKitML source (e.g. \"/ui/my-window.uikitml\"). IWSDK 0.5 parses\n * UIKitML at runtime - the build-time plugin and its generated JSON are gone.\n */\n config: string;\n /** Keep the window yawed toward the viewer while it is being dragged. */\n billboardWhileDragging?: boolean;\n}\n\nexport function createUIWindow(world: World, options: CreateWindowOptions): Entity {\n const handMenu = resolveHandMenu(options.handMenu);\n const entity = world\n .createTransformEntity()\n .addComponent(PanelUI, {\n config: options.config,\n })\n .addComponent(UIWindow, {\n // Applied by UIWindowSystem once the document loads - see components.ts.\n targetWidth: options.maxWidth ?? 0,\n targetHeight: options.maxHeight ?? 0,\n windowId: options.id ?? '',\n title: options.title ?? '',\n dockMode: options.dockMode ?? DockMode.WorldLocked,\n movable: options.movable ?? true,\n // Chrome buttons are opt-in - see WindowOptionsBase.\n closable: options.closable ?? false,\n minimizable: options.minimizable ?? false,\n pinnable: options.pinnable ?? false,\n dockable: options.dockable ?? false,\n billboardWhileDragging: options.billboardWhileDragging ?? DEFAULT_BILLBOARD_WHILE_DRAGGING,\n dragDelay: options.dragDelay ?? DEFAULT_DRAG_DELAY,\n followOffset: options.followOffset ?? [...DEFAULT_WINDOW_FOLLOW.offset],\n followSpeed: options.followSpeed ?? DEFAULT_WINDOW_FOLLOW.speed,\n followTolerance: options.followTolerance ?? DEFAULT_WINDOW_FOLLOW.tolerance,\n hand: handMenu.hand,\n handAnchor: handMenu.anchor,\n handAnchorDistance: handMenu.anchorDistance,\n handOffset: handMenu.offset,\n palmGate: handMenu.palmGate,\n palmAngle: handMenu.palmAngle,\n })\n .addComponent(RayInteractable)\n .addComponent(PokeInteractable);\n\n if (options.position) {\n entity.object3D?.position.set(...options.position);\n }\n if (options.region) {\n entity.addComponent(UIDockedTo, { regionId: options.region });\n }\n return entity;\n}\n\nexport interface CreateDockRegionOptions {\n id: string;\n flow?: (typeof RegionFlowType)[keyof typeof RegionFlowType];\n pitch?: number;\n columns?: number;\n capacity?: number;\n snapRadius?: number;\n /** World position of the region origin. */\n position?: [number, number, number];\n /**\n * Body-lock the region itself: it follows the player's head at\n * `followOffset`, and every window docked into it comes along.\n */\n follow?: boolean;\n followOffset?: [number, number, number];\n}\n\nexport function createDockRegion(world: World, options: CreateDockRegionOptions): Entity {\n const entity = world.createTransformEntity().addComponent(UIDockRegion, {\n regionId: options.id,\n // One set of region defaults: the core's DEFAULT_REGION.\n flow: options.flow ?? DEFAULT_REGION.flow,\n pitch: options.pitch ?? DEFAULT_REGION.pitch,\n columns: options.columns ?? DEFAULT_REGION.columns,\n capacity: options.capacity ?? DEFAULT_REGION.capacity,\n snapRadius: options.snapRadius ?? DEFAULT_REGION.snapRadius,\n });\n if (options.position) {\n entity.object3D?.position.set(...options.position);\n }\n if (options.follow) {\n entity.addComponent(Follower, {\n // Camera, not player.head - the head rig is origin-locked outside XR.\n target: world.camera,\n offsetPosition: options.followOffset ?? [...DEFAULT_REGION_FOLLOW.offset],\n behavior: FollowBehavior.PivotY,\n });\n }\n return entity;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import type { RegionFlow } from '@realitycollective/webxr-uiextensions';
1
2
  declare const UIDockRegionSystem_base: import("elics").SystemConstructor<import("elics").SystemSchema, {
2
3
  regions: {
3
4
  required: import("elics").Component<{
@@ -12,7 +13,7 @@ declare const UIDockRegionSystem_base: import("elics").SystemConstructor<import(
12
13
  readonly Column: "column";
13
14
  readonly Grid: "grid";
14
15
  };
15
- default: "column";
16
+ default: RegionFlow;
16
17
  };
17
18
  pitch: {
18
19
  type: import("@iwsdk/core").Types.Float32;
@@ -160,7 +161,7 @@ declare const UIDockRegionSystem_base: import("elics").SystemConstructor<import(
160
161
  readonly Column: "column";
161
162
  readonly Grid: "grid";
162
163
  };
163
- default: "column";
164
+ default: RegionFlow;
164
165
  };
165
166
  pitch: {
166
167
  type: import("@iwsdk/core").Types.Float32;
@@ -306,5 +306,7 @@ export declare class UIDockSystem extends UIDockSystem_base {
306
306
  private yawQuaternion;
307
307
  private yawEuler;
308
308
  private addFollower;
309
+ /** `manager.setFollow` after spawn: the live Follower takes the new offset and tuning at once. */
310
+ private applyFollowChange;
309
311
  }
310
312
  export {};
@@ -30,6 +30,7 @@ export class UIDockSystem extends createSystem({
30
30
  parentQuaternion = new Quaternion();
31
31
  init() {
32
32
  this.manager = windowManagerFor(this.world);
33
+ this.cleanupFuncs.push(this.manager.events.on('followChanged', ({ window }) => this.applyFollowChange(window.id)));
33
34
  }
34
35
  update() {
35
36
  let hands;
@@ -174,12 +175,13 @@ export class UIDockSystem extends createSystem({
174
175
  yawQuaternion = new Quaternion();
175
176
  yawEuler = new Euler();
176
177
  addFollower(entity, options = {}) {
178
+ // The record's follow settings (`manager.setFollow` may have changed them
179
+ // since spawn); the component's own values only before a record exists.
180
+ const record = this.manager.get(entity.getValue(UIWindow, 'windowId'));
177
181
  const configured = entity.getVectorView(UIWindow, 'followOffset');
178
- let offset = [
179
- configured[0] ?? 0,
180
- configured[1] ?? 0,
181
- configured[2] ?? 0,
182
- ];
182
+ let offset = record
183
+ ? [record.follow.offset[0], record.follow.offset[1], record.follow.offset[2]]
184
+ : [configured[0] ?? 0, configured[1] ?? 0, configured[2] ?? 0];
183
185
  const object = entity.object3D;
184
186
  if (options.fromCurrentPose && object) {
185
187
  // Current window position in the viewer's yaw-local frame (matching
@@ -205,9 +207,25 @@ export class UIDockSystem extends createSystem({
205
207
  target: this.camera,
206
208
  offsetPosition: offset,
207
209
  behavior: FollowBehavior.PivotY,
208
- speed: entity.getValue(UIWindow, 'followSpeed'),
209
- tolerance: entity.getValue(UIWindow, 'followTolerance'),
210
+ speed: record ? record.follow.speed : entity.getValue(UIWindow, 'followSpeed'),
211
+ tolerance: record ? record.follow.tolerance : entity.getValue(UIWindow, 'followTolerance'),
210
212
  });
211
213
  }
214
+ /** `manager.setFollow` after spawn: the live Follower takes the new offset and tuning at once. */
215
+ applyFollowChange(windowId) {
216
+ const record = this.manager.get(windowId);
217
+ if (!record)
218
+ return;
219
+ for (const entity of this.queries.windows.entities) {
220
+ if (entity.getValue(UIWindow, 'windowId') !== windowId || !entity.hasComponent(Follower))
221
+ continue;
222
+ const offset = entity.getVectorView(Follower, 'offsetPosition');
223
+ offset[0] = record.follow.offset[0];
224
+ offset[1] = record.follow.offset[1];
225
+ offset[2] = record.follow.offset[2];
226
+ entity.setValue(Follower, 'speed', record.follow.speed);
227
+ entity.setValue(Follower, 'tolerance', record.follow.tolerance);
228
+ }
229
+ }
212
230
  }
213
231
  //# sourceMappingURL=dock-system.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dock-system.js","sourceRoot":"","sources":["../../src/systems/dock-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIDockSystem - realises dock modes by composing IWSDK components.\n *\n * `body-follow` and `head-locked` are implemented with the IWSDK's own\n * `Follower` / `ScreenSpace` components (reuse, not recreation); this system\n * only plans and applies transitions with the pure dock state machine.\n *\n * `hand-locked` (a hand menu) has no IWSDK component to lean on, so it is\n * placed here each frame: the hand poses come from the player rig's grip\n * spaces (a controller's grip, or the tracked hand), the decision of which\n * hand, whether the palm gate is open and where the panel goes is the core's\n * `evaluateHandMenu`, and the result is written to the entity transform and\n * to `UIWindowState.gateOpen`, which `UIWindowSystem` turns into visibility.\n */\nimport {\n Follower,\n FollowBehavior,\n ScreenSpace,\n type Entity,\n} from '@iwsdk/core';\nimport { createSystem } from '../create-system.js';\nimport { Euler, Quaternion, Vector3, type Object3D } from 'three';\nimport { UIWindow, UIWindowState } from '../components.js';\nimport {\n DockMode,\n evaluateHandMenu,\n isDockMode,\n planTransition,\n recipeFor,\n type DockModeValue,\n type Hand,\n type HandPoses,\n type PoseTuple,\n} from '@realitycollective/webxr-uiextensions';\nimport { faceViewerYaw } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport type { WindowManager } from '@realitycollective/webxr-uiextensions';\n\nexport class UIDockSystem extends createSystem({\n windows: { required: [UIWindow, UIWindowState] },\n}) {\n private manager!: WindowManager;\n private windowPosition = new Vector3();\n private viewerPosition = new Vector3();\n private handPosition = new Vector3();\n private handQuaternion = new Quaternion();\n private parentQuaternion = new Quaternion();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n }\n\n override update(): void {\n let hands: HandPoses | undefined;\n for (const entity of this.queries.windows.entities) {\n const requested = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n const applied = entity.getValue(UIWindowState, 'appliedDockMode') as string;\n if (isDockMode(requested) && applied !== requested) {\n this.apply(entity, applied, requested);\n }\n if (requested === DockMode.HandLocked) {\n hands ??= this.readHands();\n this.placeOnHand(entity, hands);\n }\n }\n }\n\n /** This frame's tracked hands, in the core's hand frame (WebXR grip convention). */\n private readHands(): HandPoses {\n const poses: HandPoses = {};\n const xr = (this.input as typeof this.input | undefined)?.xr;\n const rig = this.player as { gripSpaces?: Record<Hand, Object3D> } | undefined;\n if (!xr || !rig?.gripSpaces) {\n return poses;\n }\n for (const hand of ['left', 'right'] as const) {\n if (!xr.getPrimaryInputSource(hand)) {\n continue; // nothing tracked on that side this frame\n }\n const space = rig.gripSpaces[hand];\n space.getWorldPosition(this.handPosition);\n space.getWorldQuaternion(this.handQuaternion);\n poses[hand] = {\n position: [this.handPosition.x, this.handPosition.y, this.handPosition.z],\n quaternion: [\n this.handQuaternion.x,\n this.handQuaternion.y,\n this.handQuaternion.z,\n this.handQuaternion.w,\n ],\n };\n }\n return poses;\n }\n\n private placeOnHand(entity: Entity, hands: HandPoses): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const record = this.manager.get(id);\n const object = entity.object3D;\n if (!record || !object) {\n return;\n }\n this.camera.getWorldPosition(this.viewerPosition);\n const placement = evaluateHandMenu(\n hands,\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n record.handMenu,\n );\n if (Boolean(entity.getValue(UIWindowState, 'gateOpen')) !== placement.visible) {\n entity.setValue(UIWindowState, 'gateOpen', placement.visible);\n }\n if (placement.pose) {\n this.applyWorldPose(object, placement.pose);\n }\n }\n\n /** Write a world pose onto an object whatever it is parented to. */\n private applyWorldPose(object: Object3D, pose: PoseTuple): void {\n this.windowPosition.set(pose.position[0], pose.position[1], pose.position[2]);\n this.handQuaternion.set(\n pose.quaternion[0],\n pose.quaternion[1],\n pose.quaternion[2],\n pose.quaternion[3],\n );\n const parent = object.parent;\n if (parent) {\n parent.worldToLocal(this.windowPosition);\n parent.getWorldQuaternion(this.parentQuaternion).invert();\n this.handQuaternion.premultiply(this.parentQuaternion);\n }\n object.position.copy(this.windowPosition);\n object.quaternion.copy(this.handQuaternion);\n }\n\n private apply(entity: Entity, applied: string, requested: DockModeValue): void {\n if (isDockMode(applied)) {\n const plan = planTransition(applied, requested);\n if (!plan) {\n return;\n }\n if (plan.removeFollower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (plan.removeFollower && requested === DockMode.WorldLocked) {\n // Pinning in place: leave the window facing the user at the spot it\n // was pinned, instead of freezing whatever lagging follow rotation\n // it happened to have this frame.\n this.faceViewer(entity);\n }\n if (plan.removeScreenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n if (plan.removeHandAnchor) {\n // Off the hand: the gate no longer applies, and the window stays\n // where the hand left it, facing the viewer.\n entity.setValue(UIWindowState, 'gateOpen', true);\n this.faceViewer(entity);\n }\n if (plan.addFollower) {\n // Unpinning: keep the window where the user left it, expressed\n // relative to their head, instead of snapping to the default offset.\n this.addFollower(entity, { fromCurrentPose: true });\n }\n if (plan.addScreenSpace) {\n entity.addComponent(ScreenSpace);\n }\n if (plan.snap && entity.hasComponent(Follower)) {\n entity.setValue(Follower, 'needsPositionSync', true);\n }\n } else {\n // First application after adoption: build the requested recipe from\n // scratch (the entity carries no dock components we own yet).\n const recipe = recipeFor(requested);\n if (recipe.follower && !entity.hasComponent(Follower)) {\n this.addFollower(entity);\n }\n if (!recipe.follower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (recipe.screenSpace && !entity.hasComponent(ScreenSpace)) {\n entity.addComponent(ScreenSpace);\n }\n if (!recipe.screenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n }\n\n entity.setValue(UIWindowState, 'appliedDockMode', requested);\n\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.manager.has(id)) {\n this.manager.setDockMode(id, requested);\n }\n }\n\n private faceViewer(entity: Entity): void {\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.windowPosition);\n this.camera.getWorldPosition(this.viewerPosition);\n const yaw = faceViewerYaw(\n [this.windowPosition.x, this.windowPosition.y, this.windowPosition.z],\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n object.rotation.y,\n );\n object.rotation.set(0, yaw, 0);\n }\n\n private offsetHelper = new Vector3();\n private yawQuaternion = new Quaternion();\n private yawEuler = new Euler();\n\n private addFollower(entity: Entity, options: { fromCurrentPose?: boolean } = {}): void {\n const configured = entity.getVectorView(UIWindow, 'followOffset');\n let offset: [number, number, number] = [\n configured[0] ?? 0,\n configured[1] ?? 0,\n configured[2] ?? 0,\n ];\n\n const object = entity.object3D;\n if (options.fromCurrentPose && object) {\n // Current window position in the viewer's yaw-local frame (matching\n // how Follower's PivotY applies offsets), so following resumes from\n // the same bearing and distance the window was unpinned at.\n object.getWorldPosition(this.offsetHelper);\n this.camera.getWorldPosition(this.windowPosition);\n this.camera.getWorldQuaternion(this.yawQuaternion);\n this.yawEuler.setFromQuaternion(this.yawQuaternion, 'YXZ');\n this.yawEuler.x = 0;\n this.yawEuler.z = 0;\n this.yawQuaternion.setFromEuler(this.yawEuler).conjugate();\n this.offsetHelper.sub(this.windowPosition).applyQuaternion(this.yawQuaternion);\n // Degenerate (window at the viewer): keep the configured offset.\n if (this.offsetHelper.lengthSq() > 0.01) {\n offset = [this.offsetHelper.x, this.offsetHelper.y, this.offsetHelper.z];\n }\n }\n\n entity.addComponent(Follower, {\n // The camera, not player.head: three.js syncs the camera to the\n // headset pose in-session, while the head rig sits at the origin\n // outside XR (desktop) - following it would park windows at the floor.\n target: this.camera,\n offsetPosition: offset,\n behavior: FollowBehavior.PivotY,\n speed: entity.getValue(UIWindow, 'followSpeed') as number,\n tolerance: entity.getValue(UIWindow, 'followTolerance') as number,\n });\n }\n}\n"]}
1
+ {"version":3,"file":"dock-system.js","sourceRoot":"","sources":["../../src/systems/dock-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIDockSystem - realises dock modes by composing IWSDK components.\n *\n * `body-follow` and `head-locked` are implemented with the IWSDK's own\n * `Follower` / `ScreenSpace` components (reuse, not recreation); this system\n * only plans and applies transitions with the pure dock state machine.\n *\n * `hand-locked` (a hand menu) has no IWSDK component to lean on, so it is\n * placed here each frame: the hand poses come from the player rig's grip\n * spaces (a controller's grip, or the tracked hand), the decision of which\n * hand, whether the palm gate is open and where the panel goes is the core's\n * `evaluateHandMenu`, and the result is written to the entity transform and\n * to `UIWindowState.gateOpen`, which `UIWindowSystem` turns into visibility.\n */\nimport {\n Follower,\n FollowBehavior,\n ScreenSpace,\n type Entity,\n} from '@iwsdk/core';\nimport { createSystem } from '../create-system.js';\nimport { Euler, Quaternion, Vector3, type Object3D } from 'three';\nimport { UIWindow, UIWindowState } from '../components.js';\nimport {\n DockMode,\n evaluateHandMenu,\n isDockMode,\n planTransition,\n recipeFor,\n type DockModeValue,\n type Hand,\n type HandPoses,\n type PoseTuple,\n} from '@realitycollective/webxr-uiextensions';\nimport { faceViewerYaw } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport type { WindowManager } from '@realitycollective/webxr-uiextensions';\n\nexport class UIDockSystem extends createSystem({\n windows: { required: [UIWindow, UIWindowState] },\n}) {\n private manager!: WindowManager;\n private windowPosition = new Vector3();\n private viewerPosition = new Vector3();\n private handPosition = new Vector3();\n private handQuaternion = new Quaternion();\n private parentQuaternion = new Quaternion();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n this.cleanupFuncs.push(this.manager.events.on('followChanged', ({ window }) => this.applyFollowChange(window.id)));\n }\n\n override update(): void {\n let hands: HandPoses | undefined;\n for (const entity of this.queries.windows.entities) {\n const requested = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n const applied = entity.getValue(UIWindowState, 'appliedDockMode') as string;\n if (isDockMode(requested) && applied !== requested) {\n this.apply(entity, applied, requested);\n }\n if (requested === DockMode.HandLocked) {\n hands ??= this.readHands();\n this.placeOnHand(entity, hands);\n }\n }\n }\n\n /** This frame's tracked hands, in the core's hand frame (WebXR grip convention). */\n private readHands(): HandPoses {\n const poses: HandPoses = {};\n const xr = (this.input as typeof this.input | undefined)?.xr;\n const rig = this.player as { gripSpaces?: Record<Hand, Object3D> } | undefined;\n if (!xr || !rig?.gripSpaces) {\n return poses;\n }\n for (const hand of ['left', 'right'] as const) {\n if (!xr.getPrimaryInputSource(hand)) {\n continue; // nothing tracked on that side this frame\n }\n const space = rig.gripSpaces[hand];\n space.getWorldPosition(this.handPosition);\n space.getWorldQuaternion(this.handQuaternion);\n poses[hand] = {\n position: [this.handPosition.x, this.handPosition.y, this.handPosition.z],\n quaternion: [\n this.handQuaternion.x,\n this.handQuaternion.y,\n this.handQuaternion.z,\n this.handQuaternion.w,\n ],\n };\n }\n return poses;\n }\n\n private placeOnHand(entity: Entity, hands: HandPoses): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const record = this.manager.get(id);\n const object = entity.object3D;\n if (!record || !object) {\n return;\n }\n this.camera.getWorldPosition(this.viewerPosition);\n const placement = evaluateHandMenu(\n hands,\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n record.handMenu,\n );\n if (Boolean(entity.getValue(UIWindowState, 'gateOpen')) !== placement.visible) {\n entity.setValue(UIWindowState, 'gateOpen', placement.visible);\n }\n if (placement.pose) {\n this.applyWorldPose(object, placement.pose);\n }\n }\n\n /** Write a world pose onto an object whatever it is parented to. */\n private applyWorldPose(object: Object3D, pose: PoseTuple): void {\n this.windowPosition.set(pose.position[0], pose.position[1], pose.position[2]);\n this.handQuaternion.set(\n pose.quaternion[0],\n pose.quaternion[1],\n pose.quaternion[2],\n pose.quaternion[3],\n );\n const parent = object.parent;\n if (parent) {\n parent.worldToLocal(this.windowPosition);\n parent.getWorldQuaternion(this.parentQuaternion).invert();\n this.handQuaternion.premultiply(this.parentQuaternion);\n }\n object.position.copy(this.windowPosition);\n object.quaternion.copy(this.handQuaternion);\n }\n\n private apply(entity: Entity, applied: string, requested: DockModeValue): void {\n if (isDockMode(applied)) {\n const plan = planTransition(applied, requested);\n if (!plan) {\n return;\n }\n if (plan.removeFollower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (plan.removeFollower && requested === DockMode.WorldLocked) {\n // Pinning in place: leave the window facing the user at the spot it\n // was pinned, instead of freezing whatever lagging follow rotation\n // it happened to have this frame.\n this.faceViewer(entity);\n }\n if (plan.removeScreenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n if (plan.removeHandAnchor) {\n // Off the hand: the gate no longer applies, and the window stays\n // where the hand left it, facing the viewer.\n entity.setValue(UIWindowState, 'gateOpen', true);\n this.faceViewer(entity);\n }\n if (plan.addFollower) {\n // Unpinning: keep the window where the user left it, expressed\n // relative to their head, instead of snapping to the default offset.\n this.addFollower(entity, { fromCurrentPose: true });\n }\n if (plan.addScreenSpace) {\n entity.addComponent(ScreenSpace);\n }\n if (plan.snap && entity.hasComponent(Follower)) {\n entity.setValue(Follower, 'needsPositionSync', true);\n }\n } else {\n // First application after adoption: build the requested recipe from\n // scratch (the entity carries no dock components we own yet).\n const recipe = recipeFor(requested);\n if (recipe.follower && !entity.hasComponent(Follower)) {\n this.addFollower(entity);\n }\n if (!recipe.follower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (recipe.screenSpace && !entity.hasComponent(ScreenSpace)) {\n entity.addComponent(ScreenSpace);\n }\n if (!recipe.screenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n }\n\n entity.setValue(UIWindowState, 'appliedDockMode', requested);\n\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.manager.has(id)) {\n this.manager.setDockMode(id, requested);\n }\n }\n\n private faceViewer(entity: Entity): void {\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.windowPosition);\n this.camera.getWorldPosition(this.viewerPosition);\n const yaw = faceViewerYaw(\n [this.windowPosition.x, this.windowPosition.y, this.windowPosition.z],\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n object.rotation.y,\n );\n object.rotation.set(0, yaw, 0);\n }\n\n private offsetHelper = new Vector3();\n private yawQuaternion = new Quaternion();\n private yawEuler = new Euler();\n\n private addFollower(entity: Entity, options: { fromCurrentPose?: boolean } = {}): void {\n // The record's follow settings (`manager.setFollow` may have changed them\n // since spawn); the component's own values only before a record exists.\n const record = this.manager.get(entity.getValue(UIWindow, 'windowId') as string);\n const configured = entity.getVectorView(UIWindow, 'followOffset');\n let offset: [number, number, number] = record\n ? [record.follow.offset[0], record.follow.offset[1], record.follow.offset[2]]\n : [configured[0] ?? 0, configured[1] ?? 0, configured[2] ?? 0];\n\n const object = entity.object3D;\n if (options.fromCurrentPose && object) {\n // Current window position in the viewer's yaw-local frame (matching\n // how Follower's PivotY applies offsets), so following resumes from\n // the same bearing and distance the window was unpinned at.\n object.getWorldPosition(this.offsetHelper);\n this.camera.getWorldPosition(this.windowPosition);\n this.camera.getWorldQuaternion(this.yawQuaternion);\n this.yawEuler.setFromQuaternion(this.yawQuaternion, 'YXZ');\n this.yawEuler.x = 0;\n this.yawEuler.z = 0;\n this.yawQuaternion.setFromEuler(this.yawEuler).conjugate();\n this.offsetHelper.sub(this.windowPosition).applyQuaternion(this.yawQuaternion);\n // Degenerate (window at the viewer): keep the configured offset.\n if (this.offsetHelper.lengthSq() > 0.01) {\n offset = [this.offsetHelper.x, this.offsetHelper.y, this.offsetHelper.z];\n }\n }\n\n entity.addComponent(Follower, {\n // The camera, not player.head: three.js syncs the camera to the\n // headset pose in-session, while the head rig sits at the origin\n // outside XR (desktop) - following it would park windows at the floor.\n target: this.camera,\n offsetPosition: offset,\n behavior: FollowBehavior.PivotY,\n speed: record ? record.follow.speed : (entity.getValue(UIWindow, 'followSpeed') as number),\n tolerance: record ? record.follow.tolerance : (entity.getValue(UIWindow, 'followTolerance') as number),\n });\n }\n\n /** `manager.setFollow` after spawn: the live Follower takes the new offset and tuning at once. */\n private applyFollowChange(windowId: string): void {\n const record = this.manager.get(windowId);\n if (!record) return;\n for (const entity of this.queries.windows.entities) {\n if (entity.getValue(UIWindow, 'windowId') !== windowId || !entity.hasComponent(Follower)) continue;\n const offset = entity.getVectorView(Follower, 'offsetPosition');\n offset[0] = record.follow.offset[0];\n offset[1] = record.follow.offset[1];\n offset[2] = record.follow.offset[2];\n entity.setValue(Follower, 'speed', record.follow.speed);\n entity.setValue(Follower, 'tolerance', record.follow.tolerance);\n }\n }\n}\n"]}
@@ -169,7 +169,7 @@ declare const UIDragSystem_base: import("elics").SystemConstructor<{
169
169
  readonly Column: "column";
170
170
  readonly Grid: "grid";
171
171
  };
172
- default: "column";
172
+ default: import("@realitycollective/webxr-uiextensions").RegionFlow;
173
173
  };
174
174
  pitch: {
175
175
  type: Types.Float32;
@@ -359,7 +359,7 @@ declare const UIDragSystem_base: import("elics").SystemConstructor<{
359
359
  readonly Column: "column";
360
360
  readonly Grid: "grid";
361
361
  };
362
- default: "column";
362
+ default: import("@realitycollective/webxr-uiextensions").RegionFlow;
363
363
  };
364
364
  pitch: {
365
365
  type: Types.Float32;
@@ -105,12 +105,18 @@ export class UIWindowSystem extends createSystem({
105
105
  : '';
106
106
  this.entitiesById.set(id, entity);
107
107
  if (!this.manager.has(id)) {
108
+ const followOffset = entity.getVectorView(UIWindow, 'followOffset');
108
109
  this.manager.open(id, {
109
110
  title,
110
111
  dockMode,
111
112
  ...(region ? { region } : {}),
112
113
  chrome: this.chromeFlagsOf(entity),
113
114
  handMenu: this.handMenuOf(entity),
115
+ follow: {
116
+ offset: [followOffset[0] ?? 0, followOffset[1] ?? 0, followOffset[2] ?? 0],
117
+ speed: entity.getValue(UIWindow, 'followSpeed'),
118
+ tolerance: entity.getValue(UIWindow, 'followTolerance'),
119
+ },
114
120
  });
115
121
  }
116
122
  this.wireChrome(entity, id, title);
@@ -1 +1 @@
1
- {"version":3,"file":"window-system.js","sourceRoot":"","sources":["../../src/systems/window-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIWindowSystem - wires window chrome, keeps the `WindowManager` in sync\n * with entities, and applies every manager event to the ECS: focus\n * ordering, minimize, hide/show, region docking, return-home, chrome\n * enablement and close.\n *\n * The manager is the API app code drives (a hand menu calls\n * `manager.hide(id)` or `manager.dockTo(id, region)`); this system is where\n * those calls become entity changes. The reverse direction is kept in step\n * too: a title-bar drag that docks a window, or a dock-mode change applied\n * by `UIDockSystem`, is written back into the record, so the record is\n * always what the scene shows.\n *\n * Reuse over recreation: the panel itself is loaded/rendered by the IWSDK's\n * `PanelUISystem`; this system only discovers the chrome elements by their\n * well-known ids and drives them.\n */\nimport {\n PanelDocument,\n PanelUI,\n PokeInteractable,\n RayInteractable,\n UIKitDocument,\n type Entity,\n} from '@iwsdk/core';\nimport { createSystem } from '../create-system.js';\nimport { Vector3 } from 'three';\nimport { UIDockedTo, UIWindow, UIWindowState } from '../components.js';\nimport { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport {\n minimizeLabelFor,\n pinLabelFor,\n type HandMenuOptions,\n type WindowChrome,\n type WindowManager,\n type WindowRecord,\n} from '@realitycollective/webxr-uiextensions';\nimport { DockMode, isDockMode, type DockModeValue } from '@realitycollective/webxr-uiextensions';\n\ntype UikitElement = {\n addEventListener: (type: string, listener: (event?: unknown) => void) => void;\n setProperties: (props: Record<string, unknown>) => void;\n};\n\n/** Which IWSDK interaction tags were stripped while not presented, so only those come back. */\ninterface HiddenTags {\n ray: boolean;\n poke: boolean;\n}\n\nlet autoId = 0;\n\nexport class UIWindowSystem extends createSystem({\n pendingWindows: {\n required: [UIWindow, PanelUI, PanelDocument],\n excluded: [UIWindowState],\n },\n windows: { required: [UIWindow, UIWindowState, PanelDocument] },\n}) {\n private manager!: WindowManager;\n private entitiesById = new Map<string, Entity>();\n private hiddenTags = new Map<string, HiddenTags>();\n private localCamera = new Vector3();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n\n this.cleanupFuncs.push(\n this.queries.pendingWindows.subscribe('qualify', (entity) => {\n this.adoptWindow(entity);\n }),\n this.queries.windows.subscribe('disqualify', (entity) => {\n this.releaseWindow(entity);\n }),\n this.manager.events.on('closed', ({ id }) => this.destroyWindow(id)),\n this.manager.events.on('minimized', ({ id }) => this.applyMinimized(id, true)),\n this.manager.events.on('restored', ({ id }) => this.applyMinimized(id, false)),\n this.manager.events.on('minimized', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('restored', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('hidden', ({ id }) => this.reconcilePresentation(id)),\n this.manager.events.on('shown', ({ id }) => this.reconcilePresentation(id)),\n this.manager.events.on('dockChanged', ({ window }) => {\n const entity = this.entitiesById.get(window.id);\n if (entity && entity.getValue(UIWindow, 'dockMode') !== window.dockMode) {\n entity.setValue(UIWindow, 'dockMode', window.dockMode);\n }\n this.syncPinLabel(window);\n }),\n this.manager.events.on('regionChanged', ({ window }) => this.applyRegion(window)),\n this.manager.events.on('returnHome', ({ id }) => {\n const entity = this.entitiesById.get(id);\n if (entity) {\n this.returnHome(entity);\n }\n }),\n this.manager.events.on('chromeChanged', ({ window }) => this.applyChrome(window)),\n this.manager.events.on('handMenuChanged', ({ window }) => this.applyHandMenu(window)),\n this.manager.events.on('dragStarted', (window) => this.syncPinLabel(window)),\n this.manager.events.on('dragEnded', (window) => this.syncPinLabel(window)),\n );\n }\n\n override update(): void {\n // Focus bias: nudge each window's document toward the viewer by a small\n // amount inversely related to focus depth, so overlapping windows resolve\n // in focus order. Computed in entity-local space so it holds for any\n // window orientation.\n for (const entity of this.queries.windows.entities) {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const record = this.manager.get(id);\n if (!record) {\n continue;\n }\n // The palm gate (UIDockSystem) and hide() both feed one decision.\n if (!this.reconcilePresentation(id)) {\n continue;\n }\n const document = this.documentOf(entity);\n const object = entity.object3D;\n if (!document || !object) {\n continue;\n }\n const bias = entity.getValue(UIWindow, 'focusBias') as number;\n const depth = this.manager.orderOf(id);\n const amount = bias * Math.max(0, this.manager.count - 1 - depth);\n if (amount === 0) {\n document.position.set(0, 0, 0);\n continue;\n }\n this.camera.getWorldPosition(this.localCamera);\n object.worldToLocal(this.localCamera);\n if (this.localCamera.lengthSq() === 0) {\n continue;\n }\n document.position.copy(this.localCamera.normalize().multiplyScalar(amount));\n }\n }\n\n entityFor(windowId: string): Entity | undefined {\n return this.entitiesById.get(windowId);\n }\n\n private adoptWindow(entity: Entity): void {\n let id = entity.getValue(UIWindow, 'windowId') as string;\n if (!id) {\n id = `uix-window-${autoId++}`;\n entity.setValue(UIWindow, 'windowId', id);\n }\n const title = entity.getValue(UIWindow, 'title') as string;\n const dockMode = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n const region = entity.hasComponent(UIDockedTo)\n ? (entity.getValue(UIDockedTo, 'regionId') as string)\n : '';\n this.entitiesById.set(id, entity);\n if (!this.manager.has(id)) {\n this.manager.open(id, {\n title,\n dockMode,\n ...(region ? { region } : {}),\n chrome: this.chromeFlagsOf(entity),\n handMenu: this.handMenuOf(entity),\n });\n }\n this.wireChrome(entity, id, title);\n\n // Snapshot \"home\" so the DOCK button can return the window: the spawn\n // region if it was born docked, otherwise its placement + dock mode.\n const object = entity.object3D;\n entity.addComponent(UIWindowState, {\n chromeWired: true,\n appliedDockMode: '',\n homeDockMode: dockMode,\n homeRegion: region,\n homePosition: object ? [object.position.x, object.position.y, object.position.z] : [0, 0, 0],\n homeYaw: object ? object.rotation.y : 0,\n });\n const record = this.manager.get(id);\n if (record) {\n this.applyChrome(record);\n this.applyHandMenu(record);\n this.syncPinLabel(record);\n this.syncMinimizeLabel(record);\n if (record.minimized) {\n this.applyMinimized(id, true);\n }\n this.reconcilePresentation(id);\n }\n }\n\n /** The hand-menu fields on the component, in the manager's vocabulary. */\n private handMenuOf(entity: Entity): HandMenuOptions {\n const offset = entity.getVectorView(UIWindow, 'handOffset');\n return {\n hand: entity.getValue(UIWindow, 'hand') as HandMenuOptions['hand'],\n anchor: entity.getValue(UIWindow, 'handAnchor') as HandMenuOptions['anchor'],\n anchorDistance: entity.getValue(UIWindow, 'handAnchorDistance') as number,\n offset: [offset[0] ?? 0, offset[1] ?? 0, offset[2] ?? 0],\n palmGate: Boolean(entity.getValue(UIWindow, 'palmGate')),\n palmAngle: entity.getValue(UIWindow, 'palmAngle') as number,\n };\n }\n\n /** Keep the component's hand-menu fields equal to the record. */\n private applyHandMenu(record: Pick<WindowRecord, 'id' | 'handMenu'>): void {\n const entity = this.entitiesById.get(record.id);\n if (!entity) {\n return;\n }\n const { handMenu } = record;\n entity.setValue(UIWindow, 'hand', handMenu.hand);\n entity.setValue(UIWindow, 'handAnchor', handMenu.anchor);\n entity.setValue(UIWindow, 'handAnchorDistance', handMenu.anchorDistance);\n entity.getVectorView(UIWindow, 'handOffset').set(handMenu.offset);\n entity.setValue(UIWindow, 'palmGate', handMenu.palmGate);\n entity.setValue(UIWindow, 'palmAngle', handMenu.palmAngle);\n }\n\n /** Return a window to its home: spawn region, or original placement/mode. */\n returnHome(entity: Entity): void {\n const homeRegion = entity.getValue(UIWindowState, 'homeRegion') as string;\n if (homeRegion) {\n if (\n entity.hasComponent(UIDockedTo) &&\n entity.getValue(UIDockedTo, 'regionId') === homeRegion\n ) {\n return; // already home\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n entity.addComponent(UIDockedTo, { regionId: homeRegion });\n return;\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n const homeDockMode = entity.getValue(UIWindowState, 'homeDockMode') as string;\n if (isDockMode(homeDockMode)) {\n entity.setValue(UIWindow, 'dockMode', homeDockMode);\n }\n if (homeDockMode === DockMode.WorldLocked) {\n const object = entity.object3D;\n const home = entity.getVectorView(UIWindowState, 'homePosition');\n if (object) {\n object.position.set(home[0] ?? 0, home[1] ?? 0, home[2] ?? 0);\n object.rotation.set(0, entity.getValue(UIWindowState, 'homeYaw') as number, 0);\n }\n }\n // Follow modes re-snap on their own (the dock system syncs the Follower).\n }\n\n /** The chrome flags on the component, in the manager's vocabulary. */\n private chromeFlagsOf(entity: Entity): WindowChrome {\n return {\n close: Boolean(entity.getValue(UIWindow, 'closable')),\n minimize: Boolean(entity.getValue(UIWindow, 'minimizable')),\n pin: Boolean(entity.getValue(UIWindow, 'pinnable')),\n dock: Boolean(entity.getValue(UIWindow, 'dockable')),\n };\n }\n\n /**\n * Show exactly the enabled buttons, and keep the component flags equal to\n * the record so code reading either sees one answer.\n */\n private applyChrome(record: Pick<WindowRecord, 'id' | 'chrome'>): void {\n const entity = this.entitiesById.get(record.id);\n if (!entity) {\n return;\n }\n const { chrome } = record;\n entity.setValue(UIWindow, 'closable', chrome.close);\n entity.setValue(UIWindow, 'minimizable', chrome.minimize);\n entity.setValue(UIWindow, 'pinnable', chrome.pin);\n entity.setValue(UIWindow, 'dockable', chrome.dock);\n const document = this.documentOf(entity);\n if (!document) {\n return;\n }\n const buttons: Array<[string, boolean]> = [\n [WINDOW_CHROME_IDS.close, chrome.close],\n [WINDOW_CHROME_IDS.minimize, chrome.minimize],\n [WINDOW_CHROME_IDS.pin, chrome.pin],\n [WINDOW_CHROME_IDS.dock, chrome.dock],\n ];\n for (const [elementId, enabled] of buttons) {\n (document.getElementById(elementId) as UikitElement | undefined)?.setProperties({\n display: enabled ? 'flex' : 'none',\n });\n }\n }\n\n /** MIN when open, MAX when minimized - the label names the next action. */\n private syncMinimizeLabel(record: Pick<WindowRecord, 'id' | 'minimized'>): void {\n const entity = this.entitiesById.get(record.id);\n const minimize = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.minimize) as\n | UikitElement\n | undefined)\n : undefined;\n minimize?.setProperties({ text: minimizeLabelFor(record) });\n }\n\n private syncPinLabel(record: Pick<WindowRecord, 'id' | 'dockMode' | 'dragging'>): void {\n const entity = this.entitiesById.get(record.id);\n const pin = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.pin) as\n | UikitElement\n | undefined)\n : undefined;\n pin?.setProperties({ text: pinLabelFor(record) });\n }\n\n /** The entity went away (destroyed, or lost its panel): close the record. */\n private releaseWindow(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.entitiesById.get(id) === entity) {\n this.entitiesById.delete(id);\n this.hiddenTags.delete(id);\n if (this.manager.has(id)) {\n this.manager.close(id);\n }\n }\n }\n\n /** The record was closed through the manager: tear the entity down. */\n private destroyWindow(id: string): void {\n const entity = this.entitiesById.get(id);\n if (!entity) {\n return; // closed from the entity side; releaseWindow already ran\n }\n this.entitiesById.delete(id);\n this.hiddenTags.delete(id);\n entity.destroy();\n }\n\n /**\n * Draw and expose the window exactly when the record is not hidden AND the\n * palm gate (for a hand menu) is open. Idempotent: `UIWindowState.presented`\n * records what was last applied, so this is cheap to call every frame.\n * Returns whether the window is presented.\n */\n private reconcilePresentation(id: string): boolean {\n const entity = this.entitiesById.get(id);\n const record = this.manager.get(id);\n if (!entity || !record) {\n return false;\n }\n const desired = !record.hidden && Boolean(entity.getValue(UIWindowState, 'gateOpen'));\n if (Boolean(entity.getValue(UIWindowState, 'presented')) === desired) {\n return desired;\n }\n entity.setValue(UIWindowState, 'presented', desired);\n const object = entity.object3D;\n if (object) {\n object.visible = desired;\n }\n if (!desired) {\n // Strip the interaction tags so no pointer can reach the window, and\n // remember which ones were there so only those come back.\n const tags: HiddenTags = {\n ray: entity.hasComponent(RayInteractable),\n poke: entity.hasComponent(PokeInteractable),\n };\n if (tags.ray) {\n entity.removeComponent(RayInteractable);\n }\n if (tags.poke) {\n entity.removeComponent(PokeInteractable);\n }\n this.hiddenTags.set(id, tags);\n return false;\n }\n const tags = this.hiddenTags.get(id);\n this.hiddenTags.delete(id);\n if (tags?.ray && !entity.hasComponent(RayInteractable)) {\n entity.addComponent(RayInteractable);\n }\n if (tags?.poke && !entity.hasComponent(PokeInteractable)) {\n entity.addComponent(PokeInteractable);\n }\n return true;\n }\n\n /** Make the entity's `UIDockedTo` agree with the record's region. */\n private applyRegion(record: Pick<WindowRecord, 'id' | 'region'>): void {\n const entity = this.entitiesById.get(record.id);\n if (!entity) {\n return;\n }\n const current = entity.hasComponent(UIDockedTo)\n ? (entity.getValue(UIDockedTo, 'regionId') as string)\n : undefined;\n if (current === record.region) {\n return; // the ECS already shows it (drag-drop docked, or a write-back)\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n if (record.region !== undefined) {\n entity.addComponent(UIDockedTo, { regionId: record.region });\n }\n }\n\n private wireChrome(entity: Entity, id: string, title: string): void {\n const document = this.documentOf(entity);\n if (!document) {\n return;\n }\n\n // IWSDK 0.5 removed PanelUI.maxWidth/maxHeight; world size is now the\n // document's own concern. Fit into the requested box, preserving aspect\n // ratio. Zero means \"leave intrinsic\" so the entity transform owns scale.\n const targetWidth = entity.getValue(UIWindow, 'targetWidth') ?? 0;\n const targetHeight = entity.getValue(UIWindow, 'targetHeight') ?? 0;\n if (targetWidth > 0 && targetHeight > 0) {\n document.setTargetDimensions(targetWidth, targetHeight);\n }\n\n const element = (elementId: string): UikitElement | undefined =>\n document.getElementById(elementId) as UikitElement | undefined;\n\n const titleEl = element(WINDOW_CHROME_IDS.title);\n if (titleEl && title) {\n titleEl.setProperties({ text: title });\n }\n\n // Chrome buttons live on the drag surface - keep their presses out of\n // the title-bar drag handle so clicks never turn into (or arm) drags.\n const swallowPress = (button: UikitElement | undefined): void => {\n button?.addEventListener('pointerdown', (event) => {\n (event as { stopPropagation?: () => void } | undefined)?.stopPropagation?.();\n });\n };\n swallowPress(element(WINDOW_CHROME_IDS.pin));\n swallowPress(element(WINDOW_CHROME_IDS.dock));\n swallowPress(element(WINDOW_CHROME_IDS.minimize));\n swallowPress(element(WINDOW_CHROME_IDS.close));\n\n // Any press anywhere on the window brings it to the front.\n const root = element(WINDOW_CHROME_IDS.window);\n root?.addEventListener('pointerdown', () => {\n if (this.manager.has(id)) {\n this.manager.focus(id);\n }\n });\n\n // Every button is wired once and gated on the record at click time, so\n // enabling a button later (manager.setChrome) needs no rewiring.\n const enabled = (key: keyof WindowChrome): boolean =>\n this.manager.get(id)?.chrome[key] === true;\n\n element(WINDOW_CHROME_IDS.close)?.addEventListener('click', () => {\n if (enabled('close')) {\n // One teardown path: the manager closes, destroyWindow removes the\n // entity, releaseWindow sees the record is already gone.\n this.manager.close(id);\n }\n });\n element(WINDOW_CHROME_IDS.minimize)?.addEventListener('click', () => {\n if (enabled('minimize')) {\n this.manager.toggleMinimized(id);\n }\n });\n element(WINDOW_CHROME_IDS.pin)?.addEventListener('click', () => {\n if (enabled('pin')) {\n this.manager.togglePin(id);\n }\n });\n element(WINDOW_CHROME_IDS.dock)?.addEventListener('click', () => {\n if (enabled('dock')) {\n this.manager.returnHome(id);\n }\n });\n }\n\n private applyMinimized(id: string, minimized: boolean): void {\n const entity = this.entitiesById.get(id);\n const document = entity ? this.documentOf(entity) : undefined;\n const content = document?.getElementById(WINDOW_CHROME_IDS.content) as\n | UikitElement\n | undefined;\n content?.setProperties({ display: minimized ? 'none' : 'flex' });\n }\n\n private documentOf(entity: Entity): UIKitDocument | undefined {\n return PanelDocument.data.document[entity.index] as UIKitDocument | undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"window-system.js","sourceRoot":"","sources":["../../src/systems/window-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIWindowSystem - wires window chrome, keeps the `WindowManager` in sync\n * with entities, and applies every manager event to the ECS: focus\n * ordering, minimize, hide/show, region docking, return-home, chrome\n * enablement and close.\n *\n * The manager is the API app code drives (a hand menu calls\n * `manager.hide(id)` or `manager.dockTo(id, region)`); this system is where\n * those calls become entity changes. The reverse direction is kept in step\n * too: a title-bar drag that docks a window, or a dock-mode change applied\n * by `UIDockSystem`, is written back into the record, so the record is\n * always what the scene shows.\n *\n * Reuse over recreation: the panel itself is loaded/rendered by the IWSDK's\n * `PanelUISystem`; this system only discovers the chrome elements by their\n * well-known ids and drives them.\n */\nimport {\n PanelDocument,\n PanelUI,\n PokeInteractable,\n RayInteractable,\n UIKitDocument,\n type Entity,\n} from '@iwsdk/core';\nimport { createSystem } from '../create-system.js';\nimport { Vector3 } from 'three';\nimport { UIDockedTo, UIWindow, UIWindowState } from '../components.js';\nimport { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport {\n minimizeLabelFor,\n pinLabelFor,\n type HandMenuOptions,\n type WindowChrome,\n type WindowManager,\n type WindowRecord,\n} from '@realitycollective/webxr-uiextensions';\nimport { DockMode, isDockMode, type DockModeValue } from '@realitycollective/webxr-uiextensions';\n\ntype UikitElement = {\n addEventListener: (type: string, listener: (event?: unknown) => void) => void;\n setProperties: (props: Record<string, unknown>) => void;\n};\n\n/** Which IWSDK interaction tags were stripped while not presented, so only those come back. */\ninterface HiddenTags {\n ray: boolean;\n poke: boolean;\n}\n\nlet autoId = 0;\n\nexport class UIWindowSystem extends createSystem({\n pendingWindows: {\n required: [UIWindow, PanelUI, PanelDocument],\n excluded: [UIWindowState],\n },\n windows: { required: [UIWindow, UIWindowState, PanelDocument] },\n}) {\n private manager!: WindowManager;\n private entitiesById = new Map<string, Entity>();\n private hiddenTags = new Map<string, HiddenTags>();\n private localCamera = new Vector3();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n\n this.cleanupFuncs.push(\n this.queries.pendingWindows.subscribe('qualify', (entity) => {\n this.adoptWindow(entity);\n }),\n this.queries.windows.subscribe('disqualify', (entity) => {\n this.releaseWindow(entity);\n }),\n this.manager.events.on('closed', ({ id }) => this.destroyWindow(id)),\n this.manager.events.on('minimized', ({ id }) => this.applyMinimized(id, true)),\n this.manager.events.on('restored', ({ id }) => this.applyMinimized(id, false)),\n this.manager.events.on('minimized', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('restored', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('hidden', ({ id }) => this.reconcilePresentation(id)),\n this.manager.events.on('shown', ({ id }) => this.reconcilePresentation(id)),\n this.manager.events.on('dockChanged', ({ window }) => {\n const entity = this.entitiesById.get(window.id);\n if (entity && entity.getValue(UIWindow, 'dockMode') !== window.dockMode) {\n entity.setValue(UIWindow, 'dockMode', window.dockMode);\n }\n this.syncPinLabel(window);\n }),\n this.manager.events.on('regionChanged', ({ window }) => this.applyRegion(window)),\n this.manager.events.on('returnHome', ({ id }) => {\n const entity = this.entitiesById.get(id);\n if (entity) {\n this.returnHome(entity);\n }\n }),\n this.manager.events.on('chromeChanged', ({ window }) => this.applyChrome(window)),\n this.manager.events.on('handMenuChanged', ({ window }) => this.applyHandMenu(window)),\n this.manager.events.on('dragStarted', (window) => this.syncPinLabel(window)),\n this.manager.events.on('dragEnded', (window) => this.syncPinLabel(window)),\n );\n }\n\n override update(): void {\n // Focus bias: nudge each window's document toward the viewer by a small\n // amount inversely related to focus depth, so overlapping windows resolve\n // in focus order. Computed in entity-local space so it holds for any\n // window orientation.\n for (const entity of this.queries.windows.entities) {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const record = this.manager.get(id);\n if (!record) {\n continue;\n }\n // The palm gate (UIDockSystem) and hide() both feed one decision.\n if (!this.reconcilePresentation(id)) {\n continue;\n }\n const document = this.documentOf(entity);\n const object = entity.object3D;\n if (!document || !object) {\n continue;\n }\n const bias = entity.getValue(UIWindow, 'focusBias') as number;\n const depth = this.manager.orderOf(id);\n const amount = bias * Math.max(0, this.manager.count - 1 - depth);\n if (amount === 0) {\n document.position.set(0, 0, 0);\n continue;\n }\n this.camera.getWorldPosition(this.localCamera);\n object.worldToLocal(this.localCamera);\n if (this.localCamera.lengthSq() === 0) {\n continue;\n }\n document.position.copy(this.localCamera.normalize().multiplyScalar(amount));\n }\n }\n\n entityFor(windowId: string): Entity | undefined {\n return this.entitiesById.get(windowId);\n }\n\n private adoptWindow(entity: Entity): void {\n let id = entity.getValue(UIWindow, 'windowId') as string;\n if (!id) {\n id = `uix-window-${autoId++}`;\n entity.setValue(UIWindow, 'windowId', id);\n }\n const title = entity.getValue(UIWindow, 'title') as string;\n const dockMode = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n const region = entity.hasComponent(UIDockedTo)\n ? (entity.getValue(UIDockedTo, 'regionId') as string)\n : '';\n this.entitiesById.set(id, entity);\n if (!this.manager.has(id)) {\n const followOffset = entity.getVectorView(UIWindow, 'followOffset');\n this.manager.open(id, {\n title,\n dockMode,\n ...(region ? { region } : {}),\n chrome: this.chromeFlagsOf(entity),\n handMenu: this.handMenuOf(entity),\n follow: {\n offset: [followOffset[0] ?? 0, followOffset[1] ?? 0, followOffset[2] ?? 0],\n speed: entity.getValue(UIWindow, 'followSpeed') as number,\n tolerance: entity.getValue(UIWindow, 'followTolerance') as number,\n },\n });\n }\n this.wireChrome(entity, id, title);\n\n // Snapshot \"home\" so the DOCK button can return the window: the spawn\n // region if it was born docked, otherwise its placement + dock mode.\n const object = entity.object3D;\n entity.addComponent(UIWindowState, {\n chromeWired: true,\n appliedDockMode: '',\n homeDockMode: dockMode,\n homeRegion: region,\n homePosition: object ? [object.position.x, object.position.y, object.position.z] : [0, 0, 0],\n homeYaw: object ? object.rotation.y : 0,\n });\n const record = this.manager.get(id);\n if (record) {\n this.applyChrome(record);\n this.applyHandMenu(record);\n this.syncPinLabel(record);\n this.syncMinimizeLabel(record);\n if (record.minimized) {\n this.applyMinimized(id, true);\n }\n this.reconcilePresentation(id);\n }\n }\n\n /** The hand-menu fields on the component, in the manager's vocabulary. */\n private handMenuOf(entity: Entity): HandMenuOptions {\n const offset = entity.getVectorView(UIWindow, 'handOffset');\n return {\n hand: entity.getValue(UIWindow, 'hand') as HandMenuOptions['hand'],\n anchor: entity.getValue(UIWindow, 'handAnchor') as HandMenuOptions['anchor'],\n anchorDistance: entity.getValue(UIWindow, 'handAnchorDistance') as number,\n offset: [offset[0] ?? 0, offset[1] ?? 0, offset[2] ?? 0],\n palmGate: Boolean(entity.getValue(UIWindow, 'palmGate')),\n palmAngle: entity.getValue(UIWindow, 'palmAngle') as number,\n };\n }\n\n /** Keep the component's hand-menu fields equal to the record. */\n private applyHandMenu(record: Pick<WindowRecord, 'id' | 'handMenu'>): void {\n const entity = this.entitiesById.get(record.id);\n if (!entity) {\n return;\n }\n const { handMenu } = record;\n entity.setValue(UIWindow, 'hand', handMenu.hand);\n entity.setValue(UIWindow, 'handAnchor', handMenu.anchor);\n entity.setValue(UIWindow, 'handAnchorDistance', handMenu.anchorDistance);\n entity.getVectorView(UIWindow, 'handOffset').set(handMenu.offset);\n entity.setValue(UIWindow, 'palmGate', handMenu.palmGate);\n entity.setValue(UIWindow, 'palmAngle', handMenu.palmAngle);\n }\n\n /** Return a window to its home: spawn region, or original placement/mode. */\n returnHome(entity: Entity): void {\n const homeRegion = entity.getValue(UIWindowState, 'homeRegion') as string;\n if (homeRegion) {\n if (\n entity.hasComponent(UIDockedTo) &&\n entity.getValue(UIDockedTo, 'regionId') === homeRegion\n ) {\n return; // already home\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n entity.addComponent(UIDockedTo, { regionId: homeRegion });\n return;\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n const homeDockMode = entity.getValue(UIWindowState, 'homeDockMode') as string;\n if (isDockMode(homeDockMode)) {\n entity.setValue(UIWindow, 'dockMode', homeDockMode);\n }\n if (homeDockMode === DockMode.WorldLocked) {\n const object = entity.object3D;\n const home = entity.getVectorView(UIWindowState, 'homePosition');\n if (object) {\n object.position.set(home[0] ?? 0, home[1] ?? 0, home[2] ?? 0);\n object.rotation.set(0, entity.getValue(UIWindowState, 'homeYaw') as number, 0);\n }\n }\n // Follow modes re-snap on their own (the dock system syncs the Follower).\n }\n\n /** The chrome flags on the component, in the manager's vocabulary. */\n private chromeFlagsOf(entity: Entity): WindowChrome {\n return {\n close: Boolean(entity.getValue(UIWindow, 'closable')),\n minimize: Boolean(entity.getValue(UIWindow, 'minimizable')),\n pin: Boolean(entity.getValue(UIWindow, 'pinnable')),\n dock: Boolean(entity.getValue(UIWindow, 'dockable')),\n };\n }\n\n /**\n * Show exactly the enabled buttons, and keep the component flags equal to\n * the record so code reading either sees one answer.\n */\n private applyChrome(record: Pick<WindowRecord, 'id' | 'chrome'>): void {\n const entity = this.entitiesById.get(record.id);\n if (!entity) {\n return;\n }\n const { chrome } = record;\n entity.setValue(UIWindow, 'closable', chrome.close);\n entity.setValue(UIWindow, 'minimizable', chrome.minimize);\n entity.setValue(UIWindow, 'pinnable', chrome.pin);\n entity.setValue(UIWindow, 'dockable', chrome.dock);\n const document = this.documentOf(entity);\n if (!document) {\n return;\n }\n const buttons: Array<[string, boolean]> = [\n [WINDOW_CHROME_IDS.close, chrome.close],\n [WINDOW_CHROME_IDS.minimize, chrome.minimize],\n [WINDOW_CHROME_IDS.pin, chrome.pin],\n [WINDOW_CHROME_IDS.dock, chrome.dock],\n ];\n for (const [elementId, enabled] of buttons) {\n (document.getElementById(elementId) as UikitElement | undefined)?.setProperties({\n display: enabled ? 'flex' : 'none',\n });\n }\n }\n\n /** MIN when open, MAX when minimized - the label names the next action. */\n private syncMinimizeLabel(record: Pick<WindowRecord, 'id' | 'minimized'>): void {\n const entity = this.entitiesById.get(record.id);\n const minimize = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.minimize) as\n | UikitElement\n | undefined)\n : undefined;\n minimize?.setProperties({ text: minimizeLabelFor(record) });\n }\n\n private syncPinLabel(record: Pick<WindowRecord, 'id' | 'dockMode' | 'dragging'>): void {\n const entity = this.entitiesById.get(record.id);\n const pin = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.pin) as\n | UikitElement\n | undefined)\n : undefined;\n pin?.setProperties({ text: pinLabelFor(record) });\n }\n\n /** The entity went away (destroyed, or lost its panel): close the record. */\n private releaseWindow(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.entitiesById.get(id) === entity) {\n this.entitiesById.delete(id);\n this.hiddenTags.delete(id);\n if (this.manager.has(id)) {\n this.manager.close(id);\n }\n }\n }\n\n /** The record was closed through the manager: tear the entity down. */\n private destroyWindow(id: string): void {\n const entity = this.entitiesById.get(id);\n if (!entity) {\n return; // closed from the entity side; releaseWindow already ran\n }\n this.entitiesById.delete(id);\n this.hiddenTags.delete(id);\n entity.destroy();\n }\n\n /**\n * Draw and expose the window exactly when the record is not hidden AND the\n * palm gate (for a hand menu) is open. Idempotent: `UIWindowState.presented`\n * records what was last applied, so this is cheap to call every frame.\n * Returns whether the window is presented.\n */\n private reconcilePresentation(id: string): boolean {\n const entity = this.entitiesById.get(id);\n const record = this.manager.get(id);\n if (!entity || !record) {\n return false;\n }\n const desired = !record.hidden && Boolean(entity.getValue(UIWindowState, 'gateOpen'));\n if (Boolean(entity.getValue(UIWindowState, 'presented')) === desired) {\n return desired;\n }\n entity.setValue(UIWindowState, 'presented', desired);\n const object = entity.object3D;\n if (object) {\n object.visible = desired;\n }\n if (!desired) {\n // Strip the interaction tags so no pointer can reach the window, and\n // remember which ones were there so only those come back.\n const tags: HiddenTags = {\n ray: entity.hasComponent(RayInteractable),\n poke: entity.hasComponent(PokeInteractable),\n };\n if (tags.ray) {\n entity.removeComponent(RayInteractable);\n }\n if (tags.poke) {\n entity.removeComponent(PokeInteractable);\n }\n this.hiddenTags.set(id, tags);\n return false;\n }\n const tags = this.hiddenTags.get(id);\n this.hiddenTags.delete(id);\n if (tags?.ray && !entity.hasComponent(RayInteractable)) {\n entity.addComponent(RayInteractable);\n }\n if (tags?.poke && !entity.hasComponent(PokeInteractable)) {\n entity.addComponent(PokeInteractable);\n }\n return true;\n }\n\n /** Make the entity's `UIDockedTo` agree with the record's region. */\n private applyRegion(record: Pick<WindowRecord, 'id' | 'region'>): void {\n const entity = this.entitiesById.get(record.id);\n if (!entity) {\n return;\n }\n const current = entity.hasComponent(UIDockedTo)\n ? (entity.getValue(UIDockedTo, 'regionId') as string)\n : undefined;\n if (current === record.region) {\n return; // the ECS already shows it (drag-drop docked, or a write-back)\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n if (record.region !== undefined) {\n entity.addComponent(UIDockedTo, { regionId: record.region });\n }\n }\n\n private wireChrome(entity: Entity, id: string, title: string): void {\n const document = this.documentOf(entity);\n if (!document) {\n return;\n }\n\n // IWSDK 0.5 removed PanelUI.maxWidth/maxHeight; world size is now the\n // document's own concern. Fit into the requested box, preserving aspect\n // ratio. Zero means \"leave intrinsic\" so the entity transform owns scale.\n const targetWidth = entity.getValue(UIWindow, 'targetWidth') ?? 0;\n const targetHeight = entity.getValue(UIWindow, 'targetHeight') ?? 0;\n if (targetWidth > 0 && targetHeight > 0) {\n document.setTargetDimensions(targetWidth, targetHeight);\n }\n\n const element = (elementId: string): UikitElement | undefined =>\n document.getElementById(elementId) as UikitElement | undefined;\n\n const titleEl = element(WINDOW_CHROME_IDS.title);\n if (titleEl && title) {\n titleEl.setProperties({ text: title });\n }\n\n // Chrome buttons live on the drag surface - keep their presses out of\n // the title-bar drag handle so clicks never turn into (or arm) drags.\n const swallowPress = (button: UikitElement | undefined): void => {\n button?.addEventListener('pointerdown', (event) => {\n (event as { stopPropagation?: () => void } | undefined)?.stopPropagation?.();\n });\n };\n swallowPress(element(WINDOW_CHROME_IDS.pin));\n swallowPress(element(WINDOW_CHROME_IDS.dock));\n swallowPress(element(WINDOW_CHROME_IDS.minimize));\n swallowPress(element(WINDOW_CHROME_IDS.close));\n\n // Any press anywhere on the window brings it to the front.\n const root = element(WINDOW_CHROME_IDS.window);\n root?.addEventListener('pointerdown', () => {\n if (this.manager.has(id)) {\n this.manager.focus(id);\n }\n });\n\n // Every button is wired once and gated on the record at click time, so\n // enabling a button later (manager.setChrome) needs no rewiring.\n const enabled = (key: keyof WindowChrome): boolean =>\n this.manager.get(id)?.chrome[key] === true;\n\n element(WINDOW_CHROME_IDS.close)?.addEventListener('click', () => {\n if (enabled('close')) {\n // One teardown path: the manager closes, destroyWindow removes the\n // entity, releaseWindow sees the record is already gone.\n this.manager.close(id);\n }\n });\n element(WINDOW_CHROME_IDS.minimize)?.addEventListener('click', () => {\n if (enabled('minimize')) {\n this.manager.toggleMinimized(id);\n }\n });\n element(WINDOW_CHROME_IDS.pin)?.addEventListener('click', () => {\n if (enabled('pin')) {\n this.manager.togglePin(id);\n }\n });\n element(WINDOW_CHROME_IDS.dock)?.addEventListener('click', () => {\n if (enabled('dock')) {\n this.manager.returnHome(id);\n }\n });\n }\n\n private applyMinimized(id: string, minimized: boolean): void {\n const entity = this.entitiesById.get(id);\n const document = entity ? this.documentOf(entity) : undefined;\n const content = document?.getElementById(WINDOW_CHROME_IDS.content) as\n | UikitElement\n | undefined;\n content?.setProperties({ display: minimized ? 'none' : 'flex' });\n }\n\n private documentOf(entity: Entity): UIKitDocument | undefined {\n return PanelDocument.data.document[entity.index] as UIKitDocument | undefined;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realitycollective/iwsdk-uiextensions",
3
- "version": "0.1.1-preview.0",
3
+ "version": "0.1.1-preview.2",
4
4
  "description": "Meta IWSDK adapter for the Reality Collective UI Extensions - windowing, docking, layout regions and extra controls, binding the engine-free @realitycollective/webxr-uiextensions core onto IWSDK's ECS, UIKitML and interaction systems.",
5
5
  "keywords": [
6
6
  "realitycollective",
@@ -39,14 +39,14 @@
39
39
  "@drawcall/uikitml": "0.1.8",
40
40
  "zod": "^4.4.3",
41
41
  "@pmndrs/handle": "^6.6.16",
42
- "@realitycollective/webxr-uiextensions": "^0.1.1-preview.0"
42
+ "@realitycollective/webxr-uiextensions": "^0.1.1-preview.2"
43
43
  },
44
44
  "peerDependencies": {
45
- "@iwsdk/core": ">=0.5.0 <0.6.0",
45
+ "@iwsdk/core": ">=1.0.0 <2.0.0",
46
46
  "three": ">=0.170.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@iwsdk/core": "0.5.3",
49
+ "@iwsdk/core": "1.0.0",
50
50
  "three": "npm:super-three@0.181.0",
51
51
  "@types/three": "0.184.0"
52
52
  },