@realitycollective/threejs-interactions 0.1.0-preview.1 → 0.1.0-preview.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- Change log for the Reality Collective WebXR Interaction Extensions packages. All four packages are versioned and released together; the version below is the one carried by the `v<version>` release tag.
3
+ Change log for the Reality Collective WebXR Interaction Extensions packages. All five packages are versioned and released together; the version below is the one carried by the `v<version>` release tag.
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
 
@@ -13,15 +13,34 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
13
13
  - `@realitycollective/iwsdk-interactions` - Meta IWSDK adapter: player-rig poses and stateful gamepads as the provider, IWSDK's own targeting (`Pressed` / `Grabbed` tags) forwarded as pre-resolved hints, and native grab fulfilment when the app enables IWSDK grabbing/physics. One-call `registerInteractions(world)` setup.
14
14
  - `@realitycollective/xrblocks-interactions` - EXPERIMENTAL Google XR Blocks adapter, structurally typed against xrblocks v0.20.0 (`Input.getFrame()` ray sources and direct touches, pooled structs copied, no haptics upstream).
15
15
  - `demos/playground` - the standalone interaction playground: the full station set from one portable `InteractionDescriptor`, with client-side audio cues, opt-in controller haptics and client-side toss ballistics.
16
+ - `@realitycollective/webxr-interactions` - per-source velocity. `VelocityTracker` differentiates the grip poses the runtime already samples into linear and angular velocity, and the runtime runs one by default (`velocity: false` turns it off, `{ smoothing }` averages it). A source on its first frame, or one that reappears after dropping out, reports no velocity rather than a jump from its last known pose, and a provider that supplies its own velocity keeps it. Read it from `runtime.onSample(...)` or `runtime.getSource(id)`. The differentiation itself is `velocityBetween` from `@realitycollective/webxr-input`, re-exported here; `clampDeadzone` joins the math helpers.
17
+ - `@realitycollective/webxr-interactions` - `routeAudioToSink`, the audio counterpart to `routeHapticsToProvider`: an opt-in that plays the cues named in a map through a client-supplied sink. Cues absent from the map are ignored, so an app sonifies only the moments it has sounds for.
18
+ - `@realitycollective/iwsdk-interactions` and `@realitycollective/threejs-interactions` - presence control. `setPresenceVisible(target, visible)` shows and hides the hand and controller visuals per side; the IWSDK adapter adds `setPresenceModality(mode)` to force hands or controllers over the automatic choice, and the three.js adapter takes the app's own models through `registerVisual(handedness, root)`. Every provider reports whether it can do this through `capabilities.presence`; the XR Blocks adapter reports false, since XR Blocks exposes no way to hide its visuals.
19
+ - `@realitycollective/threejs-interactions` - native WebXR pose velocity. Where the browser reports `linearVelocity` or `angularVelocity` with a grip pose (or with the wrist joint a hand falls back to), the provider passes it through on the snapshot and the core's tracker leaves it alone; where it does not, the tracker derives velocity from consecutive poses as before. The desktop mouse source synthesises nothing: its grip rides the camera ray, so the derived velocity is camera motion, and hand mechanics should be gated on `handedness !== "none"`.
20
+ - `@realitycollective/babylon-interactions` - a Babylon.js adapter, the fourth engine. It reads a `WebXRDefaultExperience` (controllers, motion controller trigger and squeeze, hand-tracking joints, session manager) for input, with `scene.onPointerObservable` as the desktop fallback, haptics through the motion controller's `pulse`, and presence over the visuals Babylon builds - motion controller root meshes and hand meshes. Babylon picks the visual per input source, so there is no modality switch. Hit-testing is a sphere test over registered nodes, with an optional `pickWithRay` hook for mesh-accurate targeting; the transform port moves, rotates and scales Babylon nodes from rest. Structurally typed against the Babylon API rather than importing `@babylonjs/core`, as the XR Blocks adapter is, so an upstream release cannot break the install - and so the whole adapter is covered headlessly by structural fakes. Written against the documented Babylon 7 API and not yet exercised against a real Babylon runtime.
21
+ - Tests for `@realitycollective/iwsdk-interactions`, which had none. `@iwsdk/core` imports headlessly in node, so the provider is covered against a structural fake world with no module mocking, and a parity suite instantiates all four providers and runs the shared contract suite against each one. The package's coverage ratchet moves off zero, and the other three ratchets rise to the floor they now measure.
16
22
 
17
23
  ### Changed
18
24
 
25
+ - Every provider reports presence through `capabilities.presence`, the required key `@realitycollective/webxr-input` 0.1.1 adds to `InputCapabilities`. The class-level `supportsPresence` field the four adapters carried while that key was unpublished is gone. This fixes a client-visible defect on the IWSDK adapter, which built its capabilities without a `presence` key at all: an app that read `capabilities.presence` saw false and hid a feature that worked. The value each provider reports is the one its own field computed - always true on IWSDK, true once Babylon has built a visual to hide, true on three.js once a model is registered, and always false on XR Blocks.
26
+ - `@realitycollective/threejs-interactions` - `registerVisual` and the new `unregisterVisual` now re-derive capabilities and notify `onCapabilitiesChanged`. Presence on this adapter is genuinely conditional: your app builds its own hand and controller models, so `capabilities.presence` is false until you hand the first one over and false again once you take the last one back.
27
+ - The Input 0.1.1 types replace the local stand-ins that were waiting on them. `InputSourceSnapshot` now carries `linearVelocity` and `angularVelocity` itself, so the `InputSourceSnapshotWithVelocity` alias is gone; the local `poseVelocity` is gone in favour of the shipped `velocityBetween`; and the per-adapter `PresenceTarget` and `PresenceModality` declarations are gone. `PresenceModality` now comes from the contracts, and a presence target is written as the contract writes it, `Handedness | "all"`. All three names were re-exported from these packages and are removed from that surface.
28
+ - Every package now requires `@realitycollective/webxr-input` at `^0.1.1` rather than `^0.1.0`, because the code above uses members that only exist from 0.1.1. A fresh install resolved to 0.1.1 either way; the range says so now, so a consumer holding an older lockfile fails at install rather than at run time.
29
+ - The provider parity suite is now three lines over `inputProviderContractCases()` from `@realitycollective/webxr-input`, run against all four adapters, instead of a hand-written copy of the same checks. The shared cases are stricter: they assert the capability record has exactly the contract's keys and that every snapshot is well formed. Two adapter-specific checks stay on top of them.
30
+ - `@realitycollective/iwsdk-interactions` - `setPresenceVisible` and `setPresenceModality` write nothing when nothing changed. The provider holds what it last applied per side - visibility, modality and the hand-tracking capability presence is derived from - and skips a side whose three inputs are unchanged. An app pushing presence from a state subscription, which is the documented pattern, no longer walks every descendant of both visual families on every unrelated state change. The per-frame re-apply during `sample()` stays unconditional, because IWSDK re-asserts its own visuals from its update loop and a capability refresh can change the modality underneath the adapter.
31
+
32
+ - `@realitycollective/iwsdk-interactions` - `pulse` resolves the side from the handedness recorded while sampling, rather than parsing the source id. Parsing the id remains the fallback for an id the provider did not produce.
33
+
19
34
  - `@realitycollective/iwsdk-interactions` targets **IWSDK 0.5.x**. Its peer range is now `>=0.5.0 <0.6.0` and it is developed and tested against `@iwsdk/core` 0.5.3. The adapter is source-compatible with 0.4.x and needed no code change, but 0.5 is the only line exercised, so it is the only line supported. Every symbol and runtime member the provider uses is present in both, and it already reads `world.input.xr.gamepads` rather than the accessor 0.5 deprecates.
20
35
  - `demos/playground` no longer compiles UIKitML at build time. `@iwsdk/vite-plugin-uikitml` was discontinued at 0.4.2 and only ever did `JSON.stringify(parse(source))`, so the panel is served as `.uikitml` from `public/ui/` and parsed on load. The demo drops that plugin, and its station markup uses `rgba()` rather than `background-opacity`, which the 0.5 parser removed.
21
36
 
37
+ ### Deprecated
38
+
39
+ - `@realitycollective/webxr-interactions` - `InteractionRuntime.onSourcesSampled` is deprecated in favour of `onSample`. They are the same stream, and since `InputSourceSnapshot` carries the velocity fields itself the two signatures are now identical, so only one name is needed. Nothing is removed: existing callers keep working and the method will go in a later major release.
40
+
22
41
  ### Notes
23
42
 
24
- - All four packages depend on `@realitycollective/webxr-input`, released independently from the [WebXR-Input](https://github.com/realitycollective/WebXR-Input) repository. That package must be published before this one.
43
+ - All five packages depend on `@realitycollective/webxr-input`, released independently from the [WebXR-Input](https://github.com/realitycollective/WebXR-Input) repository. That package must be published before this one.
25
44
  - The interaction packages themselves carry no IWSDK coupling beyond the adapter: `webxr-interactions` is engine-free, and `threejs-interactions` and `xrblocks-interactions` peer only on three.js.
26
45
 
27
46
  [0.1.0]: https://github.com/realitycollective/WebXR-Interactions/commits/main
package/README.md CHANGED
@@ -16,6 +16,17 @@ It re-exports everything from [`@realitycollective/webxr-interactions`](https://
16
16
  | **Hit-testing** | three.js raycasting against your scene graph |
17
17
  | **Movement** | Moves and rotates three.js objects for grab, hinge, dial and slide |
18
18
  | **Desktop** | A mouse fallback, so the same scene is testable without a headset |
19
+ | **Presence** | Show and hide the user's own hand and controller models, once you have registered them |
20
+
21
+ ## Presence
22
+
23
+ `registerVisual(handedness, root)` is on this adapter and on no other, because a standalone three.js app builds its own hand and controller models. The IWSDK and Babylon adapters find models the engine already built; here there is nothing to show or hide until you hand one over. So `capabilities.presence` is false until the first `registerVisual` call and true afterwards, and it changes back if you give the last one back with `unregisterVisual`. Registering notifies `onCapabilitiesChanged` like any other capability change.
24
+
25
+ ```ts
26
+ provider.registerVisual("left", leftHandModel);
27
+ provider.registerVisual("right", rightHandModel);
28
+ provider.setPresenceVisible("left", false); // hide the left hand
29
+ ```
19
30
 
20
31
  ## Usage
21
32
 
@@ -11,8 +11,8 @@
11
11
  * Capabilities are derived from the LIVE session (never the requested
12
12
  * config) and re-published on session start/end and input-source changes.
13
13
  */
14
- import { type Camera, type WebXRManager } from "three";
15
- import { type HeadPose, type InputCapabilities, type InputProvider, type InputSourceSnapshot, type Unsubscribe } from "@realitycollective/webxr-input";
14
+ import { type Camera, type Object3D, type WebXRManager } from "three";
15
+ import { type Handedness, type HeadPose, type InputCapabilities, type InputProvider, type InputSourceSnapshot, type PresenceModality, type Unsubscribe } from "@realitycollective/webxr-input";
16
16
  export interface WebXRProviderContext {
17
17
  /** three.js `renderer.xr` (or anything with the same session surface). */
18
18
  xr: Pick<WebXRManager, "getSession" | "getReferenceSpace" | "getFrame">;
@@ -48,6 +48,7 @@ export declare class WebXRInputProvider implements InputProvider {
48
48
  private readonly capsListeners;
49
49
  private readonly sourceListeners;
50
50
  private readonly selectStates;
51
+ private readonly visuals;
51
52
  private boundSession;
52
53
  private readonly sessionHandlers;
53
54
  private mouseDown;
@@ -79,6 +80,37 @@ export declare class WebXRInputProvider implements InputProvider {
79
80
  private squeezing;
80
81
  private sampleDesktop;
81
82
  getHeadPose(): HeadPose;
83
+ /**
84
+ * Hand the provider the scene object that represents one side's hand or
85
+ * controller, so presence can hide and show it. Nothing here builds those
86
+ * models: a standalone three.js app owns its own, and this is the hook
87
+ * that lets it be driven from the same place as an IWSDK app's.
88
+ *
89
+ * Registering the same side twice replaces the previous root. The first
90
+ * registration turns `capabilities.presence` on and notifies every
91
+ * capabilities listener, so an app can gate its presence UI on the
92
+ * capability like it does on every other one.
93
+ */
94
+ registerVisual(handedness: "left" | "right", root: Object3D): void;
95
+ /**
96
+ * Give a side's visual back, for an app that tears its models down.
97
+ * Dropping the last one turns `capabilities.presence` off again.
98
+ * Returns false when that side had nothing registered.
99
+ */
100
+ unregisterVisual(handedness: "left" | "right"): boolean;
101
+ /**
102
+ * Show or hide registered visuals. Returns false when the targeted side
103
+ * has no registered visual. `"none"` targets no side, so it reports
104
+ * whether presence is usable at all without changing anything.
105
+ */
106
+ setPresenceVisible(target: Handedness | "all", visible: boolean): boolean;
107
+ /**
108
+ * Always false. Switching between hand and controller models means owning
109
+ * both, and this provider owns neither - the app registered one root per
110
+ * side and decides for itself what that root contains. IWSDK builds both
111
+ * families, so its adapter implements this.
112
+ */
113
+ setPresenceModality(_mode: PresenceModality): boolean;
82
114
  pulse(sourceId: string, intensity: number, durationMs: number): boolean;
83
115
  dispose(): void;
84
116
  }
@@ -11,7 +11,7 @@
11
11
  * Capabilities are derived from the LIVE session (never the requested
12
12
  * config) and re-published on session start/end and input-source changes.
13
13
  */
14
- import { Raycaster, Vector2, Vector3, Quaternion } from "three";
14
+ import { Raycaster, Vector2, Vector3, Quaternion, } from "three";
15
15
  import { NO_CAPABILITIES, } from "@realitycollective/webxr-input";
16
16
  /** Where the desktop fallback puts its synthetic grip along the mouse ray. */
17
17
  const DEFAULT_DESKTOP_GRIP_DISTANCE = 1;
@@ -23,6 +23,7 @@ export class WebXRInputProvider {
23
23
  capsListeners = new Set();
24
24
  sourceListeners = new Set();
25
25
  selectStates = new Map();
26
+ visuals = new Map();
26
27
  boundSession = null;
27
28
  sessionHandlers = [];
28
29
  // Desktop pointer state.
@@ -161,6 +162,10 @@ export class WebXRInputProvider {
161
162
  // this `grabs` stays "none" off-headset and capability negotiation
162
163
  // disables every one of them, leaving press as the only usable behaviour.
163
164
  grabs: desktop ? "poseOnly" : NO_CAPABILITIES.grabs,
165
+ // Presence is opt-in here and nowhere else: a standalone three.js app
166
+ // builds its own hand and controller models, so there is nothing to
167
+ // show or hide until one is handed over with `registerVisual`.
168
+ presence: this.visuals.size > 0,
164
169
  };
165
170
  if (session) {
166
171
  for (const source of session.inputSources) {
@@ -238,8 +243,10 @@ export class WebXRInputProvider {
238
243
  }
239
244
  if (source.gripSpace) {
240
245
  const gripPose = frame.getPose(source.gripSpace, referenceSpace);
241
- if (gripPose)
246
+ if (gripPose) {
242
247
  snapshot.gripPose = xrPoseToTuple(gripPose);
248
+ applyPoseVelocity(snapshot, gripPose);
249
+ }
243
250
  }
244
251
  if (source.hand) {
245
252
  const joint = source.hand.get("index-finger-tip");
@@ -252,8 +259,10 @@ export class WebXRInputProvider {
252
259
  if (!snapshot.gripPose) {
253
260
  const wrist = source.hand.get("wrist");
254
261
  const wristPose = wrist ? frame.getJointPose?.(wrist, referenceSpace) : undefined;
255
- if (wristPose)
262
+ if (wristPose) {
256
263
  snapshot.gripPose = xrPoseToTuple(wristPose);
264
+ applyPoseVelocity(snapshot, wristPose);
265
+ }
257
266
  }
258
267
  }
259
268
  if ((source.gamepad?.hapticActuators?.length ?? 0) > 0) {
@@ -332,6 +341,62 @@ export class WebXRInputProvider {
332
341
  quaternion: [this.q.x, this.q.y, this.q.z, this.q.w],
333
342
  };
334
343
  }
344
+ // -- presence -----------------------------------------------------------------
345
+ /**
346
+ * Hand the provider the scene object that represents one side's hand or
347
+ * controller, so presence can hide and show it. Nothing here builds those
348
+ * models: a standalone three.js app owns its own, and this is the hook
349
+ * that lets it be driven from the same place as an IWSDK app's.
350
+ *
351
+ * Registering the same side twice replaces the previous root. The first
352
+ * registration turns `capabilities.presence` on and notifies every
353
+ * capabilities listener, so an app can gate its presence UI on the
354
+ * capability like it does on every other one.
355
+ */
356
+ registerVisual(handedness, root) {
357
+ this.visuals.set(handedness, root);
358
+ this.refreshCapabilities();
359
+ }
360
+ /**
361
+ * Give a side's visual back, for an app that tears its models down.
362
+ * Dropping the last one turns `capabilities.presence` off again.
363
+ * Returns false when that side had nothing registered.
364
+ */
365
+ unregisterVisual(handedness) {
366
+ const removed = this.visuals.delete(handedness);
367
+ if (removed)
368
+ this.refreshCapabilities();
369
+ return removed;
370
+ }
371
+ /**
372
+ * Show or hide registered visuals. Returns false when the targeted side
373
+ * has no registered visual. `"none"` targets no side, so it reports
374
+ * whether presence is usable at all without changing anything.
375
+ */
376
+ setPresenceVisible(target, visible) {
377
+ if (target === "none")
378
+ return this.visuals.size > 0;
379
+ let applied = false;
380
+ for (const side of ["left", "right"]) {
381
+ if (target !== "all" && target !== side)
382
+ continue;
383
+ const root = this.visuals.get(side);
384
+ if (!root)
385
+ continue;
386
+ root.visible = visible;
387
+ applied = true;
388
+ }
389
+ return applied;
390
+ }
391
+ /**
392
+ * Always false. Switching between hand and controller models means owning
393
+ * both, and this provider owns neither - the app registered one root per
394
+ * side and decides for itself what that root contains. IWSDK builds both
395
+ * families, so its adapter implements this.
396
+ */
397
+ setPresenceModality(_mode) {
398
+ return false;
399
+ }
335
400
  pulse(sourceId, intensity, durationMs) {
336
401
  const session = this.boundSession;
337
402
  if (!session)
@@ -363,4 +428,22 @@ function xrPoseToTuple(pose) {
363
428
  const o = pose.transform.orientation;
364
429
  return { position: [p.x, p.y, p.z], quaternion: [o.x, o.y, o.z, o.w] };
365
430
  }
431
+ /**
432
+ * Pass the platform's own velocity through when it reports one.
433
+ *
434
+ * `XRPose.linearVelocity` (metres per second) and `angularVelocity` (radians
435
+ * per second about each axis) are optional, and undefined on a user agent that
436
+ * does not compute them. Both are expressed in the reference space the pose was
437
+ * taken in, which is the space the snapshot's grip pose already uses. What is
438
+ * left undefined here is filled in by the core's `VelocityTracker`, which
439
+ * differentiates consecutive grip poses and never overwrites a supplied value.
440
+ */
441
+ function applyPoseVelocity(snapshot, pose) {
442
+ const linear = pose.linearVelocity;
443
+ if (linear)
444
+ snapshot.linearVelocity = [linear.x, linear.y, linear.z];
445
+ const angular = pose.angularVelocity;
446
+ if (angular)
447
+ snapshot.angularVelocity = [angular.x, angular.y, angular.z];
448
+ }
366
449
  //# sourceMappingURL=webxr-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webxr-provider.js","sourceRoot":"","sources":["../src/webxr-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAkC,MAAM,OAAO,CAAC;AAChG,OAAO,EACL,eAAe,GAOhB,MAAM,gCAAgC,CAAC;AAqCxC,8EAA8E;AAC9E,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,gEAAgE;AAChE,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEnC,MAAM,OAAO,kBAAkB;IACZ,OAAO,CAAuB;IACvC,YAAY,CAAoB;IACvB,aAAa,GAAG,IAAI,GAAG,EAAkC,CAAC;IAC1D,eAAe,GAAG,IAAI,GAAG,EAAc,CAAC;IACxC,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAC;IAC9D,YAAY,GAAqB,IAAI,CAAC;IAC7B,eAAe,GAAmC,EAAE,CAAC;IAEtE,yBAAyB;IACjB,SAAS,GAAG,KAAK,CAAC;IAClB,UAAU,GAAG,KAAK,CAAC;IACV,QAAQ,GAAG,IAAI,OAAO,EAAE,CAAC;IACzB,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;IAC5B,SAAS,CAAc;IACvB,YAAY,CAAS;IACrB,aAAa,CAA4B;IAE1D,SAAS;IACQ,CAAC,GAAG,IAAI,OAAO,EAAE,CAAC;IAClB,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;IACrB,UAAU,GAAG,IAAI,OAAO,EAAE,CAAC;IAC3B,SAAS,GAAG,IAAI,OAAO,EAAE,CAAC;IAC1B,cAAc,GAAG,IAAI,UAAU,EAAE,CAAC;IAEnD,YAAY,OAA6B;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,mBAAmB,IAAI,6BAA6B,CAAC;QACjF,wEAAwE;QACxE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,oBAAoB,IAAI,MAAM,CAAC;QAC5D,IAAI,CAAC,YAAY,GAAG,EAAE,GAAG,eAAe,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACvE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC7B,CAAC;IAEO,SAAS,CAAC,OAAgC;QAChD,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC;QACrC,MAAM,MAAM,GAAG,CAAC,KAAmB,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;YAC7C,IAAI,CAAC,QAAQ,CAAC,GAAG,CACf,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAClD,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CACpD,CAAC;QACJ,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,KAAmB,EAAE,EAAE;YACrC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YAC/C,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC;QACF,MAAM,IAAI,GAAG,CAAC,KAAmB,EAAE,EAAE;YACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QAClD,CAAC,CAAC;QACF,OAAO,CAAC,gBAAgB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAChD,OAAO,CAAC,gBAAgB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAChD,4EAA4E;QAC5E,4EAA4E;QAC5E,2EAA2E;QAC3E,uCAAuC;QACvC,MAAM,aAAa,GACjB,IAAI,CAAC,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,KAAY,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACxF,IAAI,aAAa;YAAE,OAAO,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QAC1E,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC3C,OAAO,GAAG,EAAE;YACV,OAAO,CAAC,mBAAmB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YACnD,OAAO,CAAC,mBAAmB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YACnD,IAAI,aAAa;gBAAE,OAAO,CAAC,mBAAmB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;YAC7E,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAChD,CAAC,CAAC;IACJ,CAAC;IAED,mEAAmE;IAC3D,WAAW;QACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;QAC7C,IAAI,OAAO,KAAK,IAAI,CAAC,YAAY;YAAE,OAAO,OAAO,CAAC;QAElD,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACnD,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvD,CAAC;YACD,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC5B,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,SAAS,GAAG,CAAC,SAAkB,EAAE,EAAE,CAAC,CAAC,KAAY,EAAE,EAAE;gBACzD,MAAM,MAAM,GAAI,KAA4B,CAAC,WAAW,CAAC;gBACzD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBACvC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;YAC9B,CAAC,CAAC;YACF,MAAM,UAAU,GAAG,CAAC,SAAkB,EAAE,EAAE,CAAC,CAAC,KAAY,EAAE,EAAE;gBAC1D,MAAM,MAAM,GAAI,KAA4B,CAAC,WAAW,CAAC;gBACzD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBACvC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;YAC9B,CAAC,CAAC;YACF,MAAM,eAAe,GAAG,GAAG,EAAE;gBAC3B,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC;oBAAE,QAAQ,EAAE,CAAC;YAC/D,CAAC,CAAC;YACF,MAAM,KAAK,GAAG,GAAG,EAAE;gBACjB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;gBAChC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;gBAC1B,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,CAAC,CAAC;YACF,MAAM,KAAK,GAAmC;gBAC5C,CAAC,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;gBAChC,CAAC,WAAW,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;gBAC/B,CAAC,cAAc,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;gBAClC,CAAC,YAAY,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;gBACjC,CAAC,oBAAoB,EAAE,eAAe,CAAC;gBACvC,CAAC,KAAK,EAAE,KAAK,CAAC;aACf,CAAC;YACF,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;gBACpC,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7C,CAAC;YACD,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,WAAW,CAAC,MAAqB;QACvC,IAAI,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;YAC/C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,mBAAmB;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,CAAC;QAC1E,MAAM,IAAI,GAAsB;YAC9B,GAAG,eAAe;YAClB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,IAAI;YACV,SAAS,EAAE,OAAO;YAClB,0EAA0E;YAC1E,sEAAsE;YACtE,8DAA8D;YAC9D,2EAA2E;YAC3E,wEAAwE;YACxE,mEAAmE;YACnE,0EAA0E;YAC1E,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK;SACpD,CAAC;QACF,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBAC1C,IAAI,MAAM,CAAC,cAAc;oBAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBAC5C,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI;oBAAE,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;gBAC7D,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;oBAChB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;oBACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;oBAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBACpB,CAAC;gBACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;wBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBAC7E,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,qBAAqB,CAAC,QAAwC;QAC5D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED,gBAAgB,CAAC,QAAoB;QACnC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,MAAM;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,oEAAoE;QACpE,6BAA6B;QAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI;gBAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC;QAC3D,IAAI,CAAC,KAAK,IAAI,CAAC,cAAc;YAAE,OAAO,EAAE,CAAC;QAEzC,MAAM,SAAS,GAA0B,EAAE,CAAC;QAC5C,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,EAAE,EAAE,CAAC;YACpF,MAAM,QAAQ,GAAwB;gBACpC,EAAE;gBACF,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY;gBACzC,UAAU,EACR,MAAM,CAAC,UAAU,KAAK,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,OAAO;oBAC3D,CAAC,CAAC,MAAM,CAAC,UAAU;oBACnB,CAAC,CAAC,MAAM;gBACZ,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC;gBACvC,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;aAC1C,CAAC;YAEF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;YACrE,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC;gBACrC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC;gBACxC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC/B,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC7C,QAAQ,CAAC,GAAG,GAAG;oBACb,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;oBACvB,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC1C,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;gBACjE,IAAI,QAAQ;oBAAE,QAAQ,CAAC,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAChB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAClD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAClF,IAAI,SAAS,EAAE,CAAC;oBACd,MAAM,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC;oBACvC,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtC,CAAC;gBACD,oEAAoE;gBACpE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;oBACvB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACvC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;oBAClF,IAAI,SAAS;wBAAE,QAAQ,CAAC,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,QAAQ,CAAC,gBAAgB,GAAG,IAAI,CAAC;YACnC,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,mEAAmE;IAC3D,WAAW,CAAC,MAAqB,EAAE,KAAkB;QAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;QACtD,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IAEO,YAAY,CAAC,MAAqB,EAAE,KAAkB;QAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;QACtD,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,uDAAuD;IAC/C,SAAS;QACf,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC;QACzD,IAAI,IAAI,CAAC,aAAa,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QAC3D,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,aAAa;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAExB,4EAA4E;QAC5E,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,MAAM,GACV,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,CAAC,kBAAkB,KAAK,OAAO,CAAC;QAC7E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEnE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;QACjD,2EAA2E;QAC3E,uEAAuE;QACvE,qEAAqE;QACrE,WAAW;QACX,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7E,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAE5D,OAAO;YACL;gBACE,EAAE,EAAE,OAAO;gBACX,IAAI,EAAE,WAAW;gBACjB,UAAU,EAAE,MAAM;gBAClB,GAAG,EAAE;oBACH,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;oBACtC,SAAS,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;iBACnD;gBACD,QAAQ,EAAE;oBACR,QAAQ,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;oBAChE,UAAU,EAAE;wBACV,IAAI,CAAC,cAAc,CAAC,CAAC;wBACrB,IAAI,CAAC,cAAc,CAAC,CAAC;wBACrB,IAAI,CAAC,cAAc,CAAC,CAAC;wBACrB,IAAI,CAAC,cAAc,CAAC,CAAC;qBACtB;iBACF;gBACD,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9B,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAClC;SACF,CAAC;IACJ,CAAC;IAED,WAAW;QACT,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO;YACL,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;SACrD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAgB,EAAE,SAAiB,EAAE,UAAkB;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC;QAClC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAC1C,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,EAAE,EAAE,CAAC;YACpF,IAAI,EAAE,KAAK,QAAQ;gBAAE,SAAS;YAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC;YACtD,IAAI,CAAC,QAAQ;gBAAE,OAAO,KAAK,CAAC;YAC5B,KAAM,QAAmE,CAAC,KAAK,EAAE,CAC/E,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EACnC,UAAU,CACX,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO;QACL,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACnD,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;IACrC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC","sourcesContent":["/**\n * WebXRInputProvider - the standalone, framework-free input provider.\n *\n * Speaks the browser's OpenXR binding directly: `XRSession` input sources\n * (`targetRaySpace`, `gripSpace`, `hand` joints), `selectstart/selectend`\n * and `squeezestart/squeezeend`, gamepad analog values where present, and\n * haptic actuators. Falls back to a desktop mouse pointer (a ray from the\n * camera through the cursor) when no immersive session is live, so every\n * consumer runs in a flat browser too.\n *\n * Capabilities are derived from the LIVE session (never the requested\n * config) and re-published on session start/end and input-source changes.\n */\nimport { Raycaster, Vector2, Vector3, Quaternion, type Camera, type WebXRManager } from \"three\";\nimport {\n NO_CAPABILITIES,\n type HeadPose,\n type InputCapabilities,\n type InputProvider,\n type InputSourceSnapshot,\n type PoseTuple,\n type Unsubscribe,\n} from \"@realitycollective/webxr-input\";\n\nexport interface WebXRProviderContext {\n /** three.js `renderer.xr` (or anything with the same session surface). */\n xr: Pick<WebXRManager, \"getSession\" | \"getReferenceSpace\" | \"getFrame\">;\n /** The rendering camera - head pose in XR, pointer projection on desktop. */\n camera: Camera;\n /** DOM element for the desktop mouse fallback (canvas). Omit to disable. */\n domElement?: HTMLElement;\n /**\n * Metres along the mouse ray at which the desktop fallback places its\n * synthetic grip pose. Hand-driven behaviours (grab, hinge, dial, slide)\n * track a grip position; a mouse has none, so one is projected onto the ray.\n * Set this near the distance of the things being manipulated - too short and\n * levers barely swing, too long and they over-swing. Default 1 metre.\n */\n desktopGripDistance?: number;\n /**\n * Which mouse button reports `squeeze` on desktop. Default `\"left\"`.\n *\n * The right button is NOT the default, because the shared desktop camera\n * controls (`DesktopControls` in `@realitycollective/xrblocks-uiextensions`)\n * bind right-drag to look and reserve the left button for interaction. That\n * leaves the left button to carry both actions, which is fine as long as an\n * interactable does not mix a select-driven behaviour (press) with a\n * grab-driven one (grab, hinge, dial, slide) - such an interactable would\n * receive both on a single click. Use `\"right\"` only in an app that does not\n * use right-drag to look.\n */\n desktopSqueezeButton?: \"left\" | \"right\" | \"none\";\n}\n\ninterface SelectState {\n selecting: boolean;\n squeezing: boolean;\n}\n\n/** Where the desktop fallback puts its synthetic grip along the mouse ray. */\nconst DEFAULT_DESKTOP_GRIP_DISTANCE = 1;\n\n/** Centre of the viewport, used while the pointer is locked. */\nconst ZERO_NDC = new Vector2(0, 0);\n\nexport class WebXRInputProvider implements InputProvider {\n private readonly context: WebXRProviderContext;\n private capabilities: InputCapabilities;\n private readonly capsListeners = new Set<(c: InputCapabilities) => void>();\n private readonly sourceListeners = new Set<() => void>();\n private readonly selectStates = new Map<XRInputSource, SelectState>();\n private boundSession: XRSession | null = null;\n private readonly sessionHandlers: Array<[string, EventListener]> = [];\n\n // Desktop pointer state.\n private mouseDown = false;\n private mouseRight = false;\n private readonly mouseNdc = new Vector2();\n private readonly raycaster = new Raycaster();\n private readonly detachDom: Unsubscribe;\n private readonly gripDistance: number;\n private readonly squeezeButton: \"left\" | \"right\" | \"none\";\n\n // Temps.\n private readonly v = new Vector3();\n private readonly q = new Quaternion();\n private readonly pointerNdc = new Vector2();\n private readonly gripPoint = new Vector3();\n private readonly gripQuaternion = new Quaternion();\n\n constructor(context: WebXRProviderContext) {\n this.context = context;\n this.gripDistance = context.desktopGripDistance ?? DEFAULT_DESKTOP_GRIP_DISTANCE;\n // Read before attachDom - it decides whether to claim the context menu.\n this.squeezeButton = context.desktopSqueezeButton ?? \"left\";\n this.capabilities = { ...NO_CAPABILITIES, headPose: true, gaze: true };\n this.detachDom = this.attachDom(context.domElement);\n this.refreshCapabilities();\n }\n\n private attachDom(element: HTMLElement | undefined): Unsubscribe {\n if (!element) return () => undefined;\n const onMove = (event: PointerEvent) => {\n const rect = element.getBoundingClientRect();\n this.mouseNdc.set(\n ((event.clientX - rect.left) / rect.width) * 2 - 1,\n -((event.clientY - rect.top) / rect.height) * 2 + 1,\n );\n };\n const onDown = (event: PointerEvent) => {\n if (event.button === 0) this.mouseDown = true;\n if (event.button === 2) this.mouseRight = true;\n onMove(event);\n };\n const onUp = (event: PointerEvent) => {\n if (event.button === 0) this.mouseDown = false;\n if (event.button === 2) this.mouseRight = false;\n };\n element.addEventListener(\"pointermove\", onMove);\n element.addEventListener(\"pointerdown\", onDown);\n // Only when the right button carries squeeze. Otherwise the menu belongs to\n // whoever else is listening - the shared camera controls suppress it there.\n // Without this, a right press opens the menu, the matching pointerup never\n // arrives, and the squeeze latches on.\n const onContextMenu =\n this.squeezeButton === \"right\" ? (event: Event) => event.preventDefault() : undefined;\n if (onContextMenu) element.addEventListener(\"contextmenu\", onContextMenu);\n window.addEventListener(\"pointerup\", onUp);\n return () => {\n element.removeEventListener(\"pointermove\", onMove);\n element.removeEventListener(\"pointerdown\", onDown);\n if (onContextMenu) element.removeEventListener(\"contextmenu\", onContextMenu);\n window.removeEventListener(\"pointerup\", onUp);\n };\n }\n\n /** Bind/unbind session listeners as the session comes and goes. */\n private syncSession(): XRSession | null {\n const session = this.context.xr.getSession();\n if (session === this.boundSession) return session;\n\n if (this.boundSession) {\n for (const [type, handler] of this.sessionHandlers) {\n this.boundSession.removeEventListener(type, handler);\n }\n this.sessionHandlers.length = 0;\n this.selectStates.clear();\n }\n this.boundSession = session;\n if (session) {\n const setSelect = (selecting: boolean) => (event: Event) => {\n const source = (event as XRInputSourceEvent).inputSource;\n const state = this.selectState(source);\n state.selecting = selecting;\n };\n const setSqueeze = (squeezing: boolean) => (event: Event) => {\n const source = (event as XRInputSourceEvent).inputSource;\n const state = this.selectState(source);\n state.squeezing = squeezing;\n };\n const onSourcesChange = () => {\n this.refreshCapabilities();\n for (const listener of [...this.sourceListeners]) listener();\n };\n const onEnd = () => {\n this.boundSession = null;\n this.sessionHandlers.length = 0;\n this.selectStates.clear();\n this.refreshCapabilities();\n };\n const pairs: Array<[string, EventListener]> = [\n [\"selectstart\", setSelect(true)],\n [\"selectend\", setSelect(false)],\n [\"squeezestart\", setSqueeze(true)],\n [\"squeezeend\", setSqueeze(false)],\n [\"inputsourceschange\", onSourcesChange],\n [\"end\", onEnd],\n ];\n for (const [type, handler] of pairs) {\n session.addEventListener(type, handler);\n this.sessionHandlers.push([type, handler]);\n }\n this.refreshCapabilities();\n }\n return session;\n }\n\n private selectState(source: XRInputSource): SelectState {\n let state = this.selectStates.get(source);\n if (!state) {\n state = { selecting: false, squeezing: false };\n this.selectStates.set(source, state);\n }\n return state;\n }\n\n private refreshCapabilities(): void {\n const session = this.boundSession ?? this.context.xr.getSession();\n const desktop = this.context.domElement !== undefined && session === null;\n const next: InputCapabilities = {\n ...NO_CAPABILITIES,\n headPose: true,\n gaze: true,\n pointer2d: desktop,\n // NOTE: `rays` stays false on desktop on purpose. sample() uses it as the\n // \"a session just ended\" sentinel, so setting it here would re-derive\n // capabilities on every frame for the whole life of the page.\n // The desktop fallback synthesises a grip on the mouse ray, so hand-driven\n // behaviours (grab, hinge, dial, slide) negotiate successfully. Without\n // this `grabs` stays \"none\" off-headset and capability negotiation\n // disables every one of them, leaving press as the only usable behaviour.\n grabs: desktop ? \"poseOnly\" : NO_CAPABILITIES.grabs,\n };\n if (session) {\n for (const source of session.inputSources) {\n if (source.targetRaySpace) next.rays = true;\n if (source.gripSpace || source.hand) next.grabs = \"poseOnly\";\n if (source.hand) {\n next.handJoints = true;\n next.pokes = true;\n next.pinch = true;\n }\n if (source.gamepad) {\n next.buttonsAxes = true;\n if ((source.gamepad.hapticActuators?.length ?? 0) > 0) next.haptics = true;\n }\n }\n }\n const changed = JSON.stringify(next) !== JSON.stringify(this.capabilities);\n this.capabilities = next;\n if (changed) {\n for (const listener of [...this.capsListeners]) listener(next);\n }\n }\n\n getCapabilities(): InputCapabilities {\n return this.capabilities;\n }\n\n onCapabilitiesChanged(listener: (c: InputCapabilities) => void): Unsubscribe {\n this.capsListeners.add(listener);\n return () => this.capsListeners.delete(listener);\n }\n\n onSourcesChanged(listener: () => void): Unsubscribe {\n this.sourceListeners.add(listener);\n return () => this.sourceListeners.delete(listener);\n }\n\n sample(): readonly InputSourceSnapshot[] {\n const session = this.syncSession();\n // The session-null path also re-derives capabilities when a session\n // just ended between frames.\n if (!session) {\n if (this.capabilities.rays) this.refreshCapabilities();\n return this.sampleDesktop();\n }\n\n const frame = this.context.xr.getFrame();\n const referenceSpace = this.context.xr.getReferenceSpace();\n if (!frame || !referenceSpace) return [];\n\n const snapshots: InputSourceSnapshot[] = [];\n let index = 0;\n for (const source of session.inputSources) {\n const state = this.selectState(source);\n const id = `${source.handedness}-${source.hand ? \"hand\" : \"controller\"}-${index++}`;\n const snapshot: InputSourceSnapshot = {\n id,\n kind: source.hand ? \"hand\" : \"controller\",\n handedness:\n source.handedness === \"left\" || source.handedness === \"right\"\n ? source.handedness\n : \"none\",\n select: this.selectValue(source, state),\n squeeze: this.squeezeValue(source, state),\n };\n\n const rayPose = frame.getPose(source.targetRaySpace, referenceSpace);\n if (rayPose) {\n const p = rayPose.transform.position;\n const o = rayPose.transform.orientation;\n this.q.set(o.x, o.y, o.z, o.w);\n this.v.set(0, 0, -1).applyQuaternion(this.q);\n snapshot.ray = {\n origin: [p.x, p.y, p.z],\n direction: [this.v.x, this.v.y, this.v.z],\n };\n }\n if (source.gripSpace) {\n const gripPose = frame.getPose(source.gripSpace, referenceSpace);\n if (gripPose) snapshot.gripPose = xrPoseToTuple(gripPose);\n }\n if (source.hand) {\n const joint = source.hand.get(\"index-finger-tip\");\n const jointPose = joint ? frame.getJointPose?.(joint, referenceSpace) : undefined;\n if (jointPose) {\n const p = jointPose.transform.position;\n snapshot.indexTip = [p.x, p.y, p.z];\n }\n // Hands without a gripSpace still need a carry pose: use the wrist.\n if (!snapshot.gripPose) {\n const wrist = source.hand.get(\"wrist\");\n const wristPose = wrist ? frame.getJointPose?.(wrist, referenceSpace) : undefined;\n if (wristPose) snapshot.gripPose = xrPoseToTuple(wristPose);\n }\n }\n if ((source.gamepad?.hapticActuators?.length ?? 0) > 0) {\n snapshot.hapticsAvailable = true;\n }\n snapshots.push(snapshot);\n }\n return snapshots;\n }\n\n /** Analog trigger where available, else the select event state. */\n private selectValue(source: XRInputSource, state: SelectState): number {\n const analog = source.gamepad?.buttons[0]?.value;\n if (analog !== undefined && analog > 0) return analog;\n return state.selecting ? 1 : 0;\n }\n\n private squeezeValue(source: XRInputSource, state: SelectState): number {\n const analog = source.gamepad?.buttons[1]?.value;\n if (analog !== undefined && analog > 0) return analog;\n return state.squeezing ? 1 : 0;\n }\n\n /** Is the configured squeeze button currently held? */\n private squeezing(): boolean {\n if (this.squeezeButton === \"left\") return this.mouseDown;\n if (this.squeezeButton === \"right\") return this.mouseRight;\n return false;\n }\n\n private sampleDesktop(): readonly InputSourceSnapshot[] {\n const element = this.context.domElement;\n if (!element) return [];\n\n // Under pointer lock the cursor stops moving and clientX/clientY freeze, so\n // the tracked NDC would stick wherever the pointer was when it locked. A\n // locked pointer aims from the centre of the viewport instead.\n const locked =\n typeof document !== \"undefined\" && document.pointerLockElement === element;\n this.pointerNdc.copy(locked ? ZERO_NDC : this.mouseNdc);\n this.raycaster.setFromCamera(this.pointerNdc, this.context.camera);\n\n const { origin, direction } = this.raycaster.ray;\n // A mouse has no grip. Hand-driven behaviours read `gripPose.position` (or\n // fall back to `ray.origin`, which is the camera and barely moves), so\n // project a grip onto the ray to give them something that tracks the\n // pointer.\n this.gripPoint.copy(direction).multiplyScalar(this.gripDistance).add(origin);\n this.context.camera.getWorldQuaternion(this.gripQuaternion);\n\n return [\n {\n id: \"mouse\",\n kind: \"pointer2d\",\n handedness: \"none\",\n ray: {\n origin: [origin.x, origin.y, origin.z],\n direction: [direction.x, direction.y, direction.z],\n },\n gripPose: {\n position: [this.gripPoint.x, this.gripPoint.y, this.gripPoint.z],\n quaternion: [\n this.gripQuaternion.x,\n this.gripQuaternion.y,\n this.gripQuaternion.z,\n this.gripQuaternion.w,\n ],\n },\n select: this.mouseDown ? 1 : 0,\n squeeze: this.squeezing() ? 1 : 0,\n },\n ];\n }\n\n getHeadPose(): HeadPose {\n this.context.camera.getWorldPosition(this.v);\n this.context.camera.getWorldQuaternion(this.q);\n return {\n position: [this.v.x, this.v.y, this.v.z],\n quaternion: [this.q.x, this.q.y, this.q.z, this.q.w],\n };\n }\n\n pulse(sourceId: string, intensity: number, durationMs: number): boolean {\n const session = this.boundSession;\n if (!session) return false;\n let index = 0;\n for (const source of session.inputSources) {\n const id = `${source.handedness}-${source.hand ? \"hand\" : \"controller\"}-${index++}`;\n if (id !== sourceId) continue;\n const actuator = source.gamepad?.hapticActuators?.[0];\n if (!actuator) return false;\n void (actuator as { pulse?: (i: number, d: number) => Promise<boolean> }).pulse?.(\n Math.min(1, Math.max(0, intensity)),\n durationMs,\n );\n return true;\n }\n return false;\n }\n\n dispose(): void {\n this.detachDom();\n if (this.boundSession) {\n for (const [type, handler] of this.sessionHandlers) {\n this.boundSession.removeEventListener(type, handler);\n }\n }\n }\n}\n\nfunction xrPoseToTuple(pose: XRPose): PoseTuple {\n const p = pose.transform.position;\n const o = pose.transform.orientation;\n return { position: [p.x, p.y, p.z], quaternion: [o.x, o.y, o.z, o.w] };\n}\n"]}
1
+ {"version":3,"file":"webxr-provider.js","sourceRoot":"","sources":["../src/webxr-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebXRInputProvider - the standalone, framework-free input provider.\n *\n * Speaks the browser's OpenXR binding directly: `XRSession` input sources\n * (`targetRaySpace`, `gripSpace`, `hand` joints), `selectstart/selectend`\n * and `squeezestart/squeezeend`, gamepad analog values where present, and\n * haptic actuators. Falls back to a desktop mouse pointer (a ray from the\n * camera through the cursor) when no immersive session is live, so every\n * consumer runs in a flat browser too.\n *\n * Capabilities are derived from the LIVE session (never the requested\n * config) and re-published on session start/end and input-source changes.\n */\nimport {\n Raycaster,\n Vector2,\n Vector3,\n Quaternion,\n type Camera,\n type Object3D,\n type WebXRManager,\n} from \"three\";\nimport {\n NO_CAPABILITIES,\n type Handedness,\n type HeadPose,\n type InputCapabilities,\n type InputProvider,\n type InputSourceSnapshot,\n type PoseTuple,\n type PresenceModality,\n type Unsubscribe,\n} from \"@realitycollective/webxr-input\";\n\nexport interface WebXRProviderContext {\n /** three.js `renderer.xr` (or anything with the same session surface). */\n xr: Pick<WebXRManager, \"getSession\" | \"getReferenceSpace\" | \"getFrame\">;\n /** The rendering camera - head pose in XR, pointer projection on desktop. */\n camera: Camera;\n /** DOM element for the desktop mouse fallback (canvas). Omit to disable. */\n domElement?: HTMLElement;\n /**\n * Metres along the mouse ray at which the desktop fallback places its\n * synthetic grip pose. Hand-driven behaviours (grab, hinge, dial, slide)\n * track a grip position; a mouse has none, so one is projected onto the ray.\n * Set this near the distance of the things being manipulated - too short and\n * levers barely swing, too long and they over-swing. Default 1 metre.\n */\n desktopGripDistance?: number;\n /**\n * Which mouse button reports `squeeze` on desktop. Default `\"left\"`.\n *\n * The right button is NOT the default, because the shared desktop camera\n * controls (`DesktopControls` in `@realitycollective/xrblocks-uiextensions`)\n * bind right-drag to look and reserve the left button for interaction. That\n * leaves the left button to carry both actions, which is fine as long as an\n * interactable does not mix a select-driven behaviour (press) with a\n * grab-driven one (grab, hinge, dial, slide) - such an interactable would\n * receive both on a single click. Use `\"right\"` only in an app that does not\n * use right-drag to look.\n */\n desktopSqueezeButton?: \"left\" | \"right\" | \"none\";\n}\n\ninterface SelectState {\n selecting: boolean;\n squeezing: boolean;\n}\n\n/** Where the desktop fallback puts its synthetic grip along the mouse ray. */\nconst DEFAULT_DESKTOP_GRIP_DISTANCE = 1;\n\n/** Centre of the viewport, used while the pointer is locked. */\nconst ZERO_NDC = new Vector2(0, 0);\n\nexport class WebXRInputProvider implements InputProvider {\n private readonly context: WebXRProviderContext;\n private capabilities: InputCapabilities;\n private readonly capsListeners = new Set<(c: InputCapabilities) => void>();\n private readonly sourceListeners = new Set<() => void>();\n private readonly selectStates = new Map<XRInputSource, SelectState>();\n private readonly visuals = new Map<\"left\" | \"right\", Object3D>();\n private boundSession: XRSession | null = null;\n private readonly sessionHandlers: Array<[string, EventListener]> = [];\n\n // Desktop pointer state.\n private mouseDown = false;\n private mouseRight = false;\n private readonly mouseNdc = new Vector2();\n private readonly raycaster = new Raycaster();\n private readonly detachDom: Unsubscribe;\n private readonly gripDistance: number;\n private readonly squeezeButton: \"left\" | \"right\" | \"none\";\n\n // Temps.\n private readonly v = new Vector3();\n private readonly q = new Quaternion();\n private readonly pointerNdc = new Vector2();\n private readonly gripPoint = new Vector3();\n private readonly gripQuaternion = new Quaternion();\n\n constructor(context: WebXRProviderContext) {\n this.context = context;\n this.gripDistance = context.desktopGripDistance ?? DEFAULT_DESKTOP_GRIP_DISTANCE;\n // Read before attachDom - it decides whether to claim the context menu.\n this.squeezeButton = context.desktopSqueezeButton ?? \"left\";\n this.capabilities = { ...NO_CAPABILITIES, headPose: true, gaze: true };\n this.detachDom = this.attachDom(context.domElement);\n this.refreshCapabilities();\n }\n\n private attachDom(element: HTMLElement | undefined): Unsubscribe {\n if (!element) return () => undefined;\n const onMove = (event: PointerEvent) => {\n const rect = element.getBoundingClientRect();\n this.mouseNdc.set(\n ((event.clientX - rect.left) / rect.width) * 2 - 1,\n -((event.clientY - rect.top) / rect.height) * 2 + 1,\n );\n };\n const onDown = (event: PointerEvent) => {\n if (event.button === 0) this.mouseDown = true;\n if (event.button === 2) this.mouseRight = true;\n onMove(event);\n };\n const onUp = (event: PointerEvent) => {\n if (event.button === 0) this.mouseDown = false;\n if (event.button === 2) this.mouseRight = false;\n };\n element.addEventListener(\"pointermove\", onMove);\n element.addEventListener(\"pointerdown\", onDown);\n // Only when the right button carries squeeze. Otherwise the menu belongs to\n // whoever else is listening - the shared camera controls suppress it there.\n // Without this, a right press opens the menu, the matching pointerup never\n // arrives, and the squeeze latches on.\n const onContextMenu =\n this.squeezeButton === \"right\" ? (event: Event) => event.preventDefault() : undefined;\n if (onContextMenu) element.addEventListener(\"contextmenu\", onContextMenu);\n window.addEventListener(\"pointerup\", onUp);\n return () => {\n element.removeEventListener(\"pointermove\", onMove);\n element.removeEventListener(\"pointerdown\", onDown);\n if (onContextMenu) element.removeEventListener(\"contextmenu\", onContextMenu);\n window.removeEventListener(\"pointerup\", onUp);\n };\n }\n\n /** Bind/unbind session listeners as the session comes and goes. */\n private syncSession(): XRSession | null {\n const session = this.context.xr.getSession();\n if (session === this.boundSession) return session;\n\n if (this.boundSession) {\n for (const [type, handler] of this.sessionHandlers) {\n this.boundSession.removeEventListener(type, handler);\n }\n this.sessionHandlers.length = 0;\n this.selectStates.clear();\n }\n this.boundSession = session;\n if (session) {\n const setSelect = (selecting: boolean) => (event: Event) => {\n const source = (event as XRInputSourceEvent).inputSource;\n const state = this.selectState(source);\n state.selecting = selecting;\n };\n const setSqueeze = (squeezing: boolean) => (event: Event) => {\n const source = (event as XRInputSourceEvent).inputSource;\n const state = this.selectState(source);\n state.squeezing = squeezing;\n };\n const onSourcesChange = () => {\n this.refreshCapabilities();\n for (const listener of [...this.sourceListeners]) listener();\n };\n const onEnd = () => {\n this.boundSession = null;\n this.sessionHandlers.length = 0;\n this.selectStates.clear();\n this.refreshCapabilities();\n };\n const pairs: Array<[string, EventListener]> = [\n [\"selectstart\", setSelect(true)],\n [\"selectend\", setSelect(false)],\n [\"squeezestart\", setSqueeze(true)],\n [\"squeezeend\", setSqueeze(false)],\n [\"inputsourceschange\", onSourcesChange],\n [\"end\", onEnd],\n ];\n for (const [type, handler] of pairs) {\n session.addEventListener(type, handler);\n this.sessionHandlers.push([type, handler]);\n }\n this.refreshCapabilities();\n }\n return session;\n }\n\n private selectState(source: XRInputSource): SelectState {\n let state = this.selectStates.get(source);\n if (!state) {\n state = { selecting: false, squeezing: false };\n this.selectStates.set(source, state);\n }\n return state;\n }\n\n private refreshCapabilities(): void {\n const session = this.boundSession ?? this.context.xr.getSession();\n const desktop = this.context.domElement !== undefined && session === null;\n const next: InputCapabilities = {\n ...NO_CAPABILITIES,\n headPose: true,\n gaze: true,\n pointer2d: desktop,\n // NOTE: `rays` stays false on desktop on purpose. sample() uses it as the\n // \"a session just ended\" sentinel, so setting it here would re-derive\n // capabilities on every frame for the whole life of the page.\n // The desktop fallback synthesises a grip on the mouse ray, so hand-driven\n // behaviours (grab, hinge, dial, slide) negotiate successfully. Without\n // this `grabs` stays \"none\" off-headset and capability negotiation\n // disables every one of them, leaving press as the only usable behaviour.\n grabs: desktop ? \"poseOnly\" : NO_CAPABILITIES.grabs,\n // Presence is opt-in here and nowhere else: a standalone three.js app\n // builds its own hand and controller models, so there is nothing to\n // show or hide until one is handed over with `registerVisual`.\n presence: this.visuals.size > 0,\n };\n if (session) {\n for (const source of session.inputSources) {\n if (source.targetRaySpace) next.rays = true;\n if (source.gripSpace || source.hand) next.grabs = \"poseOnly\";\n if (source.hand) {\n next.handJoints = true;\n next.pokes = true;\n next.pinch = true;\n }\n if (source.gamepad) {\n next.buttonsAxes = true;\n if ((source.gamepad.hapticActuators?.length ?? 0) > 0) next.haptics = true;\n }\n }\n }\n const changed = JSON.stringify(next) !== JSON.stringify(this.capabilities);\n this.capabilities = next;\n if (changed) {\n for (const listener of [...this.capsListeners]) listener(next);\n }\n }\n\n getCapabilities(): InputCapabilities {\n return this.capabilities;\n }\n\n onCapabilitiesChanged(listener: (c: InputCapabilities) => void): Unsubscribe {\n this.capsListeners.add(listener);\n return () => this.capsListeners.delete(listener);\n }\n\n onSourcesChanged(listener: () => void): Unsubscribe {\n this.sourceListeners.add(listener);\n return () => this.sourceListeners.delete(listener);\n }\n\n sample(): readonly InputSourceSnapshot[] {\n const session = this.syncSession();\n // The session-null path also re-derives capabilities when a session\n // just ended between frames.\n if (!session) {\n if (this.capabilities.rays) this.refreshCapabilities();\n return this.sampleDesktop();\n }\n\n const frame = this.context.xr.getFrame();\n const referenceSpace = this.context.xr.getReferenceSpace();\n if (!frame || !referenceSpace) return [];\n\n const snapshots: InputSourceSnapshot[] = [];\n let index = 0;\n for (const source of session.inputSources) {\n const state = this.selectState(source);\n const id = `${source.handedness}-${source.hand ? \"hand\" : \"controller\"}-${index++}`;\n const snapshot: InputSourceSnapshot = {\n id,\n kind: source.hand ? \"hand\" : \"controller\",\n handedness:\n source.handedness === \"left\" || source.handedness === \"right\"\n ? source.handedness\n : \"none\",\n select: this.selectValue(source, state),\n squeeze: this.squeezeValue(source, state),\n };\n\n const rayPose = frame.getPose(source.targetRaySpace, referenceSpace);\n if (rayPose) {\n const p = rayPose.transform.position;\n const o = rayPose.transform.orientation;\n this.q.set(o.x, o.y, o.z, o.w);\n this.v.set(0, 0, -1).applyQuaternion(this.q);\n snapshot.ray = {\n origin: [p.x, p.y, p.z],\n direction: [this.v.x, this.v.y, this.v.z],\n };\n }\n if (source.gripSpace) {\n const gripPose = frame.getPose(source.gripSpace, referenceSpace);\n if (gripPose) {\n snapshot.gripPose = xrPoseToTuple(gripPose);\n applyPoseVelocity(snapshot, gripPose);\n }\n }\n if (source.hand) {\n const joint = source.hand.get(\"index-finger-tip\");\n const jointPose = joint ? frame.getJointPose?.(joint, referenceSpace) : undefined;\n if (jointPose) {\n const p = jointPose.transform.position;\n snapshot.indexTip = [p.x, p.y, p.z];\n }\n // Hands without a gripSpace still need a carry pose: use the wrist.\n if (!snapshot.gripPose) {\n const wrist = source.hand.get(\"wrist\");\n const wristPose = wrist ? frame.getJointPose?.(wrist, referenceSpace) : undefined;\n if (wristPose) {\n snapshot.gripPose = xrPoseToTuple(wristPose);\n applyPoseVelocity(snapshot, wristPose);\n }\n }\n }\n if ((source.gamepad?.hapticActuators?.length ?? 0) > 0) {\n snapshot.hapticsAvailable = true;\n }\n snapshots.push(snapshot);\n }\n return snapshots;\n }\n\n /** Analog trigger where available, else the select event state. */\n private selectValue(source: XRInputSource, state: SelectState): number {\n const analog = source.gamepad?.buttons[0]?.value;\n if (analog !== undefined && analog > 0) return analog;\n return state.selecting ? 1 : 0;\n }\n\n private squeezeValue(source: XRInputSource, state: SelectState): number {\n const analog = source.gamepad?.buttons[1]?.value;\n if (analog !== undefined && analog > 0) return analog;\n return state.squeezing ? 1 : 0;\n }\n\n /** Is the configured squeeze button currently held? */\n private squeezing(): boolean {\n if (this.squeezeButton === \"left\") return this.mouseDown;\n if (this.squeezeButton === \"right\") return this.mouseRight;\n return false;\n }\n\n private sampleDesktop(): readonly InputSourceSnapshot[] {\n const element = this.context.domElement;\n if (!element) return [];\n\n // Under pointer lock the cursor stops moving and clientX/clientY freeze, so\n // the tracked NDC would stick wherever the pointer was when it locked. A\n // locked pointer aims from the centre of the viewport instead.\n const locked =\n typeof document !== \"undefined\" && document.pointerLockElement === element;\n this.pointerNdc.copy(locked ? ZERO_NDC : this.mouseNdc);\n this.raycaster.setFromCamera(this.pointerNdc, this.context.camera);\n\n const { origin, direction } = this.raycaster.ray;\n // A mouse has no grip. Hand-driven behaviours read `gripPose.position` (or\n // fall back to `ray.origin`, which is the camera and barely moves), so\n // project a grip onto the ray to give them something that tracks the\n // pointer.\n this.gripPoint.copy(direction).multiplyScalar(this.gripDistance).add(origin);\n this.context.camera.getWorldQuaternion(this.gripQuaternion);\n\n return [\n {\n id: \"mouse\",\n kind: \"pointer2d\",\n handedness: \"none\",\n ray: {\n origin: [origin.x, origin.y, origin.z],\n direction: [direction.x, direction.y, direction.z],\n },\n gripPose: {\n position: [this.gripPoint.x, this.gripPoint.y, this.gripPoint.z],\n quaternion: [\n this.gripQuaternion.x,\n this.gripQuaternion.y,\n this.gripQuaternion.z,\n this.gripQuaternion.w,\n ],\n },\n select: this.mouseDown ? 1 : 0,\n squeeze: this.squeezing() ? 1 : 0,\n },\n ];\n }\n\n getHeadPose(): HeadPose {\n this.context.camera.getWorldPosition(this.v);\n this.context.camera.getWorldQuaternion(this.q);\n return {\n position: [this.v.x, this.v.y, this.v.z],\n quaternion: [this.q.x, this.q.y, this.q.z, this.q.w],\n };\n }\n\n // -- presence -----------------------------------------------------------------\n\n /**\n * Hand the provider the scene object that represents one side's hand or\n * controller, so presence can hide and show it. Nothing here builds those\n * models: a standalone three.js app owns its own, and this is the hook\n * that lets it be driven from the same place as an IWSDK app's.\n *\n * Registering the same side twice replaces the previous root. The first\n * registration turns `capabilities.presence` on and notifies every\n * capabilities listener, so an app can gate its presence UI on the\n * capability like it does on every other one.\n */\n registerVisual(handedness: \"left\" | \"right\", root: Object3D): void {\n this.visuals.set(handedness, root);\n this.refreshCapabilities();\n }\n\n /**\n * Give a side's visual back, for an app that tears its models down.\n * Dropping the last one turns `capabilities.presence` off again.\n * Returns false when that side had nothing registered.\n */\n unregisterVisual(handedness: \"left\" | \"right\"): boolean {\n const removed = this.visuals.delete(handedness);\n if (removed) this.refreshCapabilities();\n return removed;\n }\n\n /**\n * Show or hide registered visuals. Returns false when the targeted side\n * has no registered visual. `\"none\"` targets no side, so it reports\n * whether presence is usable at all without changing anything.\n */\n setPresenceVisible(target: Handedness | \"all\", visible: boolean): boolean {\n if (target === \"none\") return this.visuals.size > 0;\n let applied = false;\n for (const side of [\"left\", \"right\"] as const) {\n if (target !== \"all\" && target !== side) continue;\n const root = this.visuals.get(side);\n if (!root) continue;\n root.visible = visible;\n applied = true;\n }\n return applied;\n }\n\n /**\n * Always false. Switching between hand and controller models means owning\n * both, and this provider owns neither - the app registered one root per\n * side and decides for itself what that root contains. IWSDK builds both\n * families, so its adapter implements this.\n */\n setPresenceModality(_mode: PresenceModality): boolean {\n return false;\n }\n\n pulse(sourceId: string, intensity: number, durationMs: number): boolean {\n const session = this.boundSession;\n if (!session) return false;\n let index = 0;\n for (const source of session.inputSources) {\n const id = `${source.handedness}-${source.hand ? \"hand\" : \"controller\"}-${index++}`;\n if (id !== sourceId) continue;\n const actuator = source.gamepad?.hapticActuators?.[0];\n if (!actuator) return false;\n void (actuator as { pulse?: (i: number, d: number) => Promise<boolean> }).pulse?.(\n Math.min(1, Math.max(0, intensity)),\n durationMs,\n );\n return true;\n }\n return false;\n }\n\n dispose(): void {\n this.detachDom();\n if (this.boundSession) {\n for (const [type, handler] of this.sessionHandlers) {\n this.boundSession.removeEventListener(type, handler);\n }\n }\n }\n}\n\nfunction xrPoseToTuple(pose: XRPose): PoseTuple {\n const p = pose.transform.position;\n const o = pose.transform.orientation;\n return { position: [p.x, p.y, p.z], quaternion: [o.x, o.y, o.z, o.w] };\n}\n\n/**\n * Pass the platform's own velocity through when it reports one.\n *\n * `XRPose.linearVelocity` (metres per second) and `angularVelocity` (radians\n * per second about each axis) are optional, and undefined on a user agent that\n * does not compute them. Both are expressed in the reference space the pose was\n * taken in, which is the space the snapshot's grip pose already uses. What is\n * left undefined here is filled in by the core's `VelocityTracker`, which\n * differentiates consecutive grip poses and never overwrites a supplied value.\n */\nfunction applyPoseVelocity(snapshot: InputSourceSnapshot, pose: XRPose): void {\n const linear = pose.linearVelocity;\n if (linear) snapshot.linearVelocity = [linear.x, linear.y, linear.z];\n const angular = pose.angularVelocity;\n if (angular) snapshot.angularVelocity = [angular.x, angular.y, angular.z];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realitycollective/threejs-interactions",
3
- "version": "0.1.0-preview.1",
3
+ "version": "0.1.0-preview.3",
4
4
  "description": "The default, standalone engine adapter for the Reality Collective Interaction Extensions: raw WebXR (the browser's OpenXR binding - XRSession, XRInputSource, select/squeeze, hand joints) plus three.js hit-testing and transform ports, with a desktop mouse fallback. No framework required. Re-exports the @realitycollective/webxr-interactions core.",
5
5
  "keywords": [
6
6
  "realitycollective",
@@ -33,8 +33,8 @@
33
33
  "typecheck": "tsc -p tsconfig.json --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@realitycollective/webxr-input": "^0.1.0",
37
- "@realitycollective/webxr-interactions": "^0.1.0-preview.1"
36
+ "@realitycollective/webxr-input": "^0.1.1",
37
+ "@realitycollective/webxr-interactions": "^0.1.0-preview.3"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "three": ">=0.170.0"