@realitycollective/iwsdk-uiextensions 0.1.1-preview.1 → 0.1.1
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 +21 -2
- package/README.md +7 -6
- package/dist/component-set.js +2 -22
- package/dist/component-set.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/register.d.ts +7 -0
- package/dist/register.js +5 -0
- package/dist/register.js.map +1 -1
- package/dist/systems/dock-system.d.ts +2 -0
- package/dist/systems/dock-system.js +25 -7
- package/dist/systems/dock-system.js.map +1 -1
- package/dist/systems/pointer-offer-system.d.ts +55 -0
- package/dist/systems/pointer-offer-system.js +98 -0
- package/dist/systems/pointer-offer-system.js.map +1 -0
- package/dist/systems/window-system.js +6 -0
- package/dist/systems/window-system.js.map +1 -1
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,24 @@ Change log for the Reality Collective WebXR UI Extensions packages. All five pac
|
|
|
4
4
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Preview builds are not listed separately. The entry for a version accumulates while its previews are published, and is dated when that version is released.
|
|
6
6
|
|
|
7
|
-
## [0.1.1]
|
|
7
|
+
## [0.1.1] - 2026-10-01
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
+
- Pointers for an app with windows and no interactables. IWSDK draws every hand's ray and cursor itself, whichever packages an app uses; a native host draws only what it is told, and nothing told it, so a native app with windows alone showed no ray and no cursor and could not be aimed.
|
|
12
|
+
- `@realitycollective/webxr-uiextensions` - `PanelPointerOffers.present(sourceKey, hasRay, selecting, display)` and `PresentedPointer`: the drawing for one source, the arbiter's visuals through the app's `PointerDisplay` settings, with what the cursor sits on. The rule it states: whoever owns the arbiter presents every source. With a shared arbiter that is the Interactions runtime; with its own, the window host.
|
|
13
|
+
- `@realitycollective/native-uiextensions` - `NativeWindowHost` presents while it owns the arbiter: once per frame for every source that reported a pointer sample it calls the `input` slice's `applyPointerVisuals`, and once more with nothing to draw when a source stops reporting. New option `pointerDisplay` (the settings, or a `PointerDisplay` the app keeps), `getPointerDisplay()`, and the optional `applyPointerVisuals` and `applyPointerDisplay` members on `NativeUiInputHost`, the same members `@realitycollective/native-interactions` defines on that slice. With `pointers` given nothing changes: the Interactions binding presents.
|
|
14
|
+
- `@realitycollective/webxr-uiextensions` - `UIX_CONTROL_TAGS`, `UIX_ROLE_TAGS` and `UIX_ELEMENT_TAGS`: the `<uix-*>` element vocabulary a panel loader must accept, published by the core. It was declared twice, privately, in the core's upgrader and in the IWSDK binding's component set; `uixComponentSet` now reads it, and the native contract states what a host's panel loader (a build-time cook or a run-time parser) must do with each tag.
|
|
15
|
+
- Native test harness (`harness/native`): a native app project for the WebXR-to-native conversion pipeline that runs `nativeUiHostConformanceCases()`, `windowHostContractCases()`, `sceneTargetContractCases()` and `uixElementContractCases()` against a native host's test slices (or this repository's reference fakes under Node), and plays the showcase scene over the live `__rcHost` with every window and control event logged as a JSON line. `npm run harness:ui` fills its panel folder from the showcase and the core's window chrome; `npm run harness:compile` typechecks it, bundles it through the engine and browser-global gates and compiles Hermes bytecode. CI compiles it and never runs it.
|
|
16
|
+
- Native parity round (Pale Signal handover of 29 September 2026, G4 and G7): one pointer decision per source across UI panels AND interactables, as Meta IWSDK's `MultiPointer` runs over every pointer-event object.
|
|
17
|
+
- `@realitycollective/webxr-uiextensions` - `PanelPointerOffers` (`core/pointer-offers.ts`, `UIX_POINTER_SET`, `PANEL_GRAB_RADIUS`): a window host registers a `"panel"` target set with the `PointerArbiter` from `@realitycollective/webxr-input`, offers per source and per pointer kind the nearest panel its ray, fingertip or grip reaches, and hovers, presses, touches and drags with a pointer kind only while that kind owns the source with the host's candidate. A host used on its own gets an arbiter of its own and behaves as before. Keys are the source's handedness or the input provider's id; the Interactions runtime aliases one to the other.
|
|
18
|
+
- `@realitycollective/native-uiextensions` - `NativeWindowHost` takes `pointers`; every `onPointerSample` is offered (a new optional `NativePointerSample.distance` is the distance IWSDK's pointer of that kind compares, else derived), the press machines run only while the pointer owns its source, and the panel cursor now reaches the host through `@realitycollective/native-interactions`' `applyPointerVisuals` (`targetKind: "panel"`), so a host stopgap for panel cursors is no longer needed. `showKeyboard` states what a host that cannot observe the keyboard's dismissal (an Android `NativeActivity`) reports as `"keyboardclosed"`: its own `hideKeyboard`, or a key event that ends entry.
|
|
19
|
+
- `@realitycollective/threejs-uiextensions` - `connectUIExtensions({ pointers })` and `connectWebXrPointerInput({ pointers })`: each slot's ray hit, fingertip panel and grip panel (within `PANEL_GRAB_RADIUS`) are offered under the session input source's handedness before hover, press, squeeze and touch act, and with a shared arbiter this host draws no cursor of its own (the Interactions binding draws every cursor, panels included). `ScenePointerBridge.rootOf` and `isTouchPressed` support it.
|
|
20
|
+
- `@realitycollective/xrblocks-uiextensions` - `connectUIExtensions({ pointers })` and `XrBlocksPointerBridge({ pointers })`: touch, grab and ray candidates are offered (the ray from this binding's own raycast of the wired panels each frame), presses and touches are gated on ownership, and hover is per pointer from that raycast rather than XR Blocks' per-element `onHoverEnter`/`onHoverExit` (which stay as no-ops), closing the "XR Blocks hover is not per pointer" item.
|
|
21
|
+
- `@realitycollective/iwsdk-uiextensions` - `registerUIExtensions(world, { pointers })` registers `UIPointerOfferSystem` (priority -3.95, after IWSDK's InputSystem), which offers each hand's touch, grab and ray intersections on this world's panels to the shared arbiter; IWSDK arbitrates its own pointers, so nothing is gated there, and the core's published visuals name panels on IWSDK as on every other platform. `pointerOffersFor(world)` reaches the offers.
|
|
22
|
+
- `@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.
|
|
23
|
+
- `@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...`.
|
|
24
|
+
- `@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.
|
|
11
25
|
- `@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).
|
|
12
26
|
- `@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.
|
|
13
27
|
- `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.
|
|
@@ -28,6 +42,8 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
28
42
|
|
|
29
43
|
### Changed
|
|
30
44
|
|
|
45
|
+
- Every package now requires `@realitycollective/webxr-input` at `^0.1.8-preview.0`: the shared pointer arbiter the hosts offer their panels to exists only from that release.
|
|
46
|
+
|
|
31
47
|
- 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.
|
|
32
48
|
- `@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.
|
|
33
49
|
- **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:
|
|
@@ -43,9 +59,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
43
59
|
- `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.
|
|
44
60
|
- `@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.
|
|
45
61
|
- **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.
|
|
62
|
+
- Dependencies on other Reality Collective repositories now follow one rule, checked by `scripts/rc-dependencies.mjs`. Going into `main`, each names the release npm tags `latest` and never a preview. Going into any other branch, each names the newest version on npm, release or preview. The rule covers every `package.json` in the repository (published packages, demos, harnesses and examples) and every copy `package-lock.json` records. A range passes only when its lowest version is the target, so `^0.1.8` passes for 0.1.8 and `^0.1.4` does not. CI runs it before the install, against the branch a pull request targets, and the publish workflow runs it before publishing. `release.mjs status` reports it. `release.mjs prepare` stops before cutting a release when a dependency names an unreleased preview of another repository, because that repository has to release first, and moves any range that is only behind. Other dependencies are out of scope. The script stays identical across the Reality Collective repositories.
|
|
46
63
|
|
|
47
64
|
### Fixed
|
|
48
65
|
|
|
66
|
+
- `@realitycollective/native-uiextensions` - a text field bound the way the web binds it, `setProperties({ onValueChange })`, never heard typed text on native: the binding handed the function to the host as element state and raised only a `valueChanged` event. A function-valued property now stays in the binding as a callback, only plain data crosses to the host, and the keyboard's text calls `onValueChange` as uikit's `Input` does, then raises `valueChanged`.
|
|
67
|
+
- `@realitycollective/xrblocks-uiextensions` - the host reads rays with `xb.input.getFrame()`, which xrblocks added in 0.20.0. Handed the input of an older xrblocks, the host now refuses it once at construction, naming the version it needs, instead of throwing from every frame's update. The multiplatform lab moves to xrblocks `^0.21.1` (it declared `^0.19.0`, whose input has no `getFrame`), and mounts XR Blocks' canvas inside `#scene-container` as its other two pipelines do.
|
|
49
68
|
- `@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.
|
|
50
69
|
- `@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.
|
|
51
70
|
- `@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.
|
|
@@ -103,7 +122,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
103
122
|
- `@realitycollective/webxr-uiextensions` takes its geometry vocabulary from `@realitycollective/webxr-input` at `^0.1.1` rather than redeclaring it. `Vec3Tuple`, `QuatTuple`, `HeadPose`, `HeadPoseSource` and `PointerSample` are now that package's types, re-exported under the same names, so no import changes for a consumer. `PointerSample` is its `RayTuple`, which is what lets an input provider written against the shared contracts feed this contract unchanged. It is the core's only runtime dependency: the contracts package is engine-free and carries none of its own, and `test/architecture.test.ts` now allows exactly that one name and fails on any other.
|
|
104
123
|
- `PointerSample` states its ownership rule: a delivered sample belongs to the listener and the source never writes to it again, so the core's hold-to-drag and drag maths may keep a press-time sample without copying. It mirrors the rule `@realitycollective/webxr-input` 0.1.3 writes on `InputSourceSnapshot`, so a provider feeding both contracts has one promise to keep.
|
|
105
124
|
|
|
106
|
-
[0.1.1]: https://github.com/realitycollective/WebXR-UIExtensions/
|
|
125
|
+
[0.1.1]: https://github.com/realitycollective/WebXR-UIExtensions/releases/tag/v0.1.1
|
|
107
126
|
[0.1.0]: https://github.com/realitycollective/WebXR-UIExtensions/releases/tag/v0.1.0
|
|
108
127
|
|
|
109
128
|
### Fixed
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Windowing, docking, layout regions and extra controls for [Meta's Immersive Web SDK](https://iwsdk.dev) (`@iwsdk/core`).
|
|
4
4
|
|
|
5
|
-
**This is the Meta IWSDK adapter** - and the reference implementation - for the engine-free [`@realitycollective/webxr-uiextensions`](../webxr-uiextensions/README.md) core, which it re-exports in full: one dependency gets IWSDK apps the whole surface.
|
|
5
|
+
**This is the Meta IWSDK adapter** - and the reference implementation - for the engine-free [`@realitycollective/webxr-uiextensions`](../webxr-uiextensions/README.md) core, which it re-exports in full: one dependency gets IWSDK apps the whole surface. Sibling adapters bind the same core to plain three.js ([`@realitycollective/threejs-uiextensions`](../threejs-uiextensions/README.md)), Google XR Blocks ([`@realitycollective/xrblocks-uiextensions`](../xrblocks-uiextensions/README.md), experimental) and native apps ([`@realitycollective/native-uiextensions`](../native-uiextensions/README.md)).
|
|
6
6
|
|
|
7
7
|
**Reuse, not recreation.** The IWSDK already ships an excellent spatial UI stack - UIKitML markup, `@pmndrs/uikit` rendering, `Follower`/`ScreenSpace` anchoring, grab/ray/poke interaction. This package adds the missing layer above it:
|
|
8
8
|
|
|
@@ -13,6 +13,8 @@ Windowing, docking, layout regions and extra controls for [Meta's Immersive Web
|
|
|
13
13
|
| **Dock states** | `world-locked` (place in space) ⇄ `body-follow` (lazy follow) ⇄ `head-locked`, realised with the IWSDK's own `Follower`/`ScreenSpace` |
|
|
14
14
|
| **Manipulation** | Drag windows by the title bar with the far ray or a near grab (controller squeeze, hand pinch), powered by `@pmndrs/handle`, the same library behind IWSDK grabbing; billboard-while-dragging, drop-to-dock |
|
|
15
15
|
| **Layout regions** | Named regions (row / column / grid slots) windows snap into; regions can themselves follow the player |
|
|
16
|
+
| **Shared pointers** | `registerUIExtensions(world, { pointers })` registers `UIPointerOfferSystem`, which offers each hand's panel candidates to the `PointerArbiter` the Interactions binding uses, so one pointer per hand is decided across panels and interactables, as IWSDK's own `MultiPointer` does |
|
|
17
|
+
| **Follow and placement** | `WindowManager.setFollow(id, options)` and the core's `stepFollow`, proved frame for frame against IWSDK's own `Follower`; focus bias and region slots by the same core rules every platform runs |
|
|
16
18
|
| **Controls** | `<uix-*>` custom-element upgrades: **stepper**, **toggle**, **expandable multi-line label**, **log/list view** - plus everything UIKitML already has (buttons, inputs, textareas, images, and the horizon kit's Slider/Checkbox/…) |
|
|
17
19
|
|
|
18
20
|
Everything is authored in plain UIKitML (HTML/CSS-like) - no new markup language, no custom renderer, no wrapper widgets around things the IWSDK already does.
|
|
@@ -46,7 +48,7 @@ createDockRegion(world, { id: 'wall', flow: 'column', position: [1.5, 1.8, -1.5]
|
|
|
46
48
|
createUIWindow(world, {
|
|
47
49
|
id: 'status',
|
|
48
50
|
title: 'Player Status',
|
|
49
|
-
config: './ui/status.uikitml', // UIKitML source; IWSDK
|
|
51
|
+
config: './ui/status.uikitml', // UIKitML source; IWSDK parses it at runtime
|
|
50
52
|
dockMode: DockMode.BodyFollow, // follows until the user pins it
|
|
51
53
|
pinnable: true, // title-bar buttons are off unless asked for
|
|
52
54
|
minimizable: true,
|
|
@@ -95,7 +97,7 @@ A control is a custom element, `<uix-stepper>`, `<uix-toggle>`, `<uix-expandable
|
|
|
95
97
|
</uix-stepper>
|
|
96
98
|
```
|
|
97
99
|
|
|
98
|
-
IWSDK
|
|
100
|
+
IWSDK validates every tag against a component schema (since 0.5), so a world whose panels use controls must register the set, or the panel fails to parse and never attaches:
|
|
99
101
|
|
|
100
102
|
```ts
|
|
101
103
|
import { uixComponentSet } from '@realitycollective/iwsdk-uiextensions';
|
|
@@ -152,7 +154,7 @@ Use `HAND_MENU_SNIPPET` from the core as the markup starting point: the same `ui
|
|
|
152
154
|
|
|
153
155
|
### Driving windows from code
|
|
154
156
|
|
|
155
|
-
`registerUIExtensions` returns the `WindowManager`, and it is the one API app code needs to change a window - a hand menu, a keyboard shortcut, a voice command. Every call is applied by the systems, and the same calls work on
|
|
157
|
+
`registerUIExtensions` returns the `WindowManager`, and it is the one API app code needs to change a window - a hand menu, a keyboard shortcut, a voice command. Every call is applied by the systems, and the same calls work on every adapter:
|
|
156
158
|
|
|
157
159
|
| Call | Effect |
|
|
158
160
|
| --- | --- |
|
|
@@ -164,7 +166,6 @@ Use `HAND_MENU_SNIPPET` from the core as the markup starting point: the same `ui
|
|
|
164
166
|
| `setChrome(id, { pin, dock, minimize, close })` | Enable or disable title-bar buttons at runtime |
|
|
165
167
|
| `focus(id)` | Bring to the front |
|
|
166
168
|
| `close(id)` | Destroy the window's entity |
|
|
167
|
-
|
|
168
169
|
| `setHandMenu(id, { hand, anchor, ... })` | Move a hand menu to the other hand or another anchor |
|
|
169
170
|
|
|
170
171
|
The record is always what the scene shows: a drag that docks a window, or a PIN click, is written back into `windows.get(id)`, and every change emits a typed event (`hidden`, `shown`, `regionChanged`, `returnHome`, `chromeChanged`, `handMenuChanged`, alongside the existing ones) so a menu can keep its labels honest. See `Examples/basic-window/`.
|
|
@@ -236,4 +237,4 @@ All decision logic (window manager, dock state machine, region slot math, drag m
|
|
|
236
237
|
|
|
237
238
|
## License
|
|
238
239
|
|
|
239
|
-
MIT
|
|
240
|
+
MIT - see [LICENSE](./LICENSE).
|
package/dist/component-set.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { htmlComponentSet } from '@drawcall/uikitml';
|
|
24
24
|
import { z } from 'zod';
|
|
25
|
-
import { dataAttributeKey } from '@realitycollective/webxr-uiextensions';
|
|
25
|
+
import { UIX_ELEMENT_TAGS, dataAttributeKey } from '@realitycollective/webxr-uiextensions';
|
|
26
26
|
/**
|
|
27
27
|
* Base container, taken from the parser's own `<div>` definition rather than
|
|
28
28
|
* imported from `@pmndrs/uikit` directly - that keeps a single copy of uikit
|
|
@@ -30,26 +30,6 @@ import { dataAttributeKey } from '@realitycollective/webxr-uiextensions';
|
|
|
30
30
|
*/
|
|
31
31
|
const BaseContainer = htmlComponentSet['div']
|
|
32
32
|
?.component;
|
|
33
|
-
/** Control elements an author writes, and the parts that live inside them. */
|
|
34
|
-
const CONTROL_TAGS = [
|
|
35
|
-
'uix-stepper',
|
|
36
|
-
'uix-toggle',
|
|
37
|
-
'uix-expandable-label',
|
|
38
|
-
'uix-log-view',
|
|
39
|
-
];
|
|
40
|
-
const ROLE_TAGS = [
|
|
41
|
-
'uix-decrement',
|
|
42
|
-
'uix-value',
|
|
43
|
-
'uix-increment',
|
|
44
|
-
'uix-label',
|
|
45
|
-
'uix-line',
|
|
46
|
-
'uix-up',
|
|
47
|
-
'uix-down',
|
|
48
|
-
'uix-clear',
|
|
49
|
-
'uix-status',
|
|
50
|
-
'uix-text',
|
|
51
|
-
'uix-more',
|
|
52
|
-
];
|
|
53
33
|
/**
|
|
54
34
|
* A plain uikit Container that records what it was declared as.
|
|
55
35
|
*
|
|
@@ -90,5 +70,5 @@ function define(tag) {
|
|
|
90
70
|
* Pass to `spatialUI.componentSets` so `<uix-*>` elements parse on IWSDK 0.5.
|
|
91
71
|
* Without it, a panel using any control fails to parse and never attaches.
|
|
92
72
|
*/
|
|
93
|
-
export const uixComponentSet = Object.fromEntries(
|
|
73
|
+
export const uixComponentSet = Object.fromEntries(UIX_ELEMENT_TAGS.map((tag) => [tag, define(tag)]));
|
|
94
74
|
//# sourceMappingURL=component-set.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-set.js","sourceRoot":"","sources":["../src/component-set.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,gBAAgB,EAA+C,MAAM,mBAAmB,CAAC;AAClG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"component-set.js","sourceRoot":"","sources":["../src/component-set.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,gBAAgB,EAA+C,MAAM,mBAAmB,CAAC;AAClG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE3F;;;;GAIG;AACH,MAAM,aAAa,GAAG,gBAAgB,CAAC,KAAK,CAAC;IAC3C,EAAE,SAEH,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,GAAW;IACnC,OAAO,MAAM,mBAAoB,SAAQ,aAAa;QACpD,YAAY,aAAsC,EAAE;YAClD,MAAM,UAAU,GAA4B,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAA4B,EAAE,CAAC;YAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtD,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtD,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;gBAC1C,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,KAAK,CAAC,UAAU,CAAC,CAAC;YAClB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE;gBACrC,aAAa,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;aACtD,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AAE1C,SAAS,MAAM,CAAC,GAAW;IACzB,OAAO;QACL,SAAS,EAAE,gBAAgB,CAAC,GAAG,CAAgD;QAC/E,MAAM;QACN,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAiB,MAAM,CAAC,WAAW,CAC7D,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAClD,CAAC","sourcesContent":["/**\n * The UIKitML component set for `<uix-*>` control elements.\n *\n * IWSDK 0.5 parses panels with `@drawcall/uikitml`, which validates every tag\n * against a component schema and rejects anything it does not know - including\n * every `data-*` attribute on a built-in tag. Declaring the control elements\n * here is the supported way to extend that vocabulary.\n *\n * The three.js and XR Blocks adapters need no equivalent: their parser accepts\n * custom tags unregistered, exposing the tag on\n * `userData.customElement.componentName`. This set reproduces that convention\n * on the IWSDK side - the constructor below stamps the same key and lifts\n * `data-uix-*` attributes into `userData` - so ONE markup file drives every\n * adapter and the engine-free upgraders read it identically everywhere.\n *\n * Register it when creating the world:\n * ```ts\n * const world = await World.create(container, {\n * features: { spatialUI: { kit: 'horizon', componentSets: [uixComponentSet] } },\n * });\n * ```\n */\nimport { htmlComponentSet, type ComponentSet, type ComponentDefinition } from '@drawcall/uikitml';\nimport { z } from 'zod';\nimport { UIX_ELEMENT_TAGS, dataAttributeKey } from '@realitycollective/webxr-uiextensions';\n\n/**\n * Base container, taken from the parser's own `<div>` definition rather than\n * imported from `@pmndrs/uikit` directly - that keeps a single copy of uikit\n * in the tree, whichever version `@iwsdk/core` resolves.\n */\nconst BaseContainer = htmlComponentSet['div']\n ?.component as unknown as new (properties?: Record<string, unknown>) => {\n userData: Record<string, unknown>;\n};\n\n/**\n * A plain uikit Container that records what it was declared as.\n *\n * Anything the schema let through that is not a uikit property - the\n * `data-uix-*` parameters - is moved into `userData` rather than handed to\n * uikit, which would warn about properties it does not recognise.\n */\nfunction controlComponent(tag: string): typeof BaseContainer {\n return class UixControlContainer extends BaseContainer {\n constructor(properties: Record<string, unknown> = {}) {\n const uikitProps: Record<string, unknown> = {};\n const userData: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(properties)) {\n if (key.startsWith('data-') || /^data[A-Z]/.test(key)) {\n userData[dataAttributeKey(key)] = value;\n } else {\n uikitProps[key] = value;\n }\n }\n super(uikitProps);\n Object.assign(this.userData, userData, {\n customElement: { componentName: tag, sourceTag: tag },\n });\n }\n };\n}\n\n/** Permissive: the parameters are ours, and the upgraders validate them. */\nconst schema = z.object({}).passthrough();\n\nfunction define(tag: string): ComponentDefinition {\n return {\n component: controlComponent(tag) as unknown as ComponentDefinition['component'],\n schema,\n canHaveChildren: true,\n };\n}\n\n/**\n * Pass to `spatialUI.componentSets` so `<uix-*>` elements parse on IWSDK 0.5.\n * Without it, a panel using any control fails to parse and never attaches.\n */\nexport const uixComponentSet: ComponentSet = Object.fromEntries(\n UIX_ELEMENT_TAGS.map((tag) => [tag, define(tag)]),\n);\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -26,4 +26,5 @@ export * from './systems/drag-system.js';
|
|
|
26
26
|
export * from './systems/dock-region-system.js';
|
|
27
27
|
export * from './systems/controls-system.js';
|
|
28
28
|
export * from './systems/touch-guard-system.js';
|
|
29
|
+
export * from './systems/pointer-offer-system.js';
|
|
29
30
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,sEAAsE;AACtE,cAAc,uCAAuC,CAAC;AAEtD,oBAAoB;AACpB,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC","sourcesContent":["/**\n * @realitycollective/iwsdk-uiextensions - the Meta IWSDK adapter.\n *\n * The engine-free UX core (window manager, dock state, region math, drag\n * math, control models, markup upgraders, adapter interfaces) lives in\n * `@realitycollective/webxr-uiextensions` and is re-exported here in full,\n * so this package remains a drop-in single dependency for IWSDK apps.\n *\n * What this package adds is the IWSDK binding: ECS components + systems\n * that apply the core onto `@iwsdk/core` entities, plus the one-call\n * factories and `registerUIExtensions`.\n */\n// The portable core - re-exported for a single-dependency experience.\nexport * from '@realitycollective/webxr-uiextensions';\n\n// IWSDK ECS binding\nexport * from './components.js';\nexport * from './manager-registry.js';\nexport * from './region-registry-store.js';\nexport * from './component-set.js';\nexport * from './factory.js';\nexport * from './scene-host.js';\nexport * from './register.js';\nexport * from './systems/window-system.js';\nexport * from './systems/dock-system.js';\nexport * from './systems/drag-system.js';\nexport * from './systems/dock-region-system.js';\nexport * from './systems/controls-system.js';\nexport * from './systems/touch-guard-system.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,sEAAsE;AACtE,cAAc,uCAAuC,CAAC;AAEtD,oBAAoB;AACpB,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC","sourcesContent":["/**\n * @realitycollective/iwsdk-uiextensions - the Meta IWSDK adapter.\n *\n * The engine-free UX core (window manager, dock state, region math, drag\n * math, control models, markup upgraders, adapter interfaces) lives in\n * `@realitycollective/webxr-uiextensions` and is re-exported here in full,\n * so this package remains a drop-in single dependency for IWSDK apps.\n *\n * What this package adds is the IWSDK binding: ECS components + systems\n * that apply the core onto `@iwsdk/core` entities, plus the one-call\n * factories and `registerUIExtensions`.\n */\n// The portable core - re-exported for a single-dependency experience.\nexport * from '@realitycollective/webxr-uiextensions';\n\n// IWSDK ECS binding\nexport * from './components.js';\nexport * from './manager-registry.js';\nexport * from './region-registry-store.js';\nexport * from './component-set.js';\nexport * from './factory.js';\nexport * from './scene-host.js';\nexport * from './register.js';\nexport * from './systems/window-system.js';\nexport * from './systems/dock-system.js';\nexport * from './systems/drag-system.js';\nexport * from './systems/dock-region-system.js';\nexport * from './systems/controls-system.js';\nexport * from './systems/touch-guard-system.js';\nexport * from './systems/pointer-offer-system.js';\n"]}
|
package/dist/register.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* those to the entities.
|
|
14
14
|
*/
|
|
15
15
|
import type { World } from '@iwsdk/core';
|
|
16
|
+
import { type PointerArbiterLike } from './systems/pointer-offer-system.js';
|
|
16
17
|
import type { TouchPressOptions, WindowManager } from '@realitycollective/webxr-uiextensions';
|
|
17
18
|
export interface RegisterOptions {
|
|
18
19
|
/** Disable the title-bar drag system. */
|
|
@@ -35,5 +36,11 @@ export interface RegisterOptions {
|
|
|
35
36
|
* unsigned-distance behaviour. See `UITouchGuardSystem`.
|
|
36
37
|
*/
|
|
37
38
|
touchGuard?: boolean | Partial<TouchPressOptions>;
|
|
39
|
+
/**
|
|
40
|
+
* The shared pointer arbiter (the Interactions runtime's), so panels take
|
|
41
|
+
* part in the one pointer decision per hand. Without it the panel offers go
|
|
42
|
+
* to an arbiter of their own, which is harmless. See `UIPointerOfferSystem`.
|
|
43
|
+
*/
|
|
44
|
+
pointers?: PointerArbiterLike;
|
|
38
45
|
}
|
|
39
46
|
export declare function registerUIExtensions(world: World, options?: RegisterOptions): WindowManager;
|
package/dist/register.js
CHANGED
|
@@ -4,6 +4,7 @@ import { UIDragSystem } from './systems/drag-system.js';
|
|
|
4
4
|
import { UIDockRegionSystem } from './systems/dock-region-system.js';
|
|
5
5
|
import { UIControlsSystem } from './systems/controls-system.js';
|
|
6
6
|
import { UITouchGuardSystem } from './systems/touch-guard-system.js';
|
|
7
|
+
import { UIPointerOfferSystem, pointerOffersFor } from './systems/pointer-offer-system.js';
|
|
7
8
|
import { windowManagerFor } from './manager-registry.js';
|
|
8
9
|
export function registerUIExtensions(world, options = {}) {
|
|
9
10
|
// The dock system runs first: the hand-menu palm gate it writes each frame
|
|
@@ -26,6 +27,10 @@ export function registerUIExtensions(world, options = {}) {
|
|
|
26
27
|
// reacts to the presses.
|
|
27
28
|
world.registerSystem(UITouchGuardSystem, { priority: -3.9, configData: thresholds });
|
|
28
29
|
}
|
|
30
|
+
// Straight after InputSystem (-4), before the touch guard (-3.9): offers
|
|
31
|
+
// what each pointer reaches to the shared arbiter. Gates nothing.
|
|
32
|
+
pointerOffersFor(world, options.pointers);
|
|
33
|
+
world.registerSystem(UIPointerOfferSystem, { priority: -3.95 });
|
|
29
34
|
return windowManagerFor(world);
|
|
30
35
|
}
|
|
31
36
|
//# sourceMappingURL=register.js.map
|
package/dist/register.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAA2B,MAAM,mCAAmC,CAAC;AACpH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAgCzD,MAAM,UAAU,oBAAoB,CAClC,KAAY,EACZ,UAA2B,EAAE;IAE7B,2EAA2E;IAC3E,0DAA0D;IAC1D,KAAK,CAAC,cAAc,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IACpF,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC3B,KAAK,CAAC,cAAc,CAAC,YAAY,EAAE;YACjC,UAAU,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE;SACrD,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC9B,KAAK,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC/B,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,0EAA0E;QAC1E,yBAAyB;QACzB,KAAK,CAAC,cAAc,CAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,yEAAyE;IACzE,kEAAkE;IAClE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1C,KAAK,CAAC,cAAc,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC","sourcesContent":["/**\n * One-call setup: register every UI Extensions system on a world.\n *\n * ```ts\n * const world = await World.create(container, { features: { spatialUI: true } });\n * const windows = registerUIExtensions(world);\n * ```\n *\n * The returned `WindowManager` is the API for changing window state from\n * app code (a hand menu, a keyboard shortcut): `hide`/`show`, `dockTo`/\n * `undock`/`returnHome`, `togglePin`/`setDockMode`, `minimize`/`restore`,\n * `setChrome` and `close`. The systems registered here apply every one of\n * those to the entities.\n */\nimport type { World } from '@iwsdk/core';\nimport { UIWindowSystem } from './systems/window-system.js';\nimport { UIDockSystem } from './systems/dock-system.js';\nimport { UIDragSystem } from './systems/drag-system.js';\nimport { UIDockRegionSystem } from './systems/dock-region-system.js';\nimport { UIControlsSystem } from './systems/controls-system.js';\nimport { UITouchGuardSystem } from './systems/touch-guard-system.js';\nimport { UIPointerOfferSystem, pointerOffersFor, type PointerArbiterLike } from './systems/pointer-offer-system.js';\nimport { windowManagerFor } from './manager-registry.js';\nimport type { TouchPressOptions, WindowManager } from '@realitycollective/webxr-uiextensions';\n\nexport interface RegisterOptions {\n /** Disable the title-bar drag system. */\n drag?: boolean;\n /**\n * Disable near dragging (controller squeeze or hand pinch on the title\n * bar), leaving the far ray as the only way to move a window. On by\n * default; see `UIDragSystem` for what it does to IWSDK's pointers.\n */\n nearDrag?: boolean;\n /** Disable dock regions. */\n regions?: boolean;\n /** Disable the `data-uix` control upgrades. */\n controls?: boolean;\n /**\n * Press / hold / release for IWSDK's near (poke) pointers, so a finger\n * pushed through a panel and pulled back is one click, and a finger\n * arriving from behind is none. On by default; pass thresholds to tune\n * (meters, signed, positive in front), or `false` to keep IWSDK's own\n * unsigned-distance behaviour. See `UITouchGuardSystem`.\n */\n touchGuard?: boolean | Partial<TouchPressOptions>;\n /**\n * The shared pointer arbiter (the Interactions runtime's), so panels take\n * part in the one pointer decision per hand. Without it the panel offers go\n * to an arbiter of their own, which is harmless. See `UIPointerOfferSystem`.\n */\n pointers?: PointerArbiterLike;\n}\n\nexport function registerUIExtensions(\n world: World,\n options: RegisterOptions = {},\n): WindowManager {\n // The dock system runs first: the hand-menu palm gate it writes each frame\n // is then applied by the window system in the same frame.\n world.registerSystem(UIDockSystem, { priority: -1 }).registerSystem(UIWindowSystem);\n if (options.drag !== false) {\n world.registerSystem(UIDragSystem, {\n configData: { nearDrag: options.nearDrag !== false },\n });\n }\n if (options.regions !== false) {\n world.registerSystem(UIDockRegionSystem);\n }\n if (options.controls !== false) {\n world.registerSystem(UIControlsSystem);\n }\n if (options.touchGuard !== false) {\n const thresholds = typeof options.touchGuard === 'object' ? options.touchGuard : {};\n // Straight after InputSystem (-4) has moved the pointers, before anything\n // reacts to the presses.\n world.registerSystem(UITouchGuardSystem, { priority: -3.9, configData: thresholds });\n }\n // Straight after InputSystem (-4), before the touch guard (-3.9): offers\n // what each pointer reaches to the shared arbiter. Gates nothing.\n pointerOffersFor(world, options.pointers);\n world.registerSystem(UIPointerOfferSystem, { priority: -3.95 });\n return windowManagerFor(world);\n}\n"]}
|
|
@@ -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
|
-
|
|
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":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,QAAQ,EACR,cAAc,EACd,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAiB,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,SAAS,GAKV,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,MAAM,OAAO,YAAa,SAAQ,YAAY,CAAC;IAC7C,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE;CACjD,CAAC;IACQ,OAAO,CAAiB;IACxB,cAAc,GAAG,IAAI,OAAO,EAAE,CAAC;IAC/B,cAAc,GAAG,IAAI,OAAO,EAAE,CAAC;IAC/B,YAAY,GAAG,IAAI,OAAO,EAAE,CAAC;IAC7B,cAAc,GAAG,IAAI,UAAU,EAAE,CAAC;IAClC,gBAAgB,GAAG,IAAI,UAAU,EAAE,CAAC;IAEnC,IAAI;QACX,IAAI,CAAC,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrH,CAAC;IAEQ,MAAM;QACb,IAAI,KAA4B,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACnD,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAkB,CAAC;YACzE,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAW,CAAC;YAC5E,IAAI,UAAU,CAAC,SAAS,CAAC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBACnD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YACzC,CAAC;YACD,IAAI,SAAS,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACtC,KAAK,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,oFAAoF;IAC5E,SAAS;QACf,MAAM,KAAK,GAAc,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAI,IAAI,CAAC,KAAuC,EAAE,EAAE,CAAC;QAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,MAA6D,CAAC;QAC/E,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,CAAU,EAAE,CAAC;YAC9C,IAAI,CAAC,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,SAAS,CAAC,0CAA0C;YACtD,CAAC;YACD,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACnC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1C,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,GAAG;gBACZ,QAAQ,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;gBACzE,UAAU,EAAE;oBACV,IAAI,CAAC,cAAc,CAAC,CAAC;oBACrB,IAAI,CAAC,cAAc,CAAC,CAAC;oBACrB,IAAI,CAAC,cAAc,CAAC,CAAC;oBACrB,IAAI,CAAC,cAAc,CAAC,CAAC;iBACtB;aACF,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,WAAW,CAAC,MAAc,EAAE,KAAgB;QAClD,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAW,CAAC;QAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,gBAAgB,CAChC,KAAK,EACL,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EACrE,MAAM,CAAC,QAAQ,CAChB,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;YAC9E,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,oEAAoE;IAC5D,cAAc,CAAC,MAAgB,EAAE,IAAe;QACtD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9E,IAAI,CAAC,cAAc,CAAC,GAAG,CACrB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CACnB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC7B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACzC,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC;YAC1D,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;IAEO,KAAK,CAAC,MAAc,EAAE,OAAe,EAAE,SAAwB;QACrE,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAChD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzD,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,IAAI,CAAC,cAAc,IAAI,SAAS,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC9D,oEAAoE;gBACpE,mEAAmE;gBACnE,kCAAkC;gBAClC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,IAAI,CAAC,iBAAiB,IAAI,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC/D,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;YACtC,CAAC;YACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,iEAAiE;gBACjE,6CAA6C;gBAC7C,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;gBACjD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrB,+DAA+D;gBAC/D,qEAAqE;gBACrE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/C,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,EAAE,IAAI,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,oEAAoE;YACpE,8DAA8D;YAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;YACpC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtD,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtD,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC5D,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC5D,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAED,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,EAAE,SAAS,CAAC,CAAC;QAE7D,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAW,CAAC;QAC3D,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,MAAc;QAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QACD,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,aAAa,CACvB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EACrE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EACrE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAClB,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IACjC,CAAC;IAEO,YAAY,GAAG,IAAI,OAAO,EAAE,CAAC;IAC7B,aAAa,GAAG,IAAI,UAAU,EAAE,CAAC;IACjC,QAAQ,GAAG,IAAI,KAAK,EAAE,CAAC;IAEvB,WAAW,CAAC,MAAc,EAAE,UAAyC,EAAE;QAC7E,0EAA0E;QAC1E,wEAAwE;QACxE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAW,CAAC,CAAC;QACjF,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QAClE,IAAI,MAAM,GAA6B,MAAM;YAC3C,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC7E,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAEjE,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/B,IAAI,OAAO,CAAC,eAAe,IAAI,MAAM,EAAE,CAAC;YACtC,oEAAoE;YACpE,oEAAoE;YACpE,4DAA4D;YAC5D,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACnD,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YAC3D,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC;YACpB,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC;YACpB,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;YAC3D,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC/E,iEAAiE;YACjE,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;gBACxC,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;QAED,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE;YAC5B,gEAAgE;YAChE,iEAAiE;YACjE,uEAAuE;YACvE,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,cAAc,CAAC,MAAM;YAC/B,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAY;YAC1F,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAY;SACvG,CAAC,CAAC;IACL,CAAC;IAED,kGAAkG;IAC1F,iBAAiB,CAAC,QAAgB;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACnD,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACnG,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;YAChE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACxD,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;CACF","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"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UIPointerOfferSystem - tells the shared pointer arbiter which panels
|
|
3
|
+
* IWSDK's pointers reach.
|
|
4
|
+
*
|
|
5
|
+
* IWSDK already arbitrates its own pointers: per hand the first of touch,
|
|
6
|
+
* grab and ray with a hit owns the hand. This system gates nothing. Once per
|
|
7
|
+
* frame, right after `InputSystem` has moved the pointers, it reads what each
|
|
8
|
+
* of a hand's three pointers intersects and OFFERS the ones that sit under a
|
|
9
|
+
* panel (a `PanelUI` entity's object, or any descendant) to the family's
|
|
10
|
+
* `PanelPointerOffers`. The shared arbiter, and the visuals the Interactions
|
|
11
|
+
* runtime publishes from it, then know about panels on IWSDK exactly as they
|
|
12
|
+
* do on every other platform.
|
|
13
|
+
*
|
|
14
|
+
* Offers are keyed by the hand name ('left', 'right'), which the Interactions
|
|
15
|
+
* runtime aliases to its own source ids. The panel id is the window id when
|
|
16
|
+
* the entity is a managed window, else its `PanelUI` config path (the same id
|
|
17
|
+
* the scene host announces), else the entity index. A kind with no panel hit
|
|
18
|
+
* is offered as `undefined`. A pointer that is pressing (`getButtonsDown()`
|
|
19
|
+
* not empty) sets the selection lock for that kind.
|
|
20
|
+
*/
|
|
21
|
+
import { type World } from '@iwsdk/core';
|
|
22
|
+
import { PanelPointerOffers } from '@realitycollective/webxr-uiextensions';
|
|
23
|
+
/** The shared arbiter type, taken from the core so this package needs no direct dependency on it. */
|
|
24
|
+
export type PointerArbiterLike = NonNullable<ConstructorParameters<typeof PanelPointerOffers>[0]>;
|
|
25
|
+
/**
|
|
26
|
+
* The `PanelPointerOffers` for a world: built on the first call (with
|
|
27
|
+
* `pointers` when given, else an arbiter of its own) and the same instance
|
|
28
|
+
* after. `registerUIExtensions(world, { pointers })` makes the first call.
|
|
29
|
+
*/
|
|
30
|
+
export declare function pointerOffersFor(world: World, pointers?: PointerArbiterLike): PanelPointerOffers;
|
|
31
|
+
declare const UIPointerOfferSystem_base: import("elics").SystemConstructor<import("elics").SystemSchema, {
|
|
32
|
+
panels: {
|
|
33
|
+
required: import("elics").Component<{
|
|
34
|
+
config: {
|
|
35
|
+
type: import("@iwsdk/core").Types.String;
|
|
36
|
+
default: string;
|
|
37
|
+
};
|
|
38
|
+
}>[];
|
|
39
|
+
};
|
|
40
|
+
}, World, import("@iwsdk/core").System<import("elics").SystemSchema, {
|
|
41
|
+
panels: {
|
|
42
|
+
required: import("elics").Component<{
|
|
43
|
+
config: {
|
|
44
|
+
type: import("@iwsdk/core").Types.String;
|
|
45
|
+
default: string;
|
|
46
|
+
};
|
|
47
|
+
}>[];
|
|
48
|
+
};
|
|
49
|
+
}>>;
|
|
50
|
+
export declare class UIPointerOfferSystem extends UIPointerOfferSystem_base {
|
|
51
|
+
private offers;
|
|
52
|
+
init(): void;
|
|
53
|
+
update(): void;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UIPointerOfferSystem - tells the shared pointer arbiter which panels
|
|
3
|
+
* IWSDK's pointers reach.
|
|
4
|
+
*
|
|
5
|
+
* IWSDK already arbitrates its own pointers: per hand the first of touch,
|
|
6
|
+
* grab and ray with a hit owns the hand. This system gates nothing. Once per
|
|
7
|
+
* frame, right after `InputSystem` has moved the pointers, it reads what each
|
|
8
|
+
* of a hand's three pointers intersects and OFFERS the ones that sit under a
|
|
9
|
+
* panel (a `PanelUI` entity's object, or any descendant) to the family's
|
|
10
|
+
* `PanelPointerOffers`. The shared arbiter, and the visuals the Interactions
|
|
11
|
+
* runtime publishes from it, then know about panels on IWSDK exactly as they
|
|
12
|
+
* do on every other platform.
|
|
13
|
+
*
|
|
14
|
+
* Offers are keyed by the hand name ('left', 'right'), which the Interactions
|
|
15
|
+
* runtime aliases to its own source ids. The panel id is the window id when
|
|
16
|
+
* the entity is a managed window, else its `PanelUI` config path (the same id
|
|
17
|
+
* the scene host announces), else the entity index. A kind with no panel hit
|
|
18
|
+
* is offered as `undefined`. A pointer that is pressing (`getButtonsDown()`
|
|
19
|
+
* not empty) sets the selection lock for that kind.
|
|
20
|
+
*/
|
|
21
|
+
import { PanelUI } from '@iwsdk/core';
|
|
22
|
+
import { createSystem } from '../create-system.js';
|
|
23
|
+
import { UIWindow } from '../components.js';
|
|
24
|
+
import { PanelPointerOffers, } from '@realitycollective/webxr-uiextensions';
|
|
25
|
+
const HANDS = ['left', 'right'];
|
|
26
|
+
const KINDS = ['touch', 'grab', 'ray'];
|
|
27
|
+
const offersByWorld = new WeakMap();
|
|
28
|
+
/**
|
|
29
|
+
* The `PanelPointerOffers` for a world: built on the first call (with
|
|
30
|
+
* `pointers` when given, else an arbiter of its own) and the same instance
|
|
31
|
+
* after. `registerUIExtensions(world, { pointers })` makes the first call.
|
|
32
|
+
*/
|
|
33
|
+
export function pointerOffersFor(world, pointers) {
|
|
34
|
+
let offers = offersByWorld.get(world);
|
|
35
|
+
if (!offers) {
|
|
36
|
+
offers = new PanelPointerOffers(pointers);
|
|
37
|
+
offersByWorld.set(world, offers);
|
|
38
|
+
}
|
|
39
|
+
return offers;
|
|
40
|
+
}
|
|
41
|
+
export class UIPointerOfferSystem extends createSystem({
|
|
42
|
+
panels: { required: [PanelUI] },
|
|
43
|
+
}) {
|
|
44
|
+
offers;
|
|
45
|
+
init() {
|
|
46
|
+
this.offers = pointerOffersFor(this.world);
|
|
47
|
+
const offers = this.offers;
|
|
48
|
+
this.cleanupFuncs.push(() => {
|
|
49
|
+
offers.dispose();
|
|
50
|
+
offersByWorld.delete(this.world);
|
|
51
|
+
this.offers = undefined;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
update() {
|
|
55
|
+
const offers = this.offers;
|
|
56
|
+
const xr = this.input?.xr;
|
|
57
|
+
if (!offers || !xr) {
|
|
58
|
+
return; // headless, or no XR input: nothing to offer
|
|
59
|
+
}
|
|
60
|
+
const roots = new Map();
|
|
61
|
+
for (const entity of this.queries.panels.entities) {
|
|
62
|
+
if (entity.object3D) {
|
|
63
|
+
roots.set(entity.object3D, entity);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const hand of HANDS) {
|
|
67
|
+
const multi = xr.multiPointers[hand];
|
|
68
|
+
for (const kind of KINDS) {
|
|
69
|
+
const pointer = multi.getPointer(kind);
|
|
70
|
+
const hit = pointer.getIntersection();
|
|
71
|
+
const entity = hit ? panelOf(hit.object, roots) : undefined;
|
|
72
|
+
offers.offer(hand, kind, hit && entity
|
|
73
|
+
? { panelId: panelIdOf(entity), point: [hit.point.x, hit.point.y, hit.point.z], distance: hit.distance }
|
|
74
|
+
: undefined);
|
|
75
|
+
offers.setSelecting(hand, kind, (pointer.getButtonsDown?.().size ?? 0) > 0);
|
|
76
|
+
}
|
|
77
|
+
offers.resolve(hand);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** The panel entity whose root is `object` or one of its ancestors. */
|
|
82
|
+
function panelOf(object, roots) {
|
|
83
|
+
for (let node = object; node; node = node.parent) {
|
|
84
|
+
const entity = roots.get(node);
|
|
85
|
+
if (entity) {
|
|
86
|
+
return entity;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/** Window id, else config path, else entity index: the scene host's naming. */
|
|
92
|
+
function panelIdOf(entity) {
|
|
93
|
+
const windowId = entity.hasComponent(UIWindow)
|
|
94
|
+
? UIWindow.data.windowId[entity.index]
|
|
95
|
+
: '';
|
|
96
|
+
return windowId || PanelUI.data.config[entity.index] || String(entity.index);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=pointer-offer-system.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pointer-offer-system.js","sourceRoot":"","sources":["../../src/systems/pointer-offer-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIPointerOfferSystem - tells the shared pointer arbiter which panels\n * IWSDK's pointers reach.\n *\n * IWSDK already arbitrates its own pointers: per hand the first of touch,\n * grab and ray with a hit owns the hand. This system gates nothing. Once per\n * frame, right after `InputSystem` has moved the pointers, it reads what each\n * of a hand's three pointers intersects and OFFERS the ones that sit under a\n * panel (a `PanelUI` entity's object, or any descendant) to the family's\n * `PanelPointerOffers`. The shared arbiter, and the visuals the Interactions\n * runtime publishes from it, then know about panels on IWSDK exactly as they\n * do on every other platform.\n *\n * Offers are keyed by the hand name ('left', 'right'), which the Interactions\n * runtime aliases to its own source ids. The panel id is the window id when\n * the entity is a managed window, else its `PanelUI` config path (the same id\n * the scene host announces), else the entity index. A kind with no panel hit\n * is offered as `undefined`. A pointer that is pressing (`getButtonsDown()`\n * not empty) sets the selection lock for that kind.\n */\nimport { PanelUI, type Entity, type World } from '@iwsdk/core';\nimport type { Object3D } from 'three';\nimport { createSystem } from '../create-system.js';\nimport { UIWindow } from '../components.js';\nimport {\n PanelPointerOffers,\n type Hand,\n} from '@realitycollective/webxr-uiextensions';\n\n/** The shared arbiter type, taken from the core so this package needs no direct dependency on it. */\nexport type PointerArbiterLike = NonNullable<ConstructorParameters<typeof PanelPointerOffers>[0]>;\ntype NearPointerKind = Parameters<PanelPointerOffers['offer']>[1];\n\nconst HANDS: readonly Hand[] = ['left', 'right'];\nconst KINDS: readonly NearPointerKind[] = ['touch', 'grab', 'ray'];\n\nconst offersByWorld = new WeakMap<World, PanelPointerOffers>();\n\n/**\n * The `PanelPointerOffers` for a world: built on the first call (with\n * `pointers` when given, else an arbiter of its own) and the same instance\n * after. `registerUIExtensions(world, { pointers })` makes the first call.\n */\nexport function pointerOffersFor(world: World, pointers?: PointerArbiterLike): PanelPointerOffers {\n let offers = offersByWorld.get(world);\n if (!offers) {\n offers = new PanelPointerOffers(pointers);\n offersByWorld.set(world, offers);\n }\n return offers;\n}\n\n/** The slice of a pointer-events sub-pointer this system reads. */\ninterface OfferPointerLike {\n getIntersection():\n | { object: Object3D; point: { x: number; y: number; z: number }; distance: number }\n | undefined;\n getButtonsDown?(): ReadonlySet<number>;\n}\n\nexport class UIPointerOfferSystem extends createSystem({\n panels: { required: [PanelUI] },\n}) {\n private offers: PanelPointerOffers | undefined;\n\n override init(): void {\n this.offers = pointerOffersFor(this.world);\n const offers = this.offers;\n this.cleanupFuncs.push(() => {\n offers.dispose();\n offersByWorld.delete(this.world);\n this.offers = undefined;\n });\n }\n\n override update(): void {\n const offers = this.offers;\n const xr = (this.input as typeof this.input | undefined)?.xr;\n if (!offers || !xr) {\n return; // headless, or no XR input: nothing to offer\n }\n const roots = new Map<Object3D, Entity>();\n for (const entity of this.queries.panels.entities) {\n if (entity.object3D) {\n roots.set(entity.object3D, entity);\n }\n }\n for (const hand of HANDS) {\n const multi = xr.multiPointers[hand];\n for (const kind of KINDS) {\n const pointer = multi.getPointer(kind) as unknown as OfferPointerLike;\n const hit = pointer.getIntersection();\n const entity = hit ? panelOf(hit.object, roots) : undefined;\n offers.offer(\n hand,\n kind,\n hit && entity\n ? { panelId: panelIdOf(entity), point: [hit.point.x, hit.point.y, hit.point.z], distance: hit.distance }\n : undefined,\n );\n offers.setSelecting(hand, kind, (pointer.getButtonsDown?.().size ?? 0) > 0);\n }\n offers.resolve(hand);\n }\n }\n}\n\n/** The panel entity whose root is `object` or one of its ancestors. */\nfunction panelOf(object: Object3D, roots: ReadonlyMap<Object3D, Entity>): Entity | undefined {\n for (let node: Object3D | null = object; node; node = node.parent) {\n const entity = roots.get(node);\n if (entity) {\n return entity;\n }\n }\n return undefined;\n}\n\n/** Window id, else config path, else entity index: the scene host's naming. */\nfunction panelIdOf(entity: Entity): string {\n const windowId = entity.hasComponent(UIWindow)\n ? (UIWindow.data.windowId[entity.index] as string)\n : '';\n return windowId || (PanelUI.data.config[entity.index] as string) || String(entity.index);\n}\n"]}
|
|
@@ -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
|
|
3
|
+
"version": "0.1.1",
|
|
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",
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
"windowing",
|
|
13
13
|
"uikitml",
|
|
14
14
|
"spatial-ui",
|
|
15
|
-
"typescript"
|
|
15
|
+
"typescript",
|
|
16
|
+
"hand-menu",
|
|
17
|
+
"pointer",
|
|
18
|
+
"docking"
|
|
16
19
|
],
|
|
17
20
|
"license": "MIT",
|
|
18
21
|
"author": "Reality Collective",
|
|
@@ -39,7 +42,8 @@
|
|
|
39
42
|
"@drawcall/uikitml": "0.1.8",
|
|
40
43
|
"zod": "^4.4.3",
|
|
41
44
|
"@pmndrs/handle": "^6.6.16",
|
|
42
|
-
"@realitycollective/webxr-
|
|
45
|
+
"@realitycollective/webxr-input": "^0.1.8",
|
|
46
|
+
"@realitycollective/webxr-uiextensions": "^0.1.1"
|
|
43
47
|
},
|
|
44
48
|
"peerDependencies": {
|
|
45
49
|
"@iwsdk/core": ">=1.0.0 <2.0.0",
|