@threlte/xr 1.0.8 → 1.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.
Files changed (68) hide show
  1. package/dist/components/ARButton.svelte.d.ts +11 -24
  2. package/dist/components/Controller.svelte +31 -27
  3. package/dist/components/Controller.svelte.d.ts +7 -5
  4. package/dist/components/Hand.svelte +24 -28
  5. package/dist/components/Hand.svelte.d.ts +6 -4
  6. package/dist/components/Headset.svelte.d.ts +4 -2
  7. package/dist/components/VRButton.svelte.d.ts +5 -18
  8. package/dist/components/XR.svelte +55 -54
  9. package/dist/components/XR.svelte.d.ts +29 -27
  10. package/dist/components/XRButton.svelte +3 -3
  11. package/dist/components/XRButton.svelte.d.ts +26 -23
  12. package/dist/components/internal/Cursor.svelte.d.ts +4 -2
  13. package/dist/components/internal/PointerCursor.svelte +8 -7
  14. package/dist/components/internal/PointerCursor.svelte.d.ts +5 -3
  15. package/dist/components/internal/ShortRay.svelte +3 -3
  16. package/dist/components/internal/ShortRay.svelte.d.ts +5 -3
  17. package/dist/components/internal/TeleportCursor.svelte +8 -7
  18. package/dist/components/internal/TeleportCursor.svelte.d.ts +5 -3
  19. package/dist/components/internal/TeleportRay.svelte +5 -5
  20. package/dist/components/internal/TeleportRay.svelte.d.ts +5 -3
  21. package/dist/hooks/currentReadable.svelte.d.ts +5 -0
  22. package/dist/hooks/currentReadable.svelte.js +11 -0
  23. package/dist/hooks/useController.svelte.d.ts +13 -0
  24. package/dist/hooks/useController.svelte.js +22 -0
  25. package/dist/hooks/useHand.svelte.d.ts +12 -0
  26. package/dist/hooks/{useHand.js → useHand.svelte.js} +8 -5
  27. package/dist/hooks/{useHandJoint.d.ts → useHandJoint.svelte.d.ts} +1 -1
  28. package/dist/hooks/useHandJoint.svelte.js +21 -0
  29. package/dist/hooks/useHeadset.js +1 -1
  30. package/dist/hooks/useHitTest.svelte.js +67 -0
  31. package/dist/hooks/useTeleport.js +1 -2
  32. package/dist/hooks/useXR.d.ts +6 -7
  33. package/dist/hooks/useXR.js +10 -12
  34. package/dist/index.d.ts +5 -5
  35. package/dist/index.js +4 -4
  36. package/dist/internal/raf.d.ts +1 -0
  37. package/dist/internal/raf.js +2 -0
  38. package/dist/internal/setupControllers.js +13 -14
  39. package/dist/internal/setupHands.js +11 -9
  40. package/dist/internal/{setupHeadset.js → setupHeadset.svelte.js} +6 -6
  41. package/dist/internal/setupRaf.svelte.js +17 -0
  42. package/dist/internal/state.svelte.d.ts +51 -0
  43. package/dist/internal/state.svelte.js +40 -0
  44. package/dist/internal/useHandTrackingState.js +10 -7
  45. package/dist/lib/getXRSessionOptions.d.ts +1 -1
  46. package/dist/lib/toggleXRSession.d.ts +3 -3
  47. package/dist/lib/toggleXRSession.js +3 -3
  48. package/dist/plugins/pointerControls/compute.js +2 -6
  49. package/dist/plugins/pointerControls/index.js +4 -10
  50. package/dist/plugins/pointerControls/{setup.js → setup.svelte.js} +18 -19
  51. package/dist/plugins/teleportControls/compute.js +2 -6
  52. package/dist/plugins/teleportControls/index.js +4 -10
  53. package/dist/plugins/teleportControls/{setup.js → setup.svelte.js} +9 -10
  54. package/dist/types.d.ts +12 -18
  55. package/package.json +10 -10
  56. package/dist/hooks/useController.d.ts +0 -9
  57. package/dist/hooks/useController.js +0 -19
  58. package/dist/hooks/useHand.d.ts +0 -8
  59. package/dist/hooks/useHandJoint.js +0 -20
  60. package/dist/hooks/useHitTest.js +0 -79
  61. package/dist/internal/setupRaf.js +0 -16
  62. package/dist/internal/stores.d.ts +0 -43
  63. package/dist/internal/stores.js +0 -42
  64. /package/dist/hooks/{useHitTest.d.ts → useHitTest.svelte.d.ts} +0 -0
  65. /package/dist/internal/{setupHeadset.d.ts → setupHeadset.svelte.d.ts} +0 -0
  66. /package/dist/internal/{setupRaf.d.ts → setupRaf.svelte.d.ts} +0 -0
  67. /package/dist/plugins/pointerControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
  68. /package/dist/plugins/teleportControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
@@ -1,3 +1,12 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import XRButton from './XRButton.svelte';
3
+ type Props = Omit<ComponentProps<typeof XRButton>, 'mode' | 'sessionInit'> & {
4
+ sessionInit?: XRSessionInit & {
5
+ domOverlay?: {
6
+ root: HTMLElement;
7
+ } | undefined;
8
+ };
9
+ };
1
10
  /**
2
11
  * `<ARButton />` is an HTML `<button />` that can be used to init and display info about your immersive AR session.
3
12
  *
@@ -8,28 +17,6 @@
8
17
  * />
9
18
  * ```
10
19
  */
11
- declare const ArButton: import("svelte").Component<Omit<import("svelte/elements").HTMLButtonAttributes & {
12
- mode: XRSessionMode;
13
- sessionInit?: XRSessionInit & {
14
- domOverlay?: {
15
- root: HTMLElement;
16
- } | undefined;
17
- };
18
- force?: "enter" | "exit";
19
- styled?: boolean;
20
- children?: import("svelte").Snippet<[{
21
- state: "unsupported" | "insecure" | "blocked" | "supported";
22
- }]>;
23
- onclick?: (event: {
24
- state: "unsupported" | "insecure" | "blocked" | "supported";
25
- nativeEvent: MouseEvent;
26
- }) => void;
27
- onerror?: (error: Error) => void;
28
- }, "mode" | "sessionInit"> & {
29
- sessionInit?: XRSessionInit & {
30
- domOverlay?: {
31
- root: HTMLElement;
32
- } | undefined;
33
- };
34
- }, {}, "">;
20
+ declare const ArButton: import("svelte").Component<Props, {}, "">;
21
+ type ArButton = ReturnType<typeof ArButton>;
35
22
  export default ArButton;
@@ -1,13 +1,15 @@
1
1
  <!--
2
2
  @component `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model.
3
3
  -->
4
- <script
5
- lang="ts"
6
- module
7
- >
8
- import { T } from '@threlte/core'
9
- import { left as leftStore, right as rightStore } from '../hooks/useController'
10
- import { isHandTracking, pointerState, teleportState, controllerEvents } from '../internal/stores'
4
+ <script lang="ts">
5
+ import { T, useThrelte } from '@threlte/core'
6
+ import { controllers } from '../hooks/useController.svelte'
7
+ import {
8
+ isHandTracking,
9
+ pointerState,
10
+ teleportState,
11
+ controllerEvents
12
+ } from '../internal/state.svelte'
11
13
  import type { XRControllerEvents } from '../types'
12
14
  import PointerCursor from './internal/PointerCursor.svelte'
13
15
  import ShortRay from './internal/ShortRay.svelte'
@@ -15,13 +17,6 @@
15
17
  import TeleportRay from './internal/TeleportRay.svelte'
16
18
  import type { Snippet } from 'svelte'
17
19
 
18
- const stores = {
19
- left: leftStore,
20
- right: rightStore
21
- } as const
22
- </script>
23
-
24
- <script lang="ts">
25
20
  type Props = {
26
21
  children?: Snippet
27
22
  grip?: Snippet
@@ -75,10 +70,12 @@
75
70
  teleportCursor: teleportCursorSnippet
76
71
  }: Props = $props()
77
72
 
73
+ const { scene } = useThrelte()
74
+
78
75
  const handedness = $derived<'left' | 'right'>(left ? 'left' : right ? 'right' : hand ?? 'left')
79
76
 
80
- $effect.pre(() =>
81
- controllerEvents[handedness].set({
77
+ $effect.pre(() => {
78
+ controllerEvents[handedness] = {
82
79
  onconnected,
83
80
  ondisconnected,
84
81
  onselect,
@@ -87,20 +84,27 @@
87
84
  onsqueeze,
88
85
  onsqueezeend,
89
86
  onsqueezestart
90
- })
91
- )
92
-
93
- const store = $derived(stores[handedness])
94
- const grip = $derived($store?.grip)
95
- const targetRay = $derived($store?.targetRay)
96
- const model = $derived($store?.model)
97
- const hasPointerControls = $derived($pointerState[handedness].enabled)
98
- const hasTeleportControls = $derived($teleportState[handedness].enabled)
87
+ }
88
+
89
+ return () => {
90
+ controllerEvents[handedness] = undefined
91
+ }
92
+ })
93
+
94
+ const xrController = $derived(controllers[handedness])
95
+ const grip = $derived(xrController?.grip)
96
+ const targetRay = $derived(xrController?.targetRay)
97
+ const model = $derived(xrController?.model)
98
+ const hasPointerControls = $derived(pointerState[handedness].enabled)
99
+ const hasTeleportControls = $derived(teleportState[handedness].enabled)
99
100
  </script>
100
101
 
101
- {#if !$isHandTracking}
102
+ {#if !isHandTracking.current}
102
103
  {#if grip}
103
- <T is={grip}>
104
+ <T
105
+ is={grip}
106
+ attach={scene}
107
+ >
104
108
  {#if children}
105
109
  {@render children?.()}
106
110
  {:else}
@@ -1,7 +1,6 @@
1
1
  import type { XRControllerEvents } from '../types';
2
2
  import type { Snippet } from 'svelte';
3
- /** `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model. */
4
- declare const Controller: import("svelte").Component<{
3
+ type Props = {
5
4
  children?: Snippet;
6
5
  grip?: Snippet;
7
6
  targetRay?: Snippet;
@@ -9,7 +8,7 @@ declare const Controller: import("svelte").Component<{
9
8
  pointerCursor?: Snippet;
10
9
  teleportRay?: Snippet;
11
10
  teleportCursor?: Snippet;
12
- } & (XRControllerEvents & ({
11
+ } & XRControllerEvents & ({
13
12
  /** Whether the controller should be matched with the left hand. */
14
13
  left: true;
15
14
  right?: undefined;
@@ -21,8 +20,11 @@ declare const Controller: import("svelte").Component<{
21
20
  hand?: undefined;
22
21
  } | {
23
22
  /** Whether the controller should be matched with the left or right hand. */
24
- hand: "left" | "right";
23
+ hand: 'left' | 'right';
25
24
  left?: undefined;
26
25
  right?: undefined;
27
- })), {}, "">;
26
+ });
27
+ /** `<Controller />` represents a THREE.XRTargetRaySpace, a THREE.XRGripSpace, and a controller model. */
28
+ declare const Controller: import("svelte").Component<Props, {}, "">;
29
+ type Controller = ReturnType<typeof Controller>;
28
30
  export default Controller;
@@ -1,21 +1,11 @@
1
- <script
2
- lang="ts"
3
- module
4
- >
1
+ <script lang="ts">
5
2
  import { Group } from 'three'
6
3
  import { T, useThrelte, useTask } from '@threlte/core'
7
4
  import type { XRHandEvents } from '../types'
8
- import { isHandTracking, handEvents } from '../internal/stores'
9
- import { left as leftStore, right as rightStore } from '../hooks/useHand'
5
+ import { isHandTracking, handEvents } from '../internal/state.svelte'
6
+ import { hands } from '../hooks/useHand.svelte'
10
7
  import type { Snippet } from 'svelte'
11
8
 
12
- const stores = {
13
- left: leftStore,
14
- right: rightStore
15
- } as const
16
- </script>
17
-
18
- <script lang="ts">
19
9
  type Props = {
20
10
  children?: Snippet
21
11
  targetRay?: Snippet
@@ -56,19 +46,21 @@
56
46
  }: Props = $props()
57
47
 
58
48
  const { scene, renderer, scheduler, renderStage } = useThrelte()
59
- const { xr } = renderer
60
- const space = xr.getReferenceSpace()
61
49
 
62
50
  const handedness = $derived<'left' | 'right'>(left ? 'left' : right ? 'right' : hand ?? 'left')
63
51
 
64
- $effect.pre(() =>
65
- handEvents[handedness].set({
52
+ $effect.pre(() => {
53
+ handEvents[handedness] = {
66
54
  onconnected,
67
55
  ondisconnected,
68
56
  onpinchend,
69
57
  onpinchstart
70
- })
71
- )
58
+ }
59
+
60
+ return () => {
61
+ handEvents[handedness] = undefined
62
+ }
63
+ })
72
64
 
73
65
  const group = new Group()
74
66
 
@@ -82,7 +74,8 @@
82
74
  */
83
75
  const { start, stop } = useTask(
84
76
  () => {
85
- const frame = xr.getFrame()
77
+ const frame = renderer.xr.getFrame()
78
+ const space = renderer.xr.getReferenceSpace()
86
79
  const joint = inputSource?.get('wrist')
87
80
 
88
81
  if (joint === undefined || space === null) return
@@ -103,33 +96,36 @@
103
96
  )
104
97
 
105
98
  $effect.pre(() => {
106
- if ($isHandTracking && (wrist !== undefined || children !== undefined) && inputSource) {
99
+ if (isHandTracking.current && (wrist !== undefined || children !== undefined) && inputSource) {
107
100
  start()
108
101
  } else {
109
102
  stop()
110
103
  }
111
104
  })
112
105
 
113
- const store = $derived(stores[handedness])
114
- const inputSource = $derived($store?.inputSource)
115
- const model = $derived($store?.model)
106
+ const xrHand = $derived(hands[handedness])
107
+ const inputSource = $derived(xrHand?.inputSource)
108
+ const model = $derived(xrHand?.model)
116
109
  </script>
117
110
 
118
- {#if $store?.hand && $isHandTracking}
119
- <T is={$store.hand}>
111
+ {#if xrHand?.hand && isHandTracking.current}
112
+ <T
113
+ is={xrHand.hand}
114
+ attach={scene}
115
+ >
120
116
  {#if children === undefined}
121
117
  <T is={model} />
122
118
  {/if}
123
119
  </T>
124
120
 
125
121
  {#if targetRay !== undefined}
126
- <T is={$store.targetRay}>
122
+ <T is={xrHand.targetRay}>
127
123
  {@render targetRay()}
128
124
  </T>
129
125
  {/if}
130
126
  {/if}
131
127
 
132
- {#if $isHandTracking}
128
+ {#if isHandTracking.current}
133
129
  <T
134
130
  is={group}
135
131
  attach={scene}
@@ -1,10 +1,10 @@
1
1
  import type { XRHandEvents } from '../types';
2
2
  import type { Snippet } from 'svelte';
3
- declare const Hand: import("svelte").Component<{
3
+ type Props = {
4
4
  children?: Snippet;
5
5
  targetRay?: Snippet;
6
6
  wrist?: Snippet;
7
- } & (XRHandEvents & ({
7
+ } & XRHandEvents & ({
8
8
  /** Whether the XRHand should be matched with the left hand. */
9
9
  left: true;
10
10
  right?: undefined;
@@ -16,8 +16,10 @@ declare const Hand: import("svelte").Component<{
16
16
  hand?: undefined;
17
17
  } | {
18
18
  /** Whether the XRHand should be matched with the left or right hand. */
19
- hand: "left" | "right";
19
+ hand: 'left' | 'right';
20
20
  left?: undefined;
21
21
  right?: undefined;
22
- })), {}, "">;
22
+ });
23
+ declare const Hand: import("svelte").Component<Props, {}, "">;
24
+ type Hand = ReturnType<typeof Hand>;
23
25
  export default Hand;
@@ -1,8 +1,10 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { Group } from 'three';
3
- declare const Headset: import("svelte").Component<{
3
+ interface Props {
4
4
  children?: Snippet<[{
5
5
  ref: Group;
6
6
  }]>;
7
- }, {}, "">;
7
+ }
8
+ declare const Headset: import("svelte").Component<Props, {}, "">;
9
+ type Headset = ReturnType<typeof Headset>;
8
10
  export default Headset;
@@ -1,3 +1,6 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import XRButton from './XRButton.svelte';
3
+ type Props = Omit<ComponentProps<typeof XRButton>, 'mode' | 'sessionInit'>;
1
4
  /**
2
5
  * `<VRButton />` is an HTML `<button />` that can be used to init and display info about your immersive VR session.
3
6
  *
@@ -8,22 +11,6 @@
8
11
  * />
9
12
  * ```
10
13
  */
11
- declare const VrButton: import("svelte").Component<Omit<import("svelte/elements").HTMLButtonAttributes & {
12
- mode: XRSessionMode;
13
- sessionInit?: XRSessionInit & {
14
- domOverlay?: {
15
- root: HTMLElement;
16
- } | undefined;
17
- };
18
- force?: "enter" | "exit";
19
- styled?: boolean;
20
- children?: import("svelte").Snippet<[{
21
- state: "unsupported" | "insecure" | "blocked" | "supported";
22
- }]>;
23
- onclick?: (event: {
24
- state: "unsupported" | "insecure" | "blocked" | "supported";
25
- nativeEvent: MouseEvent;
26
- }) => void;
27
- onerror?: (error: Error) => void;
28
- }, "mode" | "sessionInit">, {}, "">;
14
+ declare const VrButton: import("svelte").Component<Props, {}, "">;
15
+ type VrButton = ReturnType<typeof VrButton>;
29
16
  export default VrButton;
@@ -18,18 +18,18 @@ This should be placed within a Threlte `<Canvas />`.
18
18
 
19
19
  -->
20
20
  <script lang="ts">
21
- import { onMount, type Snippet } from 'svelte'
22
- import { useThrelte, watch } from '@threlte/core'
23
- import type { XRSessionEvent } from '../types'
21
+ import type { EventListener, WebXRManager, Event as ThreeEvent } from 'three'
22
+ import type { Snippet } from 'svelte'
23
+ import { useThrelte } from '@threlte/core'
24
24
  import {
25
25
  isHandTracking,
26
26
  isPresenting,
27
27
  referenceSpaceType,
28
28
  session,
29
- xr as xrStore
30
- } from '../internal/stores'
31
- import { setupRaf } from '../internal/setupRaf'
32
- import { setupHeadset } from '../internal/setupHeadset'
29
+ xr
30
+ } from '../internal/state.svelte'
31
+ import { setupRaf } from '../internal/setupRaf.svelte'
32
+ import { setupHeadset } from '../internal/setupHeadset.svelte'
33
33
  import { setupControllers } from '../internal/setupControllers'
34
34
  import { setupHands } from '../internal/setupHands'
35
35
 
@@ -59,17 +59,17 @@ This should be placed within a Threlte `<Canvas />`.
59
59
  fallback?: Snippet
60
60
  children?: Snippet
61
61
 
62
- /** Called as an XRSession is requested */
63
- onsessionstart?: (event: XRSessionEvent<'sessionstart'>) => void
62
+ /** Called as an XRSession is started */
63
+ onsessionstart?: (event: ThreeEvent<'sessionstart', WebXRManager>) => void
64
64
 
65
- /** Called after an XRSession is terminated */
66
- onsessionend?: (event: XRSessionEvent<'sessionend'>) => void
65
+ /** Called after an XRSession is ended */
66
+ onsessionend?: (event: XRSessionEvent) => void
67
67
 
68
68
  /** Called when an XRSession is hidden or unfocused. */
69
- onvisibilitychange?: (event: globalThis.XRSessionEvent) => void
69
+ onvisibilitychange?: (event: XRSessionEvent) => void
70
70
 
71
71
  /** Called when available inputsources change */
72
- oninputsourceschange?: (event: globalThis.XRSessionEvent) => void
72
+ oninputsourceschange?: (event: XRSessionEvent) => void
73
73
  }
74
74
 
75
75
  let {
@@ -85,7 +85,6 @@ This should be placed within a Threlte `<Canvas />`.
85
85
  }: Props = $props()
86
86
 
87
87
  const { renderer, renderMode } = useThrelte()
88
- const { xr } = renderer
89
88
 
90
89
  let originalRenderMode = $renderMode
91
90
 
@@ -94,56 +93,52 @@ This should be placed within a Threlte `<Canvas />`.
94
93
  setupControllers()
95
94
  setupHands()
96
95
 
97
- const handleSessionStart = () => {
98
- isPresenting.set(true)
99
- onsessionstart?.({ type: 'sessionstart', target: $session } as any)
96
+ const handleSessionStart: EventListener<object, 'sessionstart', WebXRManager> = (event) => {
97
+ isPresenting.current = true
98
+ onsessionstart?.(event)
100
99
  }
101
100
 
102
- const handleSessionEnd = () => {
103
- onsessionend?.({ type: 'sessionend', target: $session } as any)
104
- isPresenting.set(false)
105
- session.set(undefined)
101
+ const handleSessionEnd = (event: XRSessionEvent) => {
102
+ onsessionend?.(event)
103
+ isPresenting.current = false
104
+ session.current = undefined
106
105
  }
107
106
 
108
- const handleVisibilityChange = (event: globalThis.XRSessionEvent) => {
109
- onvisibilitychange?.({ ...event, target: $session! })
107
+ const handleVisibilityChange = (event: XRSessionEvent) => {
108
+ onvisibilitychange?.(event)
110
109
  }
111
110
 
112
111
  const handleInputSourcesChange = (event: XRInputSourcesChangeEvent) => {
113
- $isHandTracking = Object.values(event.session.inputSources).some((source) => source.hand)
114
- oninputsourceschange?.({ ...event, target: $session! })
112
+ isHandTracking.current = Object.values(event.session.inputSources).some((source) => source.hand)
113
+ oninputsourceschange?.(event)
115
114
  }
116
115
 
117
- const handleFramerateChange = (event: globalThis.XRSessionEvent) => {
118
- onvisibilitychange?.({ ...event, target: $session! })
116
+ const handleFramerateChange = (event: XRSessionEvent) => {
117
+ onvisibilitychange?.(event)
119
118
  }
120
119
 
121
- const updateTargetFrameRate = (frameRate?: number) => {
122
- if (frameRate === undefined) return
120
+ $effect(() => {
121
+ const currentSession = session.current
123
122
 
124
- try {
125
- $session?.updateTargetFrameRate(frameRate)
126
- } catch {
127
- // Do nothing
123
+ if (currentSession === undefined) {
124
+ return
128
125
  }
129
- }
130
-
131
- watch(session, (currentSession) => {
132
- if (currentSession === undefined) return
133
126
 
134
127
  currentSession.addEventListener('visibilitychange', handleVisibilityChange)
135
128
  currentSession.addEventListener('inputsourceschange', handleInputSourcesChange)
136
129
  currentSession.addEventListener('frameratechange', handleFramerateChange)
130
+ currentSession.addEventListener('end', handleSessionEnd)
137
131
 
138
132
  return () => {
139
133
  currentSession.removeEventListener('visibilitychange', handleVisibilityChange)
140
134
  currentSession.removeEventListener('inputsourceschange', handleInputSourcesChange)
141
135
  currentSession.removeEventListener('frameratechange', handleFramerateChange)
136
+ currentSession.removeEventListener('end', handleSessionEnd)
142
137
  }
143
138
  })
144
139
 
145
- watch(isPresenting, (presenting) => {
146
- if (presenting) {
140
+ $effect.pre(() => {
141
+ if (isPresenting.current) {
147
142
  originalRenderMode = renderMode.current
148
143
  renderMode.set('always')
149
144
  } else {
@@ -151,38 +146,44 @@ This should be placed within a Threlte `<Canvas />`.
151
146
  }
152
147
  })
153
148
 
154
- onMount(() => {
155
- $xrStore = xr
156
- xr.enabled = true
157
- xr.addEventListener('sessionstart', handleSessionStart)
158
- xr.addEventListener('sessionend', handleSessionEnd)
149
+ $effect.pre(() => {
150
+ const currentSession = session.current
151
+
152
+ xr.current = renderer.xr
153
+ renderer.xr.enabled = true
154
+ renderer.xr.addEventListener('sessionstart', handleSessionStart)
159
155
 
160
156
  return () => {
161
- $xrStore = undefined
162
- xr.enabled = false
163
- xr.removeEventListener('sessionstart', handleSessionStart)
164
- xr.removeEventListener('sessionend', handleSessionEnd)
157
+ xr.current = undefined
158
+ renderer.xr.enabled = false
159
+ renderer.xr.removeEventListener('sessionstart', handleSessionStart)
165
160
 
166
161
  // if unmounted while presenting (e.g. due to sveltekit navigation), end the session
167
- session.current?.end()
162
+ currentSession?.end()
168
163
  }
169
164
  })
170
165
 
171
166
  $effect.pre(() => {
172
- updateTargetFrameRate(frameRate)
167
+ if (frameRate === undefined) return
168
+
169
+ try {
170
+ session.current?.updateTargetFrameRate(frameRate)
171
+ } catch {
172
+ // Do nothing
173
+ }
173
174
  })
174
175
 
175
176
  $effect.pre(() => {
176
- xr.setFoveation(foveation)
177
+ renderer.xr.setFoveation(foveation)
177
178
  })
178
179
 
179
180
  $effect.pre(() => {
180
- xr.setReferenceSpaceType(referenceSpace)
181
- $referenceSpaceType = referenceSpace
181
+ renderer.xr.setReferenceSpaceType(referenceSpace)
182
+ referenceSpaceType.current = referenceSpace
182
183
  })
183
184
  </script>
184
185
 
185
- {#if $isPresenting}
186
+ {#if isPresenting.current}
186
187
  {@render children?.()}
187
188
  {:else}
188
189
  {@render fallback?.()}
@@ -1,23 +1,6 @@
1
- import { type Snippet } from 'svelte';
2
- import type { XRSessionEvent } from '../types';
3
- /**
4
- * `<XR />` is a WebXR manager that configures your scene for XR rendering and interaction.
5
- *
6
- * This should be placed within a Threlte `<Canvas />`.
7
- *
8
- * ```svelte
9
- * <XR
10
- * foveation={1}
11
- * frameRate={90}
12
- * referenceSpace='local-floor'
13
- * onsessionstart={(event: XREvent<XRManagerEvent>) => {}}
14
- * onsessionend={(event: XREvent<XRManagerEvent>) => {}}
15
- * onvisibilitychange={(event: XREvent<XRSessionEvent>) => {}}
16
- * oninputsourceschange={(event: XREvent<XRSessionEvent>) => {}}
17
- * />
18
- * ```
19
- */
20
- declare const Xr: import("svelte").Component<{
1
+ import type { WebXRManager, Event as ThreeEvent } from 'three';
2
+ import type { Snippet } from 'svelte';
3
+ interface Props {
21
4
  /**
22
5
  * Enables foveated rendering. Default is `1`, the three.js default.
23
6
  *
@@ -39,13 +22,32 @@ declare const Xr: import("svelte").Component<{
39
22
  referenceSpace?: XRReferenceSpaceType;
40
23
  fallback?: Snippet;
41
24
  children?: Snippet;
42
- /** Called as an XRSession is requested */
43
- onsessionstart?: (event: XRSessionEvent<"sessionstart">) => void;
44
- /** Called after an XRSession is terminated */
45
- onsessionend?: (event: XRSessionEvent<"sessionend">) => void;
25
+ /** Called as an XRSession is started */
26
+ onsessionstart?: (event: ThreeEvent<'sessionstart', WebXRManager>) => void;
27
+ /** Called after an XRSession is ended */
28
+ onsessionend?: (event: XRSessionEvent) => void;
46
29
  /** Called when an XRSession is hidden or unfocused. */
47
- onvisibilitychange?: (event: globalThis.XRSessionEvent) => void;
30
+ onvisibilitychange?: (event: XRSessionEvent) => void;
48
31
  /** Called when available inputsources change */
49
- oninputsourceschange?: (event: globalThis.XRSessionEvent) => void;
50
- }, {}, "">;
32
+ oninputsourceschange?: (event: XRSessionEvent) => void;
33
+ }
34
+ /**
35
+ * `<XR />` is a WebXR manager that configures your scene for XR rendering and interaction.
36
+ *
37
+ * This should be placed within a Threlte `<Canvas />`.
38
+ *
39
+ * ```svelte
40
+ * <XR
41
+ * foveation={1}
42
+ * frameRate={90}
43
+ * referenceSpace='local-floor'
44
+ * onsessionstart={(event: XREvent<XRManagerEvent>) => {}}
45
+ * onsessionend={(event: XREvent<XRManagerEvent>) => {}}
46
+ * onvisibilitychange={(event: XREvent<XRSessionEvent>) => {}}
47
+ * oninputsourceschange={(event: XREvent<XRSessionEvent>) => {}}
48
+ * />
49
+ * ```
50
+ */
51
+ declare const Xr: import("svelte").Component<Props, {}, "">;
52
+ type Xr = ReturnType<typeof Xr>;
51
53
  export default Xr;
@@ -21,7 +21,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
21
21
  import type { HTMLButtonAttributes } from 'svelte/elements'
22
22
  import { getXRSupportState } from '../lib/getXRSupportState'
23
23
  import { toggleXRSession } from '../lib/toggleXRSession'
24
- import { session, xr } from '../internal/stores'
24
+ import { isPresenting, xr } from '../internal/state.svelte'
25
25
  import type { Snippet } from 'svelte'
26
26
 
27
27
  type Props = HTMLButtonAttributes & {
@@ -57,7 +57,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
57
57
  type SupportState = 'unsupported' | 'insecure' | 'blocked' | 'supported'
58
58
 
59
59
  const handleButtonClick = async (nativeEvent: MouseEvent, state: SupportState) => {
60
- if (!$xr) {
60
+ if (!xr.current) {
61
61
  throw new Error(
62
62
  'The <XR> component was not created. This is required to start an XR session.'
63
63
  )
@@ -119,7 +119,7 @@ display info about your WebXR session. This is aliased by `ARButton` and
119
119
  {:else if state === 'blocked'}
120
120
  {modeText} blocked
121
121
  {:else if state === 'supported'}
122
- {$session ? 'Exit' : 'Enter'} {modeText}
122
+ {isPresenting.current ? 'Exit' : 'Enter'} {modeText}
123
123
  {/if}
124
124
  </button>
125
125
  {/await}