@webspatial/core-sdk 1.7.0 → 2.0.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 (296) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +10 -0
  3. package/dist/JSBCommand.d.ts +398 -0
  4. package/dist/JSBCommand.js +668 -0
  5. package/dist/JSBCommand.js.map +1 -0
  6. package/dist/Spatial.d.ts +63 -0
  7. package/dist/Spatial.js +60 -0
  8. package/dist/Spatial.js.map +1 -0
  9. package/dist/SpatialObject.d.ts +19 -0
  10. package/dist/SpatialObject.js +44 -0
  11. package/dist/SpatialObject.js.map +1 -0
  12. package/dist/SpatialScene.d.ts +51 -0
  13. package/dist/SpatialScene.js +74 -0
  14. package/dist/SpatialScene.js.map +1 -0
  15. package/dist/SpatialSession.d.ts +143 -0
  16. package/dist/SpatialSession.js +173 -0
  17. package/dist/SpatialSession.js.map +1 -0
  18. package/dist/SpatialWebEvent.d.ts +8 -0
  19. package/dist/SpatialWebEvent.js +18 -0
  20. package/dist/SpatialWebEvent.js.map +1 -0
  21. package/dist/SpatialWebEventCreator.d.ts +11 -0
  22. package/dist/SpatialWebEventCreator.js +11 -0
  23. package/dist/SpatialWebEventCreator.js.map +1 -0
  24. package/dist/Spatialized2DElement.d.ts +42 -0
  25. package/dist/Spatialized2DElement.js +47 -0
  26. package/dist/Spatialized2DElement.js.map +1 -0
  27. package/dist/SpatializedDynamic3DElement.d.ts +7 -0
  28. package/dist/SpatializedDynamic3DElement.js +43 -0
  29. package/dist/SpatializedDynamic3DElement.js.map +1 -0
  30. package/dist/SpatializedElement.d.ts +7 -0
  31. package/dist/SpatializedElement.js +206 -0
  32. package/dist/SpatializedElement.js.map +1 -0
  33. package/dist/SpatializedElementCreator.d.ts +15 -0
  34. package/dist/SpatializedElementCreator.js +47 -0
  35. package/dist/SpatializedElementCreator.js.map +1 -0
  36. package/dist/SpatializedStatic3DElement.d.ts +195 -0
  37. package/dist/SpatializedStatic3DElement.js +327 -0
  38. package/dist/SpatializedStatic3DElement.js.map +1 -0
  39. package/dist/WebMsgCommand.d.ts +7 -0
  40. package/dist/WebMsgCommand.js +23 -0
  41. package/dist/WebMsgCommand.js.map +1 -0
  42. package/dist/index.d.ts +35 -1251
  43. package/dist/index.js +21 -3650
  44. package/dist/index.js.map +1 -1
  45. package/dist/install-polyfills.d.ts +2 -0
  46. package/dist/install-polyfills.js +13 -0
  47. package/dist/install-polyfills.js.map +1 -0
  48. package/dist/isSSREnv.d.ts +3 -0
  49. package/dist/isSSREnv.js +6 -0
  50. package/dist/isSSREnv.js.map +1 -0
  51. package/dist/motion/AnimationObject.d.ts +7 -0
  52. package/dist/motion/AnimationObject.js +140 -0
  53. package/dist/motion/AnimationObject.js.map +1 -0
  54. package/dist/motion/compute/index.d.ts +10 -0
  55. package/dist/motion/compute/index.js +19 -0
  56. package/dist/motion/compute/index.js.map +1 -0
  57. package/dist/motion/compute/motionPropertyValues.d.ts +19 -0
  58. package/dist/motion/compute/motionPropertyValues.js +50 -0
  59. package/dist/motion/compute/motionPropertyValues.js.map +1 -0
  60. package/dist/motion/compute/normalize.d.ts +31 -0
  61. package/dist/motion/compute/normalize.js +201 -0
  62. package/dist/motion/compute/normalize.js.map +1 -0
  63. package/dist/motion/compute/validate.d.ts +22 -0
  64. package/dist/motion/compute/validate.js +103 -0
  65. package/dist/motion/compute/validate.js.map +1 -0
  66. package/dist/motion/index.d.ts +8 -0
  67. package/dist/motion/index.js +6 -0
  68. package/dist/motion/index.js.map +1 -0
  69. package/dist/motion/native/serializeMotionTimeline.d.ts +12 -0
  70. package/dist/motion/native/serializeMotionTimeline.js +21 -0
  71. package/dist/motion/native/serializeMotionTimeline.js.map +1 -0
  72. package/dist/physicalMetrics-W5uZ5xKb.d.ts +48 -0
  73. package/dist/physicalMetrics.d.ts +1 -0
  74. package/dist/physicalMetrics.js +58 -0
  75. package/dist/physicalMetrics.js.map +1 -0
  76. package/dist/platform-adapter/CommandResultUtils.d.ts +12 -0
  77. package/dist/platform-adapter/CommandResultUtils.js +21 -0
  78. package/dist/platform-adapter/CommandResultUtils.js.map +1 -0
  79. package/dist/platform-adapter/createPlatformSync.d.ts +16 -0
  80. package/dist/platform-adapter/createPlatformSync.js +30 -0
  81. package/dist/platform-adapter/createPlatformSync.js.map +1 -0
  82. package/dist/platform-adapter/index.d.ts +12 -0
  83. package/dist/platform-adapter/index.js +10 -0
  84. package/dist/platform-adapter/index.js.map +1 -0
  85. package/dist/platform-adapter/interface.d.ts +7 -0
  86. package/dist/platform-adapter/interface.js +1 -0
  87. package/dist/platform-adapter/interface.js.map +1 -0
  88. package/dist/platform-adapter/pico-os/PicoOSPlatform.d.ts +25 -0
  89. package/dist/platform-adapter/pico-os/PicoOSPlatform.js +168 -0
  90. package/dist/platform-adapter/pico-os/PicoOSPlatform.js.map +1 -0
  91. package/dist/platform-adapter/puppeteer/PuppeteerPlatform.d.ts +66 -0
  92. package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js +372 -0
  93. package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js.map +1 -0
  94. package/dist/platform-adapter/spatialRequestMetadata.d.ts +6 -0
  95. package/dist/platform-adapter/spatialRequestMetadata.js +39 -0
  96. package/dist/platform-adapter/spatialRequestMetadata.js.map +1 -0
  97. package/dist/platform-adapter/spatialSceneQuery.d.ts +12 -0
  98. package/dist/platform-adapter/spatialSceneQuery.js +12 -0
  99. package/dist/platform-adapter/spatialSceneQuery.js.map +1 -0
  100. package/dist/platform-adapter/vision-os/VisionOSPlatform.d.ts +19 -0
  101. package/dist/platform-adapter/vision-os/VisionOSPlatform.js +77 -0
  102. package/dist/platform-adapter/vision-os/VisionOSPlatform.js.map +1 -0
  103. package/dist/platform-runtime.d.ts +12 -0
  104. package/dist/platform-runtime.js +14 -0
  105. package/dist/platform-runtime.js.map +1 -0
  106. package/dist/reality/Attachment.d.ts +19 -0
  107. package/dist/reality/Attachment.js +39 -0
  108. package/dist/reality/Attachment.js.map +1 -0
  109. package/dist/reality/component/ModelComponent.d.ts +14 -0
  110. package/dist/reality/component/ModelComponent.js +11 -0
  111. package/dist/reality/component/ModelComponent.js.map +1 -0
  112. package/dist/reality/component/SpatialComponent.d.ts +8 -0
  113. package/dist/reality/component/SpatialComponent.js +19 -0
  114. package/dist/reality/component/SpatialComponent.js.map +1 -0
  115. package/dist/reality/component/index.d.ts +8 -0
  116. package/dist/reality/component/index.js +3 -0
  117. package/dist/reality/component/index.js.map +1 -0
  118. package/dist/reality/entity/SpatialEntity.d.ts +7 -0
  119. package/dist/reality/entity/SpatialEntity.js +291 -0
  120. package/dist/reality/entity/SpatialEntity.js.map +1 -0
  121. package/dist/reality/entity/SpatialModelEntity.d.ts +17 -0
  122. package/dist/reality/entity/SpatialModelEntity.js +17 -0
  123. package/dist/reality/entity/SpatialModelEntity.js.map +1 -0
  124. package/dist/reality/entity/index.d.ts +8 -0
  125. package/dist/reality/entity/index.js +3 -0
  126. package/dist/reality/entity/index.js.map +1 -0
  127. package/dist/reality/geometry/SpatialBoxGeometry.d.ts +16 -0
  128. package/dist/reality/geometry/SpatialBoxGeometry.js +13 -0
  129. package/dist/reality/geometry/SpatialBoxGeometry.js.map +1 -0
  130. package/dist/reality/geometry/SpatialConeGeometry.d.ts +16 -0
  131. package/dist/reality/geometry/SpatialConeGeometry.js +13 -0
  132. package/dist/reality/geometry/SpatialConeGeometry.js.map +1 -0
  133. package/dist/reality/geometry/SpatialCylinderGeometry.d.ts +16 -0
  134. package/dist/reality/geometry/SpatialCylinderGeometry.js +13 -0
  135. package/dist/reality/geometry/SpatialCylinderGeometry.js.map +1 -0
  136. package/dist/reality/geometry/SpatialGeometry.d.ts +7 -0
  137. package/dist/reality/geometry/SpatialGeometry.js +13 -0
  138. package/dist/reality/geometry/SpatialGeometry.js.map +1 -0
  139. package/dist/reality/geometry/SpatialPlaneGeometry.d.ts +16 -0
  140. package/dist/reality/geometry/SpatialPlaneGeometry.js +13 -0
  141. package/dist/reality/geometry/SpatialPlaneGeometry.js.map +1 -0
  142. package/dist/reality/geometry/SpatialSphereGeometry.d.ts +16 -0
  143. package/dist/reality/geometry/SpatialSphereGeometry.js +13 -0
  144. package/dist/reality/geometry/SpatialSphereGeometry.js.map +1 -0
  145. package/dist/reality/geometry/index.d.ts +12 -0
  146. package/dist/reality/geometry/index.js +7 -0
  147. package/dist/reality/geometry/index.js.map +1 -0
  148. package/dist/reality/index.d.ts +18 -0
  149. package/dist/reality/index.js +7 -0
  150. package/dist/reality/index.js.map +1 -0
  151. package/dist/reality/material/SpatialMaterial.d.ts +7 -0
  152. package/dist/reality/material/SpatialMaterial.js +13 -0
  153. package/dist/reality/material/SpatialMaterial.js.map +1 -0
  154. package/dist/reality/material/SpatialUnlitMaterial.d.ts +16 -0
  155. package/dist/reality/material/SpatialUnlitMaterial.js +16 -0
  156. package/dist/reality/material/SpatialUnlitMaterial.js.map +1 -0
  157. package/dist/reality/material/index.d.ts +8 -0
  158. package/dist/reality/material/index.js +3 -0
  159. package/dist/reality/material/index.js.map +1 -0
  160. package/dist/reality/realityCreator.d.ts +22 -0
  161. package/dist/reality/realityCreator.js +89 -0
  162. package/dist/reality/realityCreator.js.map +1 -0
  163. package/dist/reality/resource/SpatialModelAsset.d.ts +15 -0
  164. package/dist/reality/resource/SpatialModelAsset.js +12 -0
  165. package/dist/reality/resource/SpatialModelAsset.js.map +1 -0
  166. package/dist/reality/resource/SpatialTextureResource.d.ts +16 -0
  167. package/dist/reality/resource/SpatialTextureResource.js +16 -0
  168. package/dist/reality/resource/SpatialTextureResource.js.map +1 -0
  169. package/dist/reality/resource/index.d.ts +9 -0
  170. package/dist/reality/resource/index.js +3 -0
  171. package/dist/reality/resource/index.js.map +1 -0
  172. package/dist/runtime/WebSpatialRuntimeError.d.ts +10 -0
  173. package/dist/runtime/WebSpatialRuntimeError.js +14 -0
  174. package/dist/runtime/WebSpatialRuntimeError.js.map +1 -0
  175. package/dist/runtime/capability-data.d.ts +12 -0
  176. package/dist/runtime/capability-data.js +134 -0
  177. package/dist/runtime/capability-data.js.map +1 -0
  178. package/dist/runtime/index.d.ts +8 -0
  179. package/dist/runtime/index.js +27 -0
  180. package/dist/runtime/index.js.map +1 -0
  181. package/dist/runtime/jsbAdapterPlatform.d.ts +9 -0
  182. package/dist/runtime/jsbAdapterPlatform.js +38 -0
  183. package/dist/runtime/jsbAdapterPlatform.js.map +1 -0
  184. package/dist/runtime/keys.d.ts +34 -0
  185. package/dist/runtime/keys.js +122 -0
  186. package/dist/runtime/keys.js.map +1 -0
  187. package/dist/runtime/semver.d.ts +8 -0
  188. package/dist/runtime/semver.js +32 -0
  189. package/dist/runtime/semver.js.map +1 -0
  190. package/dist/runtime/supports.d.ts +22 -0
  191. package/dist/runtime/supports.js +78 -0
  192. package/dist/runtime/supports.js.map +1 -0
  193. package/dist/runtime/types.d.ts +12 -0
  194. package/dist/runtime/types.js +1 -0
  195. package/dist/runtime/types.js.map +1 -0
  196. package/dist/runtime/userAgent.d.ts +20 -0
  197. package/dist/runtime/userAgent.js +41 -0
  198. package/dist/runtime/userAgent.js.map +1 -0
  199. package/dist/scene-polyfill.d.ts +18 -0
  200. package/dist/scene-polyfill.js +480 -0
  201. package/dist/scene-polyfill.js.map +1 -0
  202. package/dist/spatial-host.d.ts +13 -0
  203. package/dist/spatial-host.js +18 -0
  204. package/dist/spatial-host.js.map +1 -0
  205. package/dist/spatial-window-polyfill.d.ts +3 -0
  206. package/dist/spatial-window-polyfill.js +155 -0
  207. package/dist/spatial-window-polyfill.js.map +1 -0
  208. package/dist/spatializedElementMotion-_0XWPQCE.d.ts +1009 -0
  209. package/dist/types/animation.d.ts +7 -0
  210. package/dist/types/animation.js +1 -0
  211. package/dist/types/animation.js.map +1 -0
  212. package/dist/types/global.d.ts +38 -0
  213. package/dist/types/global.js +1 -0
  214. package/dist/types/global.js.map +1 -0
  215. package/dist/types/internal.d.ts +7 -0
  216. package/dist/types/internal.js +12 -0
  217. package/dist/types/internal.js.map +1 -0
  218. package/dist/types/motion/index.d.ts +7 -0
  219. package/dist/types/motion/index.js +3 -0
  220. package/dist/types/motion/index.js.map +1 -0
  221. package/dist/types/motion/spatializedElementMotion.d.ts +7 -0
  222. package/dist/types/motion/spatializedElementMotion.js +1 -0
  223. package/dist/types/motion/spatializedElementMotion.js.map +1 -0
  224. package/dist/types/motion/spatializedMotion.d.ts +7 -0
  225. package/dist/types/motion/spatializedMotion.js +16 -0
  226. package/dist/types/motion/spatializedMotion.js.map +1 -0
  227. package/dist/types/motion/spatializedPlayback.d.ts +11 -0
  228. package/dist/types/motion/spatializedPlayback.js +1 -0
  229. package/dist/types/motion/spatializedPlayback.js.map +1 -0
  230. package/dist/types/motion/spatializedVisual.d.ts +35 -0
  231. package/dist/types/motion/spatializedVisual.js +1 -0
  232. package/dist/types/motion/spatializedVisual.js.map +1 -0
  233. package/dist/types/types.d.ts +7 -0
  234. package/dist/types/types.js +117 -0
  235. package/dist/types/types.js.map +1 -0
  236. package/dist/utils.d.ts +44 -0
  237. package/dist/utils.js +86 -0
  238. package/dist/utils.js.map +1 -0
  239. package/package.json +20 -4
  240. package/src/JSBCommand.ts +132 -5
  241. package/src/SpatialObject.ts +10 -0
  242. package/src/Spatialized2DElement.ts +5 -0
  243. package/src/SpatializedDynamic3DElement.ts +5 -0
  244. package/src/SpatializedElement.ts +29 -0
  245. package/src/SpatializedStatic3DElement.ts +42 -3
  246. package/src/WebMsgCommand.ts +40 -0
  247. package/src/coverage-boost.test.ts +239 -216
  248. package/src/index.ts +25 -11
  249. package/src/install-polyfills.ts +38 -0
  250. package/src/jsbcommand.coverage.test.ts +23 -0
  251. package/src/motion/AnimationObject.test.ts +421 -0
  252. package/src/motion/AnimationObject.ts +208 -0
  253. package/src/motion/compute/index.ts +10 -0
  254. package/src/motion/compute/motionPropertyValues.test.ts +55 -0
  255. package/src/motion/compute/motionPropertyValues.ts +70 -0
  256. package/src/motion/compute/normalize.test.ts +99 -0
  257. package/src/motion/compute/normalize.ts +289 -0
  258. package/src/motion/compute/validate.test.ts +229 -0
  259. package/src/motion/compute/validate.ts +156 -0
  260. package/src/motion/index.ts +2 -0
  261. package/src/motion/native/serializeMotionTimeline.test.ts +61 -0
  262. package/src/motion/native/serializeMotionTimeline.ts +27 -0
  263. package/src/platform-adapter/createPlatformSync.ts +5 -3
  264. package/src/platform-adapter/pico-os/PicoOSPlatform.ts +76 -11
  265. package/src/platform-adapter/spatialRequestMetadata.test.ts +175 -0
  266. package/src/platform-adapter/spatialRequestMetadata.ts +48 -0
  267. package/src/platform-adapter/vision-os/VisionOSPlatform.ts +6 -1
  268. package/src/reality/entity/SpatialEntity.ts +127 -0
  269. package/src/runtime/capability-data.ts +104 -4
  270. package/src/runtime/index.ts +0 -11
  271. package/src/runtime/keys.ts +12 -1
  272. package/src/runtime/supports.test.ts +143 -4
  273. package/src/runtime/supports.ts +3 -1
  274. package/src/runtime/useAnimation.capability.test.ts +275 -0
  275. package/src/scene-polyfill.manifest.test.ts +306 -5
  276. package/src/scene-polyfill.request-metadata.test.ts +39 -0
  277. package/src/scene-polyfill.test.ts +33 -94
  278. package/src/scene-polyfill.ts +117 -87
  279. package/src/spatial-window-polyfill.ts +18 -2
  280. package/src/transform-roundtrip.test.ts +90 -0
  281. package/src/types/animation.ts +224 -0
  282. package/src/types/global.ts +1 -8
  283. package/src/types/motion/index.ts +10 -0
  284. package/src/types/motion/spatializedElementMotion.ts +49 -0
  285. package/src/types/motion/spatializedMotion.ts +147 -0
  286. package/src/types/motion/spatializedPlayback.ts +17 -0
  287. package/src/types/motion/spatializedVisual.ts +24 -0
  288. package/src/types/types.ts +3 -0
  289. package/src/utils.ts +54 -0
  290. package/tsup.config.ts +61 -36
  291. package/vitest.config.ts +5 -1
  292. package/dist/iife/index.d.ts +0 -1251
  293. package/dist/iife/index.global.js +0 -75
  294. package/dist/iife/index.global.js.map +0 -1
  295. package/src/platform-adapter/ssr/SSRPlatform.ts +0 -52
  296. /package/src/{ssr-polyfill.ts → isSSREnv.ts} +0 -0
@@ -1,10 +1,8 @@
1
1
  import { FocusScene } from './JSBCommand'
2
2
  import { openSpatialSceneSync } from './spatial-host'
3
- import { SpatialScene } from './SpatialScene'
4
3
  import {
5
4
  SpatialSceneCreationOptions,
6
5
  SpatialSceneType,
7
- SpatialSceneState,
8
6
  isValidSceneUnit,
9
7
  isValidSpatialSceneType,
10
8
  isValidWorldScalingType,
@@ -74,33 +72,93 @@ function deepMergePlain<
74
72
  return out
75
73
  }
76
74
 
75
+ /**
76
+ * Pick a value from a manifest-like object where both snake_case and camelCase may exist.
77
+ *
78
+ * Rule: if both keys exist on the same object, snake_case wins.
79
+ */
80
+ function preferSnake<T>(
81
+ src: Record<string, any> | undefined,
82
+ snakeKey: string,
83
+ camelKey: string,
84
+ ): T | undefined {
85
+ if (!src) return undefined
86
+ if (src[snakeKey] !== undefined) return src[snakeKey] as T
87
+ if (src[camelKey] !== undefined) return src[camelKey] as T
88
+ return undefined
89
+ }
90
+
91
+ /**
92
+ * Normalize resizability keys from manifest input into the runtime camelCase shape.
93
+ *
94
+ * Accepts both:
95
+ * - camelCase: minWidth/minHeight/maxWidth/maxHeight
96
+ * - snake_case: min_width/min_height/max_width/max_height
97
+ *
98
+ * Rule: if both exist on the same object, snake_case wins.
99
+ */
100
+ function normalizeResizability(
101
+ src: Record<string, any> | undefined,
102
+ ): SpatialSceneCreationOptions['resizability'] | undefined {
103
+ if (!src || typeof src !== 'object' || Array.isArray(src)) return undefined
104
+ const out: any = {}
105
+ const minWidth = preferSnake<any>(src, 'min_width', 'minWidth')
106
+ if (minWidth !== undefined) out.minWidth = minWidth
107
+ const minHeight = preferSnake<any>(src, 'min_height', 'minHeight')
108
+ if (minHeight !== undefined) out.minHeight = minHeight
109
+ const maxWidth = preferSnake<any>(src, 'max_width', 'maxWidth')
110
+ if (maxWidth !== undefined) out.maxWidth = maxWidth
111
+ const maxHeight = preferSnake<any>(src, 'max_height', 'maxHeight')
112
+ if (maxHeight !== undefined) out.maxHeight = maxHeight
113
+ return Object.keys(out).length > 0 ? out : undefined
114
+ }
115
+
77
116
  /**
78
117
  * Normalize XRSpatialSceneDefaults (manifest shape) into SpatialSceneCreationOptions (runtime shape).
79
- * - Only remap default_size -> defaultSize when present.
80
- * - Leaves other keys (resizability, worldScaling, etc.) unchanged.
81
- * - Units are left as-is; downstream formatting is handled by formatSceneConfig.
118
+ *
119
+ * - Remaps supported snake_case aliases into camelCase keys.
120
+ * - Keeps output camelCase-only (snake_case keys are treated as input aliases).
121
+ * - Units are not formatted here; downstream formatting is handled by formatSceneConfig.
122
+ *
123
+ * Rule: if both snake_case and camelCase are present on the same object, snake_case wins.
82
124
  */
83
125
  function normalizeXRDefaultsToSceneOptions(
84
126
  src: XRSpatialSceneDefaults | Record<string, any>,
85
127
  ): SpatialSceneCreationOptions {
86
- const out: any = { ...(src || {}) }
87
- const ds =
88
- (src as any).defaultSize !== undefined
89
- ? (src as any).defaultSize
90
- : (src as any).default_size
91
- if (ds !== undefined) {
92
- out.defaultSize = ds
93
- }
94
- if ('default_size' in out) {
95
- delete out.default_size
96
- }
128
+ const input: any = src || {}
129
+ const out: any = {}
130
+
131
+ const ds = preferSnake<any>(input, 'default_size', 'defaultSize')
132
+ if (ds !== undefined) out.defaultSize = ds
133
+
134
+ const res = input.resizability
135
+ const normalizedRes = normalizeResizability(res)
136
+ if (normalizedRes) out.resizability = normalizedRes
137
+
138
+ const worldScaling = preferSnake<any>(input, 'world_scaling', 'worldScaling')
139
+ if (worldScaling !== undefined) out.worldScaling = worldScaling
140
+
141
+ const worldAlignment = preferSnake<any>(
142
+ input,
143
+ 'world_alignment',
144
+ 'worldAlignment',
145
+ )
146
+ if (worldAlignment !== undefined) out.worldAlignment = worldAlignment
147
+
148
+ const baseplateVisibility = preferSnake<any>(
149
+ input,
150
+ 'baseplate_visibility',
151
+ 'baseplateVisibility',
152
+ )
153
+ if (baseplateVisibility !== undefined)
154
+ out.baseplateVisibility = baseplateVisibility
155
+
97
156
  return out
98
157
  }
99
158
 
100
159
  class SceneManager {
101
160
  private originalOpen: any
102
161
  private static instance: SceneManager
103
- private manifestReady: Promise<void> | null = null
104
162
  static getInstance() {
105
163
  if (!SceneManager.instance) {
106
164
  SceneManager.instance = new SceneManager()
@@ -109,7 +167,9 @@ class SceneManager {
109
167
  }
110
168
 
111
169
  init(window: WindowProxy) {
112
- this.manifestReady = this.setupManifest()
170
+ // Load manifest defaults in the background; scene creation reads the
171
+ // resolved xr_window_defaults / xr_volume_defaults once available.
172
+ void this.setupManifest()
113
173
  this.originalOpen = window.open.bind(window)
114
174
  ;(window as any).open = this.open
115
175
  }
@@ -125,10 +185,6 @@ class SceneManager {
125
185
  return this.configMap[name]
126
186
  }
127
187
 
128
- waitManifest(): Promise<void> {
129
- return this.manifestReady ?? Promise.resolve()
130
- }
131
-
132
188
  // Ensure URL is absolute; only convert when a relative path is provided
133
189
  // - Keep external and special schemes untouched (http, https, data, blob, about, file, mailto, etc.)
134
190
  // - Handle protocol-relative URLs (//example.com/path)
@@ -158,12 +214,41 @@ class SceneManager {
158
214
  const xr = manifest?.xr_spatial_scene
159
215
  if (!xr || typeof xr !== 'object') return
160
216
  const { overrides, ...topLevel } = xr as XRSpatialSceneConfig
161
- // Merge top-level defaults with per-scene overrides.
162
- const windowRaw = deepMergePlain(topLevel, overrides?.window_scene)
163
- const volumeRaw = deepMergePlain(topLevel, overrides?.volume_scene)
164
- const windowNext = normalizeXRDefaultsToSceneOptions(windowRaw)
165
217
 
166
- const volumeNext = normalizeXRDefaultsToSceneOptions(volumeRaw)
218
+ // Normalize top-level defaults and per-type overrides separately, then deep-merge.
219
+ // - Ensures overrides keep higher priority than top-level defaults.
220
+ // - Ensures snake_case vs camelCase resolution is applied within the same object only.
221
+ // - This affects only manifest-resolved defaults; initScene callback chaining remains unchanged
222
+ // because callbackReturnMap stores the raw callback return values.
223
+ const normalizedTopLevel = normalizeXRDefaultsToSceneOptions(
224
+ topLevel as any,
225
+ )
226
+ const overrideObj: any = overrides as any
227
+ const windowOverrideSrc = preferSnake<any>(
228
+ overrideObj,
229
+ 'window_scene',
230
+ 'windowScene',
231
+ )
232
+ const volumeOverrideSrc = preferSnake<any>(
233
+ overrideObj,
234
+ 'volume_scene',
235
+ 'volumeScene',
236
+ )
237
+ const normalizedWindowOverride = windowOverrideSrc
238
+ ? normalizeXRDefaultsToSceneOptions(windowOverrideSrc)
239
+ : undefined
240
+ const normalizedVolumeOverride = volumeOverrideSrc
241
+ ? normalizeXRDefaultsToSceneOptions(volumeOverrideSrc)
242
+ : undefined
243
+
244
+ const windowNext = deepMergePlain(
245
+ normalizedTopLevel,
246
+ normalizedWindowOverride,
247
+ )
248
+ const volumeNext = deepMergePlain(
249
+ normalizedTopLevel,
250
+ normalizedVolumeOverride,
251
+ )
167
252
  if (windowNext && Object.keys(windowNext).length > 0) {
168
253
  xr_window_defaults = windowNext
169
254
  }
@@ -194,9 +279,12 @@ class SceneManager {
194
279
  const host = window.location.host
195
280
  const protocol = window.location.protocol
196
281
  const finalURL = `${protocol}//${host}/${token}/?command=${command}`
197
- const rid = new URL(url).searchParams.get('rid')
282
+ const sourceParams = new URL(url).searchParams
198
283
  const final = new URL(finalURL)
199
- if (rid) final.searchParams.set('rid', rid)
284
+ for (const key of ['rid', 'wsepoch']) {
285
+ const value = sourceParams.get(key)
286
+ if (value) final.searchParams.set(key, value)
287
+ }
200
288
  return this.originalOpen(final.toString(), target, features)
201
289
  }
202
290
  }
@@ -570,65 +658,7 @@ function getSceneDefaultConfig(sceneType: SpatialSceneType) {
570
658
  : xr_volume_defaults || defaultSceneConfigVolume
571
659
  }
572
660
 
573
- async function injectScenePolyfill() {
574
- if (!window.opener) return
575
-
576
- const state = await SpatialScene.getInstance().getState()
577
-
578
- // only run this in pending state
579
- if (state !== SpatialSceneState.pending) return
580
-
581
- function onContentLoaded(callback: any) {
582
- if (
583
- document.readyState === 'interactive' ||
584
- document.readyState === 'complete'
585
- ) {
586
- callback()
587
- } else {
588
- document.addEventListener('DOMContentLoaded', callback)
589
- }
590
- }
591
-
592
- onContentLoaded(async () => {
593
- await SceneManager.getInstance().waitManifest()
594
- const sceneType = window.xrCurrentSceneType ?? 'window'
595
- const rawDefault = getSceneDefaultConfig(sceneType)
596
- // Provide a formatted 'pre' to the callback for consistent units and types.
597
- const pre = deepCloneJSON(rawDefault)
598
-
599
- let cfg = pre
600
- if (typeof window.xrCurrentSceneDefaults === 'function') {
601
- try {
602
- cfg = await window.xrCurrentSceneDefaults?.(pre)
603
- } catch (error) {
604
- console.error(error)
605
- }
606
- }
607
- // fixme: this duration is too short so that hide and show is at racing, so add a little delay to avoid
608
- await new Promise((resolve, reject) => {
609
- setTimeout(() => {
610
- resolve(null)
611
- }, 1000)
612
- })
613
-
614
- // Merge callback return with base defaults to ensure missing fields are filled.
615
- const mergedCfg = deepMergePlain(deepCloneJSON(rawDefault), cfg)
616
- const [formattedConfig, errors] = formatSceneConfig(mergedCfg, sceneType)
617
- if (errors.length > 0) {
618
- console.warn(
619
- `window.xrCurrentSceneDefaults with errors: ${errors.join(', ')}`,
620
- )
621
- }
622
- const finalCfg = {
623
- ...formattedConfig,
624
- type: sceneType,
625
- }
626
- await SpatialScene.getInstance().updateSceneCreationConfig(finalCfg)
627
- })
628
- }
629
-
630
661
  export function injectSceneHook() {
631
662
  hijackWindowOpen(window)
632
663
  hijackWindowATag(window)
633
- injectScenePolyfill()
634
664
  }
@@ -78,10 +78,20 @@ function hijackDocumentElementStyle() {
78
78
  const rawDocumentStyle = document.documentElement.style
79
79
  const styleProxy = new Proxy(rawDocumentStyle, {
80
80
  set: function (target, key, value) {
81
- const ret = Reflect.set(target, key, value)
81
+ let ret: boolean
82
82
 
83
83
  if (key === SpatialGlobalCustomVars.backgroundMaterial) {
84
- setCurrentWindowStyle(value)
84
+ target.setProperty(
85
+ SpatialGlobalCustomVars.backgroundMaterial,
86
+ String(value),
87
+ )
88
+ ret = true
89
+ } else {
90
+ ret = Reflect.set(target, key, value)
91
+ }
92
+
93
+ if (key === SpatialGlobalCustomVars.backgroundMaterial) {
94
+ setCurrentWindowStyle(String(value))
85
95
  }
86
96
 
87
97
  if (
@@ -106,6 +116,12 @@ function hijackDocumentElementStyle() {
106
116
  return ret
107
117
  },
108
118
  get: function (target, prop: string) {
119
+ if (prop === SpatialGlobalCustomVars.backgroundMaterial) {
120
+ return target.getPropertyValue(
121
+ SpatialGlobalCustomVars.backgroundMaterial,
122
+ )
123
+ }
124
+
109
125
  if (typeof target[prop as keyof CSSStyleDeclaration] === 'function') {
110
126
  return function (this: any, ...args: any[]) {
111
127
  if (prop === 'setProperty') {
@@ -0,0 +1,90 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { composeSRT, decomposeSRT } from './utils'
3
+
4
+ function approxEqual(a: number, b: number, eps = 1e-6) {
5
+ return Math.abs(a - b) < eps
6
+ }
7
+
8
+ describe('composeSRT / decomposeSRT round-trip', () => {
9
+ test('identity transform round-trips', () => {
10
+ const pos = { x: 0, y: 0, z: 0 }
11
+ const rot = { x: 0, y: 0, z: 0 }
12
+ const scl = { x: 1, y: 1, z: 1 }
13
+
14
+ const matrix = composeSRT(pos, rot, scl)
15
+ const arr = Array.from(matrix.toFloat64Array())
16
+ const result = decomposeSRT(arr)
17
+
18
+ expect(approxEqual(result.position!.x, 0)).toBe(true)
19
+ expect(approxEqual(result.position!.y, 0)).toBe(true)
20
+ expect(approxEqual(result.position!.z, 0)).toBe(true)
21
+ expect(approxEqual(result.scale!.x, 1)).toBe(true)
22
+ expect(approxEqual(result.scale!.y, 1)).toBe(true)
23
+ expect(approxEqual(result.scale!.z, 1)).toBe(true)
24
+ expect(approxEqual(result.rotation!.x, 0)).toBe(true)
25
+ expect(approxEqual(result.rotation!.y, 0)).toBe(true)
26
+ expect(approxEqual(result.rotation!.z, 0)).toBe(true)
27
+ })
28
+
29
+ test('translation-only round-trips', () => {
30
+ const pos = { x: 3, y: -5, z: 10 }
31
+ const rot = { x: 0, y: 0, z: 0 }
32
+ const scl = { x: 1, y: 1, z: 1 }
33
+
34
+ const matrix = composeSRT(pos, rot, scl)
35
+ const arr = Array.from(matrix.toFloat64Array())
36
+ const result = decomposeSRT(arr)
37
+
38
+ expect(approxEqual(result.position!.x, 3)).toBe(true)
39
+ expect(approxEqual(result.position!.y, -5)).toBe(true)
40
+ expect(approxEqual(result.position!.z, 10)).toBe(true)
41
+ })
42
+
43
+ test('scale-only round-trips', () => {
44
+ const pos = { x: 0, y: 0, z: 0 }
45
+ const rot = { x: 0, y: 0, z: 0 }
46
+ const scl = { x: 2, y: 0.5, z: 3 }
47
+
48
+ const matrix = composeSRT(pos, rot, scl)
49
+ const arr = Array.from(matrix.toFloat64Array())
50
+ const result = decomposeSRT(arr)
51
+
52
+ expect(approxEqual(result.scale!.x, 2)).toBe(true)
53
+ expect(approxEqual(result.scale!.y, 0.5)).toBe(true)
54
+ expect(approxEqual(result.scale!.z, 3)).toBe(true)
55
+ })
56
+
57
+ test('rotation-only round-trips (45 deg Y)', () => {
58
+ const pos = { x: 0, y: 0, z: 0 }
59
+ const rot = { x: 0, y: 45, z: 0 }
60
+ const scl = { x: 1, y: 1, z: 1 }
61
+
62
+ const matrix = composeSRT(pos, rot, scl)
63
+ const arr = Array.from(matrix.toFloat64Array())
64
+ const result = decomposeSRT(arr)
65
+
66
+ expect(approxEqual(result.rotation!.x, 0)).toBe(true)
67
+ expect(approxEqual(result.rotation!.y, 45)).toBe(true)
68
+ expect(approxEqual(result.rotation!.z, 0)).toBe(true)
69
+ })
70
+
71
+ test('combined SRT round-trips', () => {
72
+ const pos = { x: 1, y: 2, z: 3 }
73
+ const rot = { x: 30, y: 45, z: 60 }
74
+ const scl = { x: 2, y: 3, z: 0.5 }
75
+
76
+ const matrix = composeSRT(pos, rot, scl)
77
+ const arr = Array.from(matrix.toFloat64Array())
78
+ const result = decomposeSRT(arr)
79
+
80
+ expect(approxEqual(result.position!.x, 1)).toBe(true)
81
+ expect(approxEqual(result.position!.y, 2)).toBe(true)
82
+ expect(approxEqual(result.position!.z, 3)).toBe(true)
83
+ expect(approxEqual(result.scale!.x, 2, 1e-4)).toBe(true)
84
+ expect(approxEqual(result.scale!.y, 3, 1e-4)).toBe(true)
85
+ expect(approxEqual(result.scale!.z, 0.5, 1e-4)).toBe(true)
86
+ expect(approxEqual(result.rotation!.x, 30, 0.1)).toBe(true)
87
+ expect(approxEqual(result.rotation!.y, 45, 0.1)).toBe(true)
88
+ expect(approxEqual(result.rotation!.z, 60, 0.1)).toBe(true)
89
+ })
90
+ })
@@ -0,0 +1,224 @@
1
+ import type { Vec3 } from './types'
2
+
3
+ // ---- Timing function ----
4
+
5
+ export type TimingFunction = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'
6
+
7
+ // ---- Transform values ----
8
+
9
+ /**
10
+ * Transform values used in animation callbacks (`onComplete`, `onCancel`).
11
+ * Rotation values are Euler angles in **degrees**, matching the input convention
12
+ * of `AnimationConfig` and the entity `rotation` prop.
13
+ */
14
+ export interface TransformValues {
15
+ position?: Vec3
16
+ rotation?: Vec3
17
+ scale?: Vec3
18
+ }
19
+
20
+ // ---- Play state ----
21
+
22
+ /**
23
+ * Represents the current state of an animation session as exposed by
24
+ * `api.playState`. The internal `delaying` state maps to `'running'`
25
+ * because from the developer's perspective the session is actively progressing.
26
+ */
27
+ export type AnimationPlayState =
28
+ | 'idle'
29
+ | 'queued'
30
+ | 'running'
31
+ | 'paused'
32
+ | 'finished'
33
+
34
+ // ---- Animation config ----
35
+
36
+ /**
37
+ * Configuration for `useAnimation`.
38
+ *
39
+ * `to` is required and declares which transform fields are animated.
40
+ * `from` is optional; omitting it causes the native layer to snapshot
41
+ * the entity's current transform at playback time.
42
+ */
43
+ export interface AnimationConfig {
44
+ /**
45
+ * Target transform values (required).
46
+ * Rotation values are Euler angles in degrees.
47
+ * Single-axis rotation > 180 deg may produce unexpected results (shortest-path SLERP).
48
+ */
49
+ to: TransformValues
50
+
51
+ /** Starting transform values. Omit to animate from the entity's current state. */
52
+ from?: TransformValues
53
+
54
+ /** Duration in seconds. Default: 0.3 */
55
+ duration?: number
56
+
57
+ /**
58
+ * Easing curve. Default: 'easeInOut'
59
+ * Only these four values are valid; other strings will throw at validation time.
60
+ */
61
+ timingFunction?: TimingFunction
62
+
63
+ /** Delay before playback starts, in seconds. Default: 0 */
64
+ delay?: number
65
+
66
+ /** Start automatically when the entity mounts. Default: true */
67
+ autoStart?: boolean
68
+
69
+ /**
70
+ * Loop behavior.
71
+ * - true: reset to `from` and replay (infinite reset loop)
72
+ * - { reverse: true }: alternate direction each cycle (infinite reverse loop)
73
+ * - undefined / false: play once
74
+ */
75
+ loop?: boolean | { reverse?: boolean }
76
+
77
+ /**
78
+ * Playback speed multiplier. Default: 1
79
+ * Values > 1 speed up; values between 0 and 1 slow down.
80
+ * Must be a positive finite number (> 0).
81
+ * Applied at session creation time and remains constant for the session.
82
+ * Maps to AnimationView.speed on the native (AVP) side.
83
+ */
84
+ playbackRate?: number
85
+
86
+ /** Called when the session is established successfully. */
87
+ onStart?: () => void
88
+
89
+ /** Called when a non-looping animation finishes naturally. */
90
+ onComplete?: (finalValues: TransformValues) => void
91
+
92
+ /** Called when playback is canceled via api.cancel(). Receives the restored transform. */
93
+ onCancel?: (currentValues: TransformValues) => void
94
+
95
+ /**
96
+ * Called when an asynchronous error occurs during a bridge or native operation.
97
+ * If not provided, the SDK logs the error via console.error.
98
+ */
99
+ onError?: (error: AnimationError) => void
100
+ }
101
+
102
+ // ---- Animation error ----
103
+
104
+ export interface AnimationError {
105
+ /** The session that encountered the error. */
106
+ animationId: string
107
+ /** The command that failed. */
108
+ command: 'play' | 'pause' | 'resume' | 'cancel'
109
+ /** Optional machine-readable error code. */
110
+ code?: string
111
+ /** Human-readable failure reason. */
112
+ reason: string
113
+ }
114
+
115
+ // ---- Animation API (returned by useAnimation) ----
116
+
117
+ export interface AnimationApi {
118
+ /** Start the animation, or continue it from paused progress. */
119
+ play(): void
120
+
121
+ /** Pause the animation at the current progress. */
122
+ pause(): void
123
+
124
+ /** Cancel the animation and restore `from`, or the start snapshot when `from` is omitted. */
125
+ cancel(): void
126
+
127
+ /** Whether the session is currently queued, delaying, or running (false while paused or idle). */
128
+ readonly isAnimating: boolean
129
+
130
+ /** Whether the animation is currently paused. */
131
+ readonly isPaused: boolean
132
+
133
+ /** Current session state. */
134
+ readonly playState: AnimationPlayState
135
+
136
+ /** Whether the most recent current session completed naturally. */
137
+ readonly finished: boolean
138
+ }
139
+
140
+ // ---- Animated props (opaque, passed to entity `animation` prop) ----
141
+
142
+ /**
143
+ * Opaque object returned as the first tuple element of `useAnimation`.
144
+ * Pass it directly to the entity's `animation` prop.
145
+ * Application code should not read or modify its internal fields.
146
+ */
147
+ export interface AnimatedProps {
148
+ /** @internal Unique identifier for this animation object. */
149
+ readonly __animationObjectId: string
150
+ /** @internal Transform fields targeted by this animation. */
151
+ readonly __animatedFields: readonly ('position' | 'rotation' | 'scale')[]
152
+ /** @internal Whether the animation currently has an alive session. */
153
+ readonly __animating: boolean
154
+ }
155
+
156
+ // ---- Cross-layer command / result ----
157
+
158
+ /**
159
+ * Float4x4 is a 16-element column-major matrix stored in a Float64Array,
160
+ * matching `DOMMatrix.toFloat64Array()`.
161
+ */
162
+ export type Float4x4 = Float64Array
163
+
164
+ /**
165
+ * Unified command sent from React SDK -> Core SDK -> native.
166
+ */
167
+ export interface AnimateTransformCommand {
168
+ /**
169
+ * Unique session identifier. A new `animationId` is generated for each
170
+ * `play` command; `pause`, `resume`, and `stop` reuse the session's id.
171
+ */
172
+ animationId: string
173
+ type: 'play' | 'pause' | 'resume' | 'cancel'
174
+ /** Required when type is 'play'; ignored otherwise. */
175
+ entityId?: string
176
+ toTransform?: Float4x4
177
+ fromTransform?: Float4x4
178
+ duration?: number
179
+ timingFunction?: TimingFunction
180
+ delay?: number
181
+ loop?: boolean | { reverse?: boolean }
182
+ /** Playback speed multiplier. Default: 1. Must be positive (> 0). Maps to AnimationView.speed on AVP. */
183
+ playbackRate?: number
184
+ }
185
+
186
+ /**
187
+ * Result returned by `SpatialEntity.animateTransform({ type: 'play' })`.
188
+ */
189
+ export interface AnimateTransformResult {
190
+ animationId: string
191
+ /** Resolves when a non-looping animation completes naturally. */
192
+ finished: Promise<TransformValues>
193
+ /**
194
+ * Resolves when the animation is canceled and restored to `from`.
195
+ * After cancel, `finished` MUST remain pending (not rejected).
196
+ */
197
+ canceled: Promise<TransformValues>
198
+ /**
199
+ * Resolves when the animation fails due to a native or bridge error.
200
+ * Optional because older native runtimes may not emit the failed event.
201
+ */
202
+ failed?: Promise<AnimationError>
203
+ }
204
+
205
+ // ---- Internal animated props (cross-layer communication) ----
206
+
207
+ /**
208
+ * @internal
209
+ * Extended interface for `AnimatedProps` used internally by the entity layer
210
+ * to bind/unbind animations and query suppressed transform fields.
211
+ * Application code should never use this interface directly.
212
+ */
213
+ export interface AnimatedPropsInternal extends AnimatedProps {
214
+ /** Called by the entity layer when the entity instance becomes available. */
215
+ __bind: (
216
+ entity: import('../reality/entity/SpatialEntity').SpatialEntity,
217
+ ) => void
218
+ /** Called by the entity layer when the entity is destroyed or animation prop changes. */
219
+ __unbind: () => void
220
+ /** Returns the transform fields currently suppressed by an alive animation session. */
221
+ __getSuppressedFields: () =>
222
+ | readonly ('position' | 'rotation' | 'scale')[]
223
+ | null
224
+ }
@@ -1,15 +1,8 @@
1
1
  import type { PhysicalMetricsValueShape } from '../physicalMetrics'
2
- import type { SpatialSceneCreationOptions, SpatialSceneType } from './types'
3
-
4
2
  declare global {
5
3
  const __WEBSPATIAL_CORE_SDK_VERSION__: string
6
4
 
7
5
  interface Window {
8
- xrCurrentSceneType: SpatialSceneType
9
- xrCurrentSceneDefaults: (
10
- defaultConfig: SpatialSceneCreationOptions,
11
- ) => Promise<SpatialSceneCreationOptions>
12
-
13
6
  // Location for webspatial custom functions
14
7
  __WebSpatialData: {
15
8
  androidNativeMessage: Function
@@ -35,11 +28,11 @@ declare global {
35
28
  WebSpatailNativeVersion: string
36
29
 
37
30
  __webspatialsdk__?: {
38
- XR_ENV?: string
39
31
  'natvie-version'?: string
40
32
  'react-sdk-version'?: string
41
33
  'core-sdk-version'?: string
42
34
  physicalMetrics?: PhysicalMetricsValueShape
35
+ pageEpoch?: string | number
43
36
  }
44
37
 
45
38
  /** Present when `supports('xrInnerDepth')` is true; otherwise `undefined`. */
@@ -0,0 +1,10 @@
1
+ export * from './spatializedVisual'
2
+ export * from './spatializedPlayback'
3
+ export type {
4
+ SpatializedMotionKind,
5
+ SpatializedMotionSegmentConfig,
6
+ SpatializedMotionTimelineConfig,
7
+ SpatializedMotionConfig,
8
+ SpatializedMotionPlayState,
9
+ SpatializedPlaybackApi,
10
+ } from './spatializedMotion'