@threlte/xr 1.0.7 → 1.1.0

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 (71) hide show
  1. package/dist/components/ARButton.svelte.d.ts +11 -24
  2. package/dist/components/Controller.svelte +51 -52
  3. package/dist/components/Controller.svelte.d.ts +7 -5
  4. package/dist/components/Hand.svelte +35 -41
  5. package/dist/components/Hand.svelte.d.ts +6 -4
  6. package/dist/components/Headset.svelte +16 -7
  7. package/dist/components/Headset.svelte.d.ts +8 -18
  8. package/dist/components/VRButton.svelte.d.ts +5 -18
  9. package/dist/components/XR.svelte +50 -50
  10. package/dist/components/XR.svelte.d.ts +24 -22
  11. package/dist/components/XRButton.svelte +6 -6
  12. package/dist/components/XRButton.svelte.d.ts +26 -23
  13. package/dist/components/internal/Cursor.svelte +2 -1
  14. package/dist/components/internal/Cursor.svelte.d.ts +4 -2
  15. package/dist/components/internal/PointerCursor.svelte +32 -18
  16. package/dist/components/internal/PointerCursor.svelte.d.ts +5 -3
  17. package/dist/components/internal/ShortRay.svelte +6 -6
  18. package/dist/components/internal/ShortRay.svelte.d.ts +5 -3
  19. package/dist/components/internal/TeleportCursor.svelte +28 -17
  20. package/dist/components/internal/TeleportCursor.svelte.d.ts +5 -3
  21. package/dist/components/internal/TeleportRay.svelte +26 -20
  22. package/dist/components/internal/TeleportRay.svelte.d.ts +5 -3
  23. package/dist/hooks/currentReadable.svelte.d.ts +5 -0
  24. package/dist/hooks/currentReadable.svelte.js +11 -0
  25. package/dist/hooks/useController.svelte.d.ts +13 -0
  26. package/dist/hooks/useController.svelte.js +22 -0
  27. package/dist/hooks/useHand.svelte.d.ts +12 -0
  28. package/dist/hooks/{useHand.js → useHand.svelte.js} +8 -5
  29. package/dist/hooks/{useHandJoint.d.ts → useHandJoint.svelte.d.ts} +1 -1
  30. package/dist/hooks/useHandJoint.svelte.js +21 -0
  31. package/dist/hooks/useHeadset.js +1 -1
  32. package/dist/hooks/useHitTest.svelte.js +67 -0
  33. package/dist/hooks/useTeleport.js +1 -2
  34. package/dist/hooks/useXR.d.ts +6 -7
  35. package/dist/hooks/useXR.js +10 -12
  36. package/dist/index.d.ts +4 -4
  37. package/dist/index.js +4 -4
  38. package/dist/internal/raf.d.ts +1 -0
  39. package/dist/internal/raf.js +2 -0
  40. package/dist/internal/setupControllers.js +13 -14
  41. package/dist/internal/setupHands.js +8 -9
  42. package/dist/internal/{setupHeadset.js → setupHeadset.svelte.js} +6 -6
  43. package/dist/internal/setupRaf.svelte.js +17 -0
  44. package/dist/internal/state.svelte.d.ts +51 -0
  45. package/dist/internal/state.svelte.js +40 -0
  46. package/dist/internal/useHandTrackingState.js +10 -7
  47. package/dist/lib/getXRSessionOptions.d.ts +1 -1
  48. package/dist/lib/toggleXRSession.d.ts +3 -3
  49. package/dist/lib/toggleXRSession.js +3 -3
  50. package/dist/plugins/pointerControls/compute.js +2 -6
  51. package/dist/plugins/pointerControls/index.js +4 -10
  52. package/dist/plugins/pointerControls/{setup.js → setup.svelte.js} +21 -22
  53. package/dist/plugins/teleportControls/compute.js +2 -6
  54. package/dist/plugins/teleportControls/index.js +4 -10
  55. package/dist/plugins/teleportControls/{setup.js → setup.svelte.js} +9 -10
  56. package/package.json +8 -8
  57. package/dist/components/internal/ScenePortal.svelte +0 -23
  58. package/dist/components/internal/ScenePortal.svelte.d.ts +0 -20
  59. package/dist/hooks/useController.d.ts +0 -9
  60. package/dist/hooks/useController.js +0 -19
  61. package/dist/hooks/useHand.d.ts +0 -8
  62. package/dist/hooks/useHandJoint.js +0 -20
  63. package/dist/hooks/useHitTest.js +0 -79
  64. package/dist/internal/setupRaf.js +0 -16
  65. package/dist/internal/stores.d.ts +0 -43
  66. package/dist/internal/stores.js +0 -42
  67. /package/dist/hooks/{useHitTest.d.ts → useHitTest.svelte.d.ts} +0 -0
  68. /package/dist/internal/{setupHeadset.d.ts → setupHeadset.svelte.d.ts} +0 -0
  69. /package/dist/internal/{setupRaf.d.ts → setupRaf.svelte.d.ts} +0 -0
  70. /package/dist/plugins/pointerControls/{setup.d.ts → setup.svelte.d.ts} +0 -0
  71. /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,29 +1,22 @@
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 { writable } from 'svelte/store'
9
- import { T } from '@threlte/core'
10
- import { left as leftStore, right as rightStore } from '../hooks/useController'
11
- 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'
12
13
  import type { XRControllerEvents } from '../types'
13
14
  import PointerCursor from './internal/PointerCursor.svelte'
14
15
  import ShortRay from './internal/ShortRay.svelte'
15
- import ScenePortal from './internal/ScenePortal.svelte'
16
16
  import TeleportCursor from './internal/TeleportCursor.svelte'
17
17
  import TeleportRay from './internal/TeleportRay.svelte'
18
18
  import type { Snippet } from 'svelte'
19
19
 
20
- const stores = {
21
- left: leftStore,
22
- right: rightStore
23
- } as const
24
- </script>
25
-
26
- <script lang="ts">
27
20
  type Props = {
28
21
  children?: Snippet
29
22
  grip?: Snippet
@@ -77,11 +70,12 @@
77
70
  teleportCursor: teleportCursorSnippet
78
71
  }: Props = $props()
79
72
 
80
- const handedness = writable<'left' | 'right'>(left ? 'left' : right ? 'right' : hand)
81
- $effect.pre(() => handedness.set(left ? 'left' : right ? 'right' : (hand as 'left' | 'right')))
73
+ const { scene } = useThrelte()
74
+
75
+ const handedness = $derived<'left' | 'right'>(left ? 'left' : right ? 'right' : hand ?? 'left')
82
76
 
83
- $effect.pre(() =>
84
- controllerEvents[$handedness].set({
77
+ $effect.pre(() => {
78
+ controllerEvents[handedness] = {
85
79
  onconnected,
86
80
  ondisconnected,
87
81
  onselect,
@@ -90,20 +84,27 @@
90
84
  onsqueeze,
91
85
  onsqueezeend,
92
86
  onsqueezestart
93
- })
94
- )
95
-
96
- let store = $derived(stores[$handedness])
97
- let grip = $derived($store?.grip)
98
- let targetRay = $derived($store?.targetRay)
99
- let model = $derived($store?.model)
100
- let hasPointerControls = $derived($pointerState[$handedness].enabled)
101
- let 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)
102
100
  </script>
103
101
 
104
- {#if !$isHandTracking}
102
+ {#if !isHandTracking.current}
105
103
  {#if grip}
106
- <T is={grip}>
104
+ <T
105
+ is={grip}
106
+ attach={scene}
107
+ >
107
108
  {#if children}
108
109
  {@render children?.()}
109
110
  {:else}
@@ -120,7 +121,7 @@
120
121
 
121
122
  {#if hasPointerControls || hasTeleportControls}
122
123
  <ShortRay
123
- handedness={$handedness}
124
+ {handedness}
124
125
  children={pointerRaySnippet}
125
126
  />
126
127
  {/if}
@@ -128,24 +129,22 @@
128
129
  {/if}
129
130
  {/if}
130
131
 
131
- <ScenePortal>
132
- {#if hasPointerControls}
133
- <PointerCursor
134
- handedness={$handedness}
135
- children={pointerCursorSnippet}
136
- />
137
- {/if}
132
+ {#if hasPointerControls}
133
+ <PointerCursor
134
+ {handedness}
135
+ children={pointerCursorSnippet}
136
+ />
137
+ {/if}
138
138
 
139
- {#if hasTeleportControls && targetRay !== undefined}
140
- <TeleportRay
141
- {targetRay}
142
- handedness={$handedness}
143
- children={teleportRaySnippet}
144
- />
145
-
146
- <TeleportCursor
147
- handedness={$handedness}
148
- children={teleportCursorSnippet}
149
- />
150
- {/if}
151
- </ScenePortal>
139
+ {#if hasTeleportControls && targetRay !== undefined}
140
+ <TeleportRay
141
+ {targetRay}
142
+ {handedness}
143
+ children={teleportRaySnippet}
144
+ />
145
+
146
+ <TeleportCursor
147
+ {handedness}
148
+ children={teleportCursorSnippet}
149
+ />
150
+ {/if}
@@ -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,23 +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'
10
- import ScenePortal from './internal/ScenePortal.svelte'
11
- import { writable } from 'svelte/store'
5
+ import { isHandTracking, handEvents } from '../internal/state.svelte'
6
+ import { hands } from '../hooks/useHand.svelte'
12
7
  import type { Snippet } from 'svelte'
13
8
 
14
- const stores = {
15
- left: leftStore,
16
- right: rightStore
17
- } as const
18
- </script>
19
-
20
- <script lang="ts">
21
9
  type Props = {
22
10
  children?: Snippet
23
11
  targetRay?: Snippet
@@ -44,7 +32,7 @@
44
32
  }
45
33
  )
46
34
 
47
- let {
35
+ const {
48
36
  left,
49
37
  right,
50
38
  hand,
@@ -57,23 +45,24 @@
57
45
  wrist
58
46
  }: Props = $props()
59
47
 
60
- const { renderer, scheduler, renderStage } = useThrelte()
61
- const { xr } = renderer
62
- const space = xr.getReferenceSpace()
48
+ const { scene, renderer, scheduler, renderStage } = useThrelte()
63
49
 
64
- const handedness = writable<'left' | 'right'>(left ? 'left' : right ? 'right' : hand)
65
- $effect.pre(() => handedness.set(left ? 'left' : right ? 'right' : (hand as 'left' | 'right')))
50
+ const handedness = $derived<'left' | 'right'>(left ? 'left' : right ? 'right' : hand ?? 'left')
66
51
 
67
- $effect.pre(() =>
68
- handEvents[$handedness].set({
52
+ $effect.pre(() => {
53
+ handEvents[handedness] = {
69
54
  onconnected,
70
55
  ondisconnected,
71
56
  onpinchend,
72
57
  onpinchstart
73
- })
74
- )
58
+ }
59
+
60
+ return () => {
61
+ handEvents[handedness] = undefined
62
+ }
63
+ })
75
64
 
76
- let group = new Group()
65
+ const group = new Group()
77
66
 
78
67
  /**
79
68
  * Currently children of a hand XRSpace or model will not
@@ -85,7 +74,8 @@
85
74
  */
86
75
  const { start, stop } = useTask(
87
76
  () => {
88
- const frame = xr.getFrame()
77
+ const frame = renderer.xr.getFrame()
78
+ const space = renderer.xr.getReferenceSpace()
89
79
  const joint = inputSource?.get('wrist')
90
80
 
91
81
  if (joint === undefined || space === null) return
@@ -106,37 +96,41 @@
106
96
  )
107
97
 
108
98
  $effect.pre(() => {
109
- if ($isHandTracking && (wrist !== undefined || children !== undefined) && inputSource) {
99
+ if (isHandTracking.current && (wrist !== undefined || children !== undefined) && inputSource) {
110
100
  start()
111
101
  } else {
112
102
  stop()
113
103
  }
114
104
  })
115
105
 
116
- let store = $derived(stores[$handedness])
117
- let inputSource = $derived($store?.inputSource)
118
- let model = $derived($store?.model)
106
+ const xrHand = $derived(hands[handedness])
107
+ const inputSource = $derived(xrHand?.inputSource)
108
+ const model = $derived(xrHand?.model)
119
109
  </script>
120
110
 
121
- {#if $store?.hand && $isHandTracking}
122
- <T is={$store.hand}>
111
+ {#if xrHand?.hand && isHandTracking.current}
112
+ <T
113
+ is={xrHand.hand}
114
+ attach={scene}
115
+ >
123
116
  {#if children === undefined}
124
117
  <T is={model} />
125
118
  {/if}
126
119
  </T>
127
120
 
128
121
  {#if targetRay !== undefined}
129
- <T is={$store.targetRay}>
122
+ <T is={xrHand.targetRay}>
130
123
  {@render targetRay()}
131
124
  </T>
132
125
  {/if}
133
126
  {/if}
134
127
 
135
- {#if $isHandTracking}
136
- <ScenePortal>
137
- <T is={group}>
138
- {@render wrist?.()}
139
- {@render children?.()}
140
- </T>
141
- </ScenePortal>
128
+ {#if isHandTracking.current}
129
+ <T
130
+ is={group}
131
+ attach={scene}
132
+ >
133
+ {@render wrist?.()}
134
+ {@render children?.()}
135
+ </T>
142
136
  {/if}
@@ -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,13 +1,22 @@
1
1
  <script lang="ts">
2
- import { T } from '@threlte/core'
2
+ import type { Snippet } from 'svelte'
3
+ import type { Group } from 'three'
4
+ import { T, useThrelte } from '@threlte/core'
3
5
  import { useHeadset } from '../hooks/useHeadset'
4
- import ScenePortal from './internal/ScenePortal.svelte'
5
6
 
7
+ interface Props {
8
+ children?: Snippet<[{ ref: Group }]>
9
+ }
10
+
11
+ const { children }: Props = $props()
12
+
13
+ const { scene } = useThrelte()
6
14
  const headset = useHeadset()
7
15
  </script>
8
16
 
9
- <ScenePortal>
10
- <T is={headset}>
11
- <slot />
12
- </T>
13
- </ScenePortal>
17
+ <T
18
+ is={headset}
19
+ attach={scene}
20
+ >
21
+ {@render children?.({ ref: headset })}
22
+ </T>
@@ -1,20 +1,10 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
1
+ import type { Snippet } from 'svelte';
2
+ import type { Group } from 'three';
3
+ interface Props {
4
+ children?: Snippet<[{
5
+ ref: Group;
6
+ }]>;
13
7
  }
14
- declare const Headset: $$__sveltets_2_IsomorphicComponent<any, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {
17
- default: {};
18
- }, {}, string>;
19
- type Headset = InstanceType<typeof Headset>;
8
+ declare const Headset: import("svelte").Component<Props, {}, "">;
9
+ type Headset = ReturnType<typeof Headset>;
20
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 { onDestroy, onMount, type Snippet } from 'svelte'
22
- import { useThrelte, watch } from '@threlte/core'
21
+ import type { Snippet } from 'svelte'
22
+ import { useThrelte } from '@threlte/core'
23
23
  import type { XRSessionEvent } from '../types'
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
 
@@ -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
 
@@ -95,50 +94,40 @@ This should be placed within a Threlte `<Canvas />`.
95
94
  setupHands()
96
95
 
97
96
  const handleSessionStart = () => {
98
- isPresenting.set(true)
99
- onsessionstart?.({ type: 'sessionstart', target: $session } as any)
97
+ isPresenting.current = true
98
+ onsessionstart?.({ type: 'sessionstart', target: session.current } as any)
100
99
  }
101
100
 
102
101
  const handleSessionEnd = () => {
103
- onsessionend?.({ type: 'sessionend', target: $session } as any)
104
- isPresenting.set(false)
105
- session.set(undefined)
102
+ onsessionend?.({ type: 'sessionend', target: session.current } as any)
103
+ isPresenting.current = false
104
+ session.current = undefined
106
105
  }
107
106
 
108
107
  const handleVisibilityChange = (event: globalThis.XRSessionEvent) => {
109
- onvisibilitychange?.({ ...event, target: $session! })
108
+ onvisibilitychange?.({ ...event, target: session.current! })
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, target: session.current! })
115
114
  }
116
115
 
117
116
  const handleFramerateChange = (event: globalThis.XRSessionEvent) => {
118
- onvisibilitychange?.({ ...event, target: $session! })
117
+ onvisibilitychange?.({ ...event, target: session.current! })
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)
137
130
 
138
- xr.setFoveation(foveation)
139
-
140
- updateTargetFrameRate(frameRate)
141
-
142
131
  return () => {
143
132
  currentSession.removeEventListener('visibilitychange', handleVisibilityChange)
144
133
  currentSession.removeEventListener('inputsourceschange', handleInputSourcesChange)
@@ -146,8 +135,8 @@ This should be placed within a Threlte `<Canvas />`.
146
135
  }
147
136
  })
148
137
 
149
- watch(isPresenting, (presenting) => {
150
- if (presenting) {
138
+ $effect.pre(() => {
139
+ if (isPresenting.current) {
151
140
  originalRenderMode = renderMode.current
152
141
  renderMode.set('always')
153
142
  } else {
@@ -155,35 +144,46 @@ This should be placed within a Threlte `<Canvas />`.
155
144
  }
156
145
  })
157
146
 
158
- onMount(() => {
159
- $xrStore = xr
160
- xr.enabled = true
161
- xr.addEventListener('sessionstart', handleSessionStart)
162
- xr.addEventListener('sessionend', handleSessionEnd)
147
+ $effect.pre(() => {
148
+ const currentSession = session.current
149
+
150
+ xr.current = renderer.xr
151
+ renderer.xr.enabled = true
152
+ renderer.xr.addEventListener('sessionstart', handleSessionStart)
153
+ renderer.xr.addEventListener('sessionend', handleSessionEnd)
163
154
 
164
155
  return () => {
165
- $xrStore = undefined
166
- xr.enabled = false
167
- xr.removeEventListener('sessionstart', handleSessionStart)
168
- xr.removeEventListener('sessionend', handleSessionEnd)
156
+ xr.current = undefined
157
+ renderer.xr.enabled = false
158
+ renderer.xr.removeEventListener('sessionstart', handleSessionStart)
159
+ renderer.xr.removeEventListener('sessionend', handleSessionEnd)
160
+
161
+ // if unmounted while presenting (e.g. due to sveltekit navigation), end the session
162
+ currentSession?.end()
169
163
  }
170
164
  })
171
- onDestroy(() => {
172
- // if unmounted while presenting (e.g. due to sveltekit navigation), end the session
173
- if (session.current) {
174
- session.current.end()
165
+
166
+ $effect.pre(() => {
167
+ if (frameRate === undefined) return
168
+
169
+ try {
170
+ session.current?.updateTargetFrameRate(frameRate)
171
+ } catch {
172
+ // Do nothing
175
173
  }
176
174
  })
177
175
 
178
- $effect.pre(() => updateTargetFrameRate(frameRate))
179
- $effect.pre(() => xr.setFoveation(foveation))
180
176
  $effect.pre(() => {
181
- xr.setReferenceSpaceType(referenceSpace)
182
- $referenceSpaceType = referenceSpace
177
+ renderer.xr.setFoveation(foveation)
178
+ })
179
+
180
+ $effect.pre(() => {
181
+ renderer.xr.setReferenceSpaceType(referenceSpace)
182
+ referenceSpaceType.current = referenceSpace
183
183
  })
184
184
  </script>
185
185
 
186
- {#if $isPresenting}
186
+ {#if isPresenting.current}
187
187
  {@render children?.()}
188
188
  {:else}
189
189
  {@render fallback?.()}