@webspatial/react-sdk 1.6.1 → 1.7.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.
@@ -2,498 +2,17 @@
2
2
  (function(){
3
3
  if(typeof window === 'undefined') return;
4
4
  if(!window.__webspatialsdk__) window.__webspatialsdk__ = {}
5
- window.__webspatialsdk__['react-sdk-version'] = "1.6.1"
5
+ window.__webspatialsdk__['react-sdk-version'] = "1.7.0"
6
6
  window.__webspatialsdk__['XR_ENV'] = "avp"
7
7
  })()
8
8
 
9
9
 
10
- // src/spatialized-container/hooks/useDomProxy.ts
11
- import { useCallback, useEffect, useRef } from "react";
12
-
13
- // src/spatialized-container/types.ts
14
- var SpatialCustomStyleVars = {
15
- back: "--xr-back",
16
- depth: "--xr-depth",
17
- backgroundMaterial: "--xr-background-material",
18
- xrZIndex: "--xr-z-index"
19
- };
20
-
21
- // src/spatialized-container/utils.ts
22
- function getInheritedStyleProps(computedStyle) {
23
- var propNames = [
24
- "azimuth",
25
- "borderCollapse",
26
- "borderSpacing",
27
- "captionSide",
28
- "color",
29
- "cursor",
30
- "direction",
31
- // 'elevation',
32
- "emptyCells",
33
- "fontFamily",
34
- "fontSize",
35
- "fontStyle",
36
- "fontVariant",
37
- "fontWeight",
38
- "font",
39
- "letterSpacing",
40
- "lineHeight",
41
- "listStyleImage",
42
- "listStylePosition",
43
- "listStyleType",
44
- "listStyle",
45
- "orphans",
46
- // 'pitchRange',
47
- // 'pitch',
48
- "quotes",
49
- // 'richness',
50
- // 'speakHeader',
51
- // 'speakNumeral',
52
- // 'speakPunctuation',
53
- // 'speak',
54
- // 'speechRate',
55
- // 'stress',
56
- "textAlign",
57
- "textIndent",
58
- "textTransform",
59
- "visibility",
60
- // 'voiceFamily',
61
- // 'volume',
62
- "whiteSpace",
63
- "widows",
64
- "wordSpacing",
65
- // background also need to be synced
66
- "background",
67
- // position also need to be synced
68
- "position",
69
- "width",
70
- "height",
71
- "display",
72
- // content-visibility also need to be synced
73
- "contentVisibility"
74
- ];
75
- var props = {};
76
- for (var cssName of propNames) {
77
- if (computedStyle[cssName]) {
78
- props[cssName] = computedStyle[cssName];
79
- }
80
- }
81
- return props;
82
- }
83
- function parseTransformOrigin(computedStyle) {
84
- const transformOriginProperty = computedStyle.getPropertyValue("transform-origin");
85
- const [x, y] = transformOriginProperty.split(" ").map(parseFloat);
86
- const width = parseFloat(computedStyle.getPropertyValue("width"));
87
- const height = parseFloat(computedStyle.getPropertyValue("height"));
88
- return {
89
- x: width > 0 ? x / width : 0.5,
90
- y: height > 0 ? y / height : 0.5,
91
- z: 0.5
92
- };
93
- }
94
- function parseBorderRadius(borderProperty, width) {
95
- if (borderProperty === "") {
96
- return 0;
97
- }
98
- if (borderProperty.endsWith("%")) {
99
- return width * parseFloat(borderProperty) / 100;
100
- }
101
- return parseFloat(borderProperty);
102
- }
103
- function parseCornerRadius(computedStyle) {
104
- const width = parseFloat(computedStyle.getPropertyValue("width"));
105
- const topLeftPropertyValue = computedStyle.getPropertyValue(
106
- "border-top-left-radius"
107
- );
108
- const topRightPropertyValue = computedStyle.getPropertyValue(
109
- "border-top-right-radius"
110
- );
111
- const bottomLeftPropertyValue = computedStyle.getPropertyValue(
112
- "border-bottom-left-radius"
113
- );
114
- const bottomRightPropertyValue = computedStyle.getPropertyValue(
115
- "border-bottom-right-radius"
116
- );
117
- const cornerRadius = {
118
- topLeading: parseBorderRadius(topLeftPropertyValue, width),
119
- bottomLeading: parseBorderRadius(bottomLeftPropertyValue, width),
120
- topTrailing: parseBorderRadius(topRightPropertyValue, width),
121
- bottomTrailing: parseBorderRadius(bottomRightPropertyValue, width)
122
- };
123
- return cornerRadius;
124
- }
125
- function extractAndRemoveCustomProperties(cssText, properties) {
126
- if (!cssText) {
127
- return { extractedValues: {}, filteredCssText: "" };
128
- }
129
- const extractedValues = {};
130
- const rules = cssText.split(";");
131
- const filteredRules = rules.filter((rule) => {
132
- const [key, value] = rule.split(":").map((part) => part.trim());
133
- if (properties.includes(key)) {
134
- extractedValues[key] = value;
135
- return false;
136
- }
137
- return true;
138
- });
139
- const filteredCssText = filteredRules.join(";").trim();
140
- return { extractedValues, filteredCssText };
141
- }
142
- function joinToCSSText(cssKV) {
143
- const rules = Object.entries(cssKV).map(([key, value]) => `${key}: ${value}`);
144
- return rules.join(";");
145
- }
146
-
147
- // src/spatialized-container/hooks/useDomProxy.ts
148
- var SpatialContainerRefProxy = class {
149
- transformVisibilityTaskContainerDom = null;
150
- /** Raw Standard host element (styled root). Used to mirror class onto the transform probe. */
151
- standardRawDom = null;
152
- standardClassObserver = null;
153
- /**
154
- * When set, Standard's DOM className is forwarded here so TransformVisibilityTaskContainer
155
- * can render it from React state (avoids React clobbering imperative class updates).
156
- */
157
- mirrorClassNotify = null;
158
- /** Last class string applied to the probe + used to skip redundant syncs. */
159
- lastMirroredClassName = null;
160
- /** Coalesce multiple class sync triggers in the same turn (Observer + classList, etc.). */
161
- classSyncMicrotaskQueued = false;
162
- ref;
163
- domProxy;
164
- styleProxy;
165
- // extre ref props, used to add extra props to ref
166
- extraRefProps;
167
- constructor(ref, extraRefProps) {
168
- this.ref = ref;
169
- this.extraRefProps = extraRefProps;
170
- }
171
- setMirrorClassNotify(fn) {
172
- this.mirrorClassNotify = fn;
173
- if (fn && this.standardRawDom) {
174
- this.flushSyncTransformClassFromStandard(true);
175
- }
176
- }
177
- disconnectStandardClassObserver() {
178
- this.standardClassObserver?.disconnect();
179
- this.standardClassObserver = null;
180
- }
181
- attachStandardClassObserver() {
182
- this.disconnectStandardClassObserver();
183
- if (!this.standardRawDom) {
184
- return;
185
- }
186
- this.standardClassObserver = new MutationObserver(() => {
187
- this.scheduleSyncTransformClassFromStandard();
188
- });
189
- this.standardClassObserver.observe(this.standardRawDom, {
190
- attributes: true,
191
- attributeFilter: ["class"]
192
- });
193
- }
194
- /**
195
- * Merge multiple sync requests (e.g. classList hook + MutationObserver) into one microtask.
196
- */
197
- scheduleSyncTransformClassFromStandard() {
198
- if (this.classSyncMicrotaskQueued) {
199
- return;
200
- }
201
- this.classSyncMicrotaskQueued = true;
202
- queueMicrotask(() => {
203
- this.classSyncMicrotaskQueued = false;
204
- this.flushSyncTransformClassFromStandard(false);
205
- });
206
- }
207
- /**
208
- * Source of truth: Standard host DOM (incl. styled-components runtime class changes).
209
- * @param force when true, skip same-string short-circuit (e.g. mirror notify just registered).
210
- */
211
- flushSyncTransformClassFromStandard(force) {
212
- if (!this.standardRawDom) {
213
- return;
214
- }
215
- const name = this.standardRawDom.className;
216
- const probe = this.transformVisibilityTaskContainerDom;
217
- if (!force && probe && probe.className === name && this.lastMirroredClassName === name) {
218
- return;
219
- }
220
- this.lastMirroredClassName = name;
221
- if (probe) {
222
- probe.className = name;
223
- }
224
- this.mirrorClassNotify?.(name);
225
- }
226
- updateStandardSpatializedContainerDom(dom) {
227
- const self = this;
228
- if (!dom) {
229
- this.disconnectStandardClassObserver();
230
- this.standardRawDom = null;
231
- this.lastMirroredClassName = null;
232
- this.domProxy = void 0;
233
- this.styleProxy = void 0;
234
- this.updateDomProxyToRef();
235
- return;
236
- }
237
- this.standardRawDom = dom;
238
- let cacheExtraRefProps;
239
- const domProxy = new Proxy(
240
- dom,
241
- {
242
- get(target, prop) {
243
- if (prop === "__raw") {
244
- return target;
245
- }
246
- if (prop === "xrClientDepth") {
247
- return target.style.getPropertyValue(SpatialCustomStyleVars.depth);
248
- }
249
- if (prop === "xrOffsetBack") {
250
- return target.style.getPropertyValue(SpatialCustomStyleVars.back);
251
- }
252
- if (prop === "style") {
253
- if (!self.styleProxy) {
254
- self.styleProxy = new Proxy(target.style, {
255
- get(target2, prop2) {
256
- if (prop2 === "visibility" || prop2 === "transform") {
257
- return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
258
- prop2
259
- );
260
- }
261
- const value2 = Reflect.get(target2, prop2);
262
- if (typeof value2 === "function") {
263
- if (prop2 === "setProperty" || prop2 === "removeProperty" || prop2 === "getPropertyValue") {
264
- return function(...args) {
265
- const validProperties = ["visibility", "transform"];
266
- const [property] = args;
267
- if (validProperties.includes(property)) {
268
- if (prop2 === "setProperty") {
269
- const [, kValue] = args;
270
- self.transformVisibilityTaskContainerDom?.style.setProperty(
271
- property,
272
- kValue
273
- );
274
- } else if (prop2 === "removeProperty") {
275
- self.transformVisibilityTaskContainerDom?.style.removeProperty(
276
- property
277
- );
278
- } else if (prop2 === "getPropertyValue") {
279
- return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
280
- property
281
- );
282
- }
283
- } else {
284
- return value2.apply(this, args);
285
- }
286
- }.bind(target2);
287
- } else {
288
- return value2.bind(target2);
289
- }
290
- } else {
291
- return value2;
292
- }
293
- },
294
- set(target2, prop2, value2) {
295
- if (prop2 === "visibility") {
296
- self.transformVisibilityTaskContainerDom?.style.setProperty(
297
- "visibility",
298
- value2
299
- );
300
- return true;
301
- }
302
- if (prop2 === "transform") {
303
- self.transformVisibilityTaskContainerDom?.style.setProperty(
304
- "transform",
305
- value2
306
- );
307
- return true;
308
- }
309
- if (prop2 === SpatialCustomStyleVars.backgroundMaterial) {
310
- target2.setProperty(
311
- SpatialCustomStyleVars.backgroundMaterial,
312
- value2
313
- );
314
- } else if (prop2 === SpatialCustomStyleVars.back) {
315
- target2.setProperty(
316
- SpatialCustomStyleVars.back,
317
- value2
318
- );
319
- } else if (prop2 === SpatialCustomStyleVars.xrZIndex) {
320
- target2.setProperty(
321
- SpatialCustomStyleVars.xrZIndex,
322
- value2
323
- );
324
- } else if (prop2 === SpatialCustomStyleVars.depth) {
325
- target2.setProperty(
326
- SpatialCustomStyleVars.depth,
327
- value2
328
- );
329
- } else if (prop2 === "cssText") {
330
- const toFilteredCSSProperties = ["transform", "visibility"];
331
- const { extractedValues, filteredCssText } = extractAndRemoveCustomProperties(
332
- value2,
333
- toFilteredCSSProperties
334
- );
335
- toFilteredCSSProperties.forEach((key) => {
336
- if (extractedValues[key]) {
337
- self.transformVisibilityTaskContainerDom?.style.setProperty(
338
- key,
339
- extractedValues[key]
340
- );
341
- } else {
342
- target2.removeProperty(key);
343
- }
344
- });
345
- const appendedCSSText = joinToCSSText({
346
- transform: "none",
347
- visibility: "hidden"
348
- });
349
- return Reflect.set(
350
- target2,
351
- prop2,
352
- [appendedCSSText, filteredCssText].join(";")
353
- );
354
- }
355
- return Reflect.set(target2, prop2, value2);
356
- }
357
- });
358
- }
359
- return self.styleProxy;
360
- }
361
- if (typeof prop === "string" && self.extraRefProps) {
362
- if (!cacheExtraRefProps) {
363
- cacheExtraRefProps = self.extraRefProps(domProxy);
364
- }
365
- const extraProps = cacheExtraRefProps;
366
- if (extraProps.hasOwnProperty(prop)) {
367
- return extraProps[prop];
368
- }
369
- }
370
- const value = Reflect.get(target, prop);
371
- if (typeof value === "function") {
372
- if ("removeAttribute" === prop) {
373
- return function(...args) {
374
- const [property] = args;
375
- if (property === "style") {
376
- dom.style.cssText = "visibility: hidden; transition: none; transform: none;";
377
- if (self.transformVisibilityTaskContainerDom) {
378
- self.transformVisibilityTaskContainerDom.style.visibility = "";
379
- self.transformVisibilityTaskContainerDom.style.transform = "";
380
- }
381
- return true;
382
- }
383
- if (property === "class") {
384
- domProxy.className = "xr-spatial-default";
385
- return true;
386
- }
387
- };
388
- }
389
- return value.bind(target);
390
- }
391
- return value;
392
- },
393
- set(target, prop, value) {
394
- if (prop === "className") {
395
- if (value && String(value).indexOf("xr-spatial-default") === -1) {
396
- value = value + " xr-spatial-default";
397
- }
398
- }
399
- if (typeof prop === "string" && self.extraRefProps) {
400
- if (!cacheExtraRefProps) {
401
- cacheExtraRefProps = self.extraRefProps(domProxy);
402
- }
403
- cacheExtraRefProps[prop] = value;
404
- }
405
- const ok = Reflect.set(target, prop, value);
406
- if (ok && prop === "className") {
407
- self.scheduleSyncTransformClassFromStandard();
408
- }
409
- return ok;
410
- }
411
- }
412
- );
413
- this.domProxy = domProxy;
414
- this.styleProxy = void 0;
415
- this.updateDomProxyToRef();
416
- Object.assign(dom, {
417
- __targetProxy: domProxy
418
- });
419
- this.attachStandardClassObserver();
420
- this.scheduleSyncTransformClassFromStandard();
421
- }
422
- updateTransformVisibilityTaskContainerDom(dom) {
423
- this.transformVisibilityTaskContainerDom = dom;
424
- if (!dom) {
425
- this.lastMirroredClassName = null;
426
- }
427
- this.scheduleSyncTransformClassFromStandard();
428
- this.updateDomProxyToRef();
429
- }
430
- updateDomProxyToRef() {
431
- const ref = this.ref;
432
- if (!ref) {
433
- return;
434
- }
435
- if (this.domProxy && this.transformVisibilityTaskContainerDom) {
436
- if (typeof ref === "function") {
437
- ref(this.domProxy);
438
- } else {
439
- ref.current = this.domProxy;
440
- }
441
- } else {
442
- if (typeof ref === "function") {
443
- ref(null);
444
- } else {
445
- ref.current = null;
446
- }
447
- }
448
- }
449
- updateRef(ref) {
450
- this.ref = ref;
451
- }
452
- };
453
- function hijackGetComputedStyle() {
454
- const rawFn = window.getComputedStyle.bind(window);
455
- window.getComputedStyle = (element, pseudoElt) => {
456
- const dom = element.__raw;
457
- if (dom) {
458
- return rawFn(dom, pseudoElt);
459
- }
460
- return rawFn(element, pseudoElt);
461
- };
462
- }
463
- function useDomProxy(ref, extraRefProps) {
464
- const spatialContainerRefProxy = useRef(
465
- new SpatialContainerRefProxy(ref, extraRefProps)
466
- );
467
- useEffect(() => {
468
- spatialContainerRefProxy.current.updateRef(ref);
469
- }, [ref]);
470
- const transformVisibilityTaskContainerCallback = useCallback(
471
- (el) => {
472
- spatialContainerRefProxy.current.updateTransformVisibilityTaskContainerDom(
473
- el
474
- );
475
- },
476
- []
477
- );
478
- const standardSpatializedContainerCallback = useCallback(
479
- (el) => {
480
- spatialContainerRefProxy.current.updateStandardSpatializedContainerDom(el);
481
- },
482
- []
483
- );
484
- return {
485
- transformVisibilityTaskContainerCallback,
486
- standardSpatializedContainerCallback,
487
- spatialContainerRefProxy
488
- };
489
- }
490
-
491
10
  // src/spatialized-container/hooks/use2DFrameDetector.ts
492
11
  import {
493
12
  useContext,
494
13
  useLayoutEffect,
495
- useEffect as useEffect2,
496
- useCallback as useCallback2
14
+ useEffect,
15
+ useCallback
497
16
  } from "react";
498
17
 
499
18
  // src/spatialized-container/context/SpatializedContainerContext.ts
@@ -620,11 +139,11 @@ function use2DFrameDetector(ref) {
620
139
  const spatializedContainerObject = useContext(
621
140
  SpatializedContainerContext
622
141
  );
623
- const notify2DFrameChange = useCallback2(() => {
142
+ const notify2DFrameChange = useCallback(() => {
624
143
  ref.current && spatializedContainerObject.notify2DFramePlaceHolderChange(ref.current);
625
144
  }, [ref.current, spatializedContainerObject]);
626
145
  useLayoutEffect(notify2DFrameChange, [notify2DFrameChange]);
627
- useEffect2(() => {
146
+ useEffect(() => {
628
147
  if (!ref.current || !spatializedContainerObject) {
629
148
  console.warn(
630
149
  "Ref is not attached to the DOM or spatializedContainerObject is not available"
@@ -636,7 +155,7 @@ function use2DFrameDetector(ref) {
636
155
  window.removeEventListener("resize", notify2DFrameChange);
637
156
  };
638
157
  }, []);
639
- useEffect2(() => {
158
+ useEffect(() => {
640
159
  if (!ref.current) {
641
160
  console.warn("Ref is not attached to the DOM");
642
161
  return;
@@ -647,7 +166,7 @@ function use2DFrameDetector(ref) {
647
166
  ro.disconnect();
648
167
  };
649
168
  }, []);
650
- useEffect2(() => {
169
+ useEffect(() => {
651
170
  if (!ref.current) {
652
171
  console.warn("Ref is not attached to the DOM");
653
172
  return;
@@ -666,17 +185,17 @@ function use2DFrameDetector(ref) {
666
185
  // src/spatialized-container/StandardSpatializedContainer.tsx
667
186
  import {
668
187
  forwardRef,
669
- useCallback as useCallback3,
188
+ useCallback as useCallback2,
670
189
  useContext as useContext2,
671
- useEffect as useEffect3,
672
- useRef as useRef2,
190
+ useEffect as useEffect2,
191
+ useRef,
673
192
  useState
674
193
  } from "react";
675
194
  import { jsx } from "react/jsx-runtime";
676
195
  function useSpatialTransformVisibilityWatcher(spatialId) {
677
196
  const [transformExist, setTransformExist] = useState(false);
678
197
  const spatializedContainerObject = useContext2(SpatializedContainerContext);
679
- useEffect3(() => {
198
+ useEffect2(() => {
680
199
  const fn = (spatialTransform) => {
681
200
  setTransformExist(spatialTransform.transform !== "none");
682
201
  };
@@ -691,8 +210,8 @@ function useSpatialTransformVisibilityWatcher(spatialId) {
691
210
  return transformExist;
692
211
  }
693
212
  function useInternalRef(ref) {
694
- const refInternal = useRef2(null);
695
- const refInternalCallback = useCallback3(
213
+ const refInternal = useRef(null);
214
+ const refInternalCallback = useCallback2(
696
215
  (node) => {
697
216
  refInternal.current = node;
698
217
  if (typeof ref === "function") {
@@ -718,43 +237,70 @@ function StandardSpatializedContainerBase(props, ref) {
718
237
  use2DFrameDetector(refInternal);
719
238
  }
720
239
  const transformExist = useSpatialTransformVisibilityWatcher(props[SpatialID]);
721
- const extraStyle = {
722
- visibility: "hidden",
723
- transition: "none",
724
- transform: transformExist ? "translateZ(0)" : "none"
725
- };
726
- const style = { ...inStyle, ...extraStyle };
727
240
  const classNames = className ? `${className} xr-spatial-default` : "xr-spatial-default";
728
241
  return /* @__PURE__ */ jsx(
729
242
  Component,
730
243
  {
731
244
  ref: refInternalCallback,
732
- style,
245
+ style: inStyle,
733
246
  className: classNames,
734
- ...restProps
247
+ ...restProps,
248
+ "data-xr-host": "",
249
+ "data-xr-transform-active": transformExist ? "" : void 0
735
250
  }
736
251
  );
737
252
  }
738
253
  var StandardSpatializedContainer = forwardRef(
739
254
  StandardSpatializedContainerBase
740
255
  );
741
- function injectSpatialDefaultStyle() {
742
- const styleElement = document.createElement("style");
256
+ var SPATIAL_DEFAULT_STYLE_CSS = `
257
+ :where(.xr-spatial-default) {
258
+ --xr-back: 0;
259
+ --xr-depth: 0;
260
+ --xr-z-index: 0;
261
+ --xr-background-material: none;
262
+ }
263
+ .xr-spatial-default[data-xr-host] {
264
+ visibility: hidden !important;
265
+ transition: none !important;
266
+ transform: none !important;
267
+ }
268
+ .xr-spatial-default[data-xr-host][data-xr-transform-active] {
269
+ transform: translateZ(0) !important;
270
+ }
271
+ `;
272
+ var SPATIAL_DEFAULT_STYLE_MARKER = "data-xr-spatial-default-style";
273
+ function ensureSpatialDefaultStyleInRoot(root) {
274
+ const queryRoot = root === document ? document.head : root;
275
+ if (queryRoot.querySelector(`style[${SPATIAL_DEFAULT_STYLE_MARKER}]`)) {
276
+ return;
277
+ }
278
+ const ownerDoc = root === document ? document : root.ownerDocument || document;
279
+ const styleElement = ownerDoc.createElement("style");
743
280
  styleElement.type = "text/css";
744
- styleElement.innerHTML = " :where(.xr-spatial-default) { --xr-back: 0; --xr-depth: 0; --xr-z-index: 0; --xr-background-material: none; } ";
745
- document.head.appendChild(styleElement);
281
+ styleElement.setAttribute(SPATIAL_DEFAULT_STYLE_MARKER, "");
282
+ styleElement.innerHTML = SPATIAL_DEFAULT_STYLE_CSS;
283
+ if (root === document) {
284
+ document.head.appendChild(styleElement);
285
+ } else {
286
+ ;
287
+ root.appendChild(styleElement);
288
+ }
289
+ }
290
+ function injectSpatialDefaultStyle() {
291
+ ensureSpatialDefaultStyleInRoot(document);
746
292
  }
747
293
 
748
294
  // src/spatialized-container/TransformVisibilityTaskContainer.tsx
749
295
  import {
750
296
  forwardRef as forwardRef2,
751
- useCallback as useCallback5,
752
- useRef as useRef3
297
+ useCallback as useCallback4,
298
+ useRef as useRef2
753
299
  } from "react";
754
300
  import { createPortal } from "react-dom";
755
301
 
756
302
  // src/spatialized-container/hooks/useSpatialTransformVisibility.ts
757
- import { useCallback as useCallback4, useContext as useContext3, useEffect as useEffect4 } from "react";
303
+ import { useCallback as useCallback3, useContext as useContext3, useEffect as useEffect3 } from "react";
758
304
 
759
305
  // src/notifyUpdateStandInstanceLayout.ts
760
306
  function notifyUpdateStandInstanceLayout() {
@@ -784,7 +330,7 @@ function parseTransformAndVisibilityProperties(node) {
784
330
  }
785
331
  function useSpatialTransformVisibility(spatialId, ref) {
786
332
  const spatializedContainerObject = useContext3(SpatializedContainerContext);
787
- const checkSpatialStyleUpdate = useCallback4(() => {
333
+ const checkSpatialStyleUpdate = useCallback3(() => {
788
334
  if (!ref.current) {
789
335
  return;
790
336
  }
@@ -796,10 +342,10 @@ function useSpatialTransformVisibility(spatialId, ref) {
796
342
  spatialTransformVisibility
797
343
  );
798
344
  }, []);
799
- useEffect4(() => {
345
+ useEffect3(() => {
800
346
  checkSpatialStyleUpdate();
801
347
  }, [checkSpatialStyleUpdate]);
802
- useEffect4(() => {
348
+ useEffect3(() => {
803
349
  const observer = new MutationObserver((mutationsList) => {
804
350
  checkSpatialStyleUpdate();
805
351
  });
@@ -815,7 +361,7 @@ function useSpatialTransformVisibility(spatialId, ref) {
815
361
  observer.disconnect();
816
362
  };
817
363
  }, []);
818
- useEffect4(() => {
364
+ useEffect3(() => {
819
365
  const headObserver = new MutationObserver((mutations) => {
820
366
  checkSpatialStyleUpdate();
821
367
  });
@@ -824,7 +370,7 @@ function useSpatialTransformVisibility(spatialId, ref) {
824
370
  headObserver.disconnect();
825
371
  };
826
372
  }, []);
827
- useEffect4(() => {
373
+ useEffect3(() => {
828
374
  const onDomUpdated = (event) => {
829
375
  checkSpatialStyleUpdate();
830
376
  };
@@ -858,8 +404,8 @@ function createOrGetCSSParserDivContainer() {
858
404
  return cssParserDivContainer;
859
405
  }
860
406
  function useInternalRef2(ref) {
861
- const refInternal = useRef3(null);
862
- const refInternalCallback = useCallback5(
407
+ const refInternal = useRef2(null);
408
+ const refInternalCallback = useCallback4(
863
409
  (node) => {
864
410
  refInternal.current = node;
865
411
  if (typeof ref === "function") {
@@ -909,7 +455,9 @@ import {
909
455
  useCallback as useCallback6,
910
456
  useContext as useContext7,
911
457
  useEffect as useEffect10,
458
+ useLayoutEffect as useLayoutEffect2,
912
459
  useMemo as useMemo2,
460
+ useRef as useRef5,
913
461
  useState as useState6
914
462
  } from "react";
915
463
 
@@ -928,19 +476,153 @@ function getSession() {
928
476
  if (_currentSession) {
929
477
  return _currentSession;
930
478
  }
931
- _currentSession = spatial.requestSession();
932
- return _currentSession;
479
+ _currentSession = spatial.requestSession();
480
+ return _currentSession;
481
+ }
482
+
483
+ // src/spatialized-container/context/SpatialLayerContext.ts
484
+ import { createContext as createContext2 } from "react";
485
+ var SpatialLayerContext = createContext2(0);
486
+
487
+ // src/spatialized-container/PortalSpatializedContainer.tsx
488
+ import { useMemo, useContext as useContext4, useEffect as useEffect6 } from "react";
489
+
490
+ // src/spatialized-container/context/PortalInstanceContext.ts
491
+ import { createContext as createContext3 } from "react";
492
+
493
+ // src/spatialized-container/utils.ts
494
+ function getInheritedStyleProps(computedStyle) {
495
+ var propNames = [
496
+ "azimuth",
497
+ "borderCollapse",
498
+ "borderSpacing",
499
+ "captionSide",
500
+ "color",
501
+ "cursor",
502
+ "direction",
503
+ // 'elevation',
504
+ "emptyCells",
505
+ "fontFamily",
506
+ "fontSize",
507
+ "fontStyle",
508
+ "fontVariant",
509
+ "fontWeight",
510
+ "font",
511
+ "letterSpacing",
512
+ "lineHeight",
513
+ "listStyleImage",
514
+ "listStylePosition",
515
+ "listStyleType",
516
+ "listStyle",
517
+ "orphans",
518
+ // 'pitchRange',
519
+ // 'pitch',
520
+ "quotes",
521
+ // 'richness',
522
+ // 'speakHeader',
523
+ // 'speakNumeral',
524
+ // 'speakPunctuation',
525
+ // 'speak',
526
+ // 'speechRate',
527
+ // 'stress',
528
+ "textAlign",
529
+ "textIndent",
530
+ "textTransform",
531
+ "visibility",
532
+ // 'voiceFamily',
533
+ // 'volume',
534
+ "whiteSpace",
535
+ "widows",
536
+ "wordSpacing",
537
+ // background also need to be synced
538
+ "background",
539
+ // position also need to be synced
540
+ "position",
541
+ "width",
542
+ "height",
543
+ "display",
544
+ // content-visibility also need to be synced
545
+ "contentVisibility"
546
+ ];
547
+ var props = {};
548
+ for (var cssName of propNames) {
549
+ if (computedStyle[cssName]) {
550
+ props[cssName] = computedStyle[cssName];
551
+ }
552
+ }
553
+ return props;
554
+ }
555
+ function parseTransformOrigin(computedStyle) {
556
+ const transformOriginProperty = computedStyle.getPropertyValue("transform-origin");
557
+ const [x, y] = transformOriginProperty.split(" ").map(parseFloat);
558
+ const width = parseFloat(computedStyle.getPropertyValue("width"));
559
+ const height = parseFloat(computedStyle.getPropertyValue("height"));
560
+ return {
561
+ x: width > 0 ? x / width : 0.5,
562
+ y: height > 0 ? y / height : 0.5,
563
+ z: 0.5
564
+ };
565
+ }
566
+ function parseBorderRadius(borderProperty, width) {
567
+ if (borderProperty === "") {
568
+ return 0;
569
+ }
570
+ if (borderProperty.endsWith("%")) {
571
+ return width * parseFloat(borderProperty) / 100;
572
+ }
573
+ return parseFloat(borderProperty);
574
+ }
575
+ function parseCornerRadius(computedStyle) {
576
+ const width = parseFloat(computedStyle.getPropertyValue("width"));
577
+ const topLeftPropertyValue = computedStyle.getPropertyValue(
578
+ "border-top-left-radius"
579
+ );
580
+ const topRightPropertyValue = computedStyle.getPropertyValue(
581
+ "border-top-right-radius"
582
+ );
583
+ const bottomLeftPropertyValue = computedStyle.getPropertyValue(
584
+ "border-bottom-left-radius"
585
+ );
586
+ const bottomRightPropertyValue = computedStyle.getPropertyValue(
587
+ "border-bottom-right-radius"
588
+ );
589
+ const cornerRadius = {
590
+ topLeading: parseBorderRadius(topLeftPropertyValue, width),
591
+ bottomLeading: parseBorderRadius(bottomLeftPropertyValue, width),
592
+ topTrailing: parseBorderRadius(topRightPropertyValue, width),
593
+ bottomTrailing: parseBorderRadius(bottomRightPropertyValue, width)
594
+ };
595
+ return cornerRadius;
596
+ }
597
+ function extractAndRemoveCustomProperties(cssText, properties) {
598
+ if (!cssText) {
599
+ return { extractedValues: {}, filteredCssText: "" };
600
+ }
601
+ const extractedValues = {};
602
+ const rules = cssText.split(";");
603
+ const filteredRules = rules.filter((rule) => {
604
+ const [key, value] = rule.split(":").map((part) => part.trim());
605
+ if (properties.includes(key)) {
606
+ extractedValues[key] = value;
607
+ return false;
608
+ }
609
+ return true;
610
+ });
611
+ const filteredCssText = filteredRules.join(";").trim();
612
+ return { extractedValues, filteredCssText };
613
+ }
614
+ function joinToCSSText(cssKV) {
615
+ const rules = Object.entries(cssKV).map(([key, value]) => `${key}: ${value}`);
616
+ return rules.join(";");
933
617
  }
934
618
 
935
- // src/spatialized-container/context/SpatialLayerContext.ts
936
- import { createContext as createContext2 } from "react";
937
- var SpatialLayerContext = createContext2(0);
938
-
939
- // src/spatialized-container/PortalSpatializedContainer.tsx
940
- import { useMemo, useContext as useContext4, useEffect as useEffect7 } from "react";
941
-
942
- // src/spatialized-container/context/PortalInstanceContext.ts
943
- import { createContext as createContext3 } from "react";
619
+ // src/spatialized-container/types.ts
620
+ var SpatialCustomStyleVars = {
621
+ back: "--xr-back",
622
+ depth: "--xr-depth",
623
+ backgroundMaterial: "--xr-background-material",
624
+ xrZIndex: "--xr-z-index"
625
+ };
944
626
 
945
627
  // src/utils/debugTool.ts
946
628
  import { isSSREnv as isSSREnv2 } from "@webspatial/core-sdk";
@@ -959,6 +641,18 @@ function enableDebugTool() {
959
641
  });
960
642
  }
961
643
 
644
+ // src/utils/urlUtils.ts
645
+ function getAbsoluteUrl(url) {
646
+ if (typeof window === "undefined" || !window.location?.href) {
647
+ return url;
648
+ }
649
+ try {
650
+ return new URL(url, window.location.href).href;
651
+ } catch {
652
+ return url;
653
+ }
654
+ }
655
+
962
656
  // src/spatialized-container/context/PortalInstanceContext.ts
963
657
  var PortalInstanceObject = class {
964
658
  spatialId;
@@ -1143,14 +837,14 @@ var PortalInstanceContext = createContext3(
1143
837
  );
1144
838
 
1145
839
  // src/spatialized-container/hooks/useSync2DFrame.ts
1146
- import { useEffect as useEffect5, useState as useState2 } from "react";
840
+ import { useEffect as useEffect4, useState as useState2 } from "react";
1147
841
  function useForceUpdate() {
1148
842
  const [, setToggle] = useState2(false);
1149
843
  return () => setToggle((toggle) => !toggle);
1150
844
  }
1151
845
  function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObject) {
1152
846
  const forceUpdate = useForceUpdate();
1153
- useEffect5(() => {
847
+ useEffect4(() => {
1154
848
  spatializedContainerObject.on2DFrameChange(spatialId, () => {
1155
849
  portalInstanceObject.notify2DFrameChange();
1156
850
  forceUpdate();
@@ -1162,11 +856,11 @@ function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObj
1162
856
  }
1163
857
 
1164
858
  // src/spatialized-container/hooks/useSpatializedElement.ts
1165
- import { useEffect as useEffect6, useRef as useRef4, useState as useState3 } from "react";
859
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState3 } from "react";
1166
860
  function useSpatializedElement(createSpatializedElement2, portalInstanceObject) {
1167
861
  const [spatializedElement, setSpatializedElement] = useState3();
1168
- const elementRef = useRef4(void 0);
1169
- useEffect6(() => {
862
+ const elementRef = useRef3(void 0);
863
+ useEffect5(() => {
1170
864
  let isDestroyed = false;
1171
865
  createSpatializedElement2().then(
1172
866
  (inSpatializedElement) => {
@@ -1262,7 +956,7 @@ function PortalSpatializedContainer(props) {
1262
956
  ),
1263
957
  []
1264
958
  );
1265
- useEffect7(() => {
959
+ useEffect6(() => {
1266
960
  portalInstanceObject.init();
1267
961
  return () => {
1268
962
  portalInstanceObject.destroy();
@@ -1277,58 +971,417 @@ function PortalSpatializedContainer(props) {
1277
971
  portalInstanceObject,
1278
972
  props.component
1279
973
  );
1280
- useEffect7(() => {
974
+ useEffect6(() => {
1281
975
  if (spatializedElement) {
1282
976
  spatializedElement.onSpatialTap = onSpatialTap;
1283
977
  }
1284
978
  }, [spatializedElement, onSpatialTap]);
1285
- useEffect7(() => {
979
+ useEffect6(() => {
1286
980
  if (spatializedElement) {
1287
981
  spatializedElement.onSpatialDrag = onSpatialDrag;
1288
982
  }
1289
- }, [spatializedElement, onSpatialDrag]);
1290
- useEffect7(() => {
1291
- if (spatializedElement) {
1292
- spatializedElement.onSpatialDragEnd = onSpatialDragEnd;
983
+ }, [spatializedElement, onSpatialDrag]);
984
+ useEffect6(() => {
985
+ if (spatializedElement) {
986
+ spatializedElement.onSpatialDragEnd = onSpatialDragEnd;
987
+ }
988
+ }, [spatializedElement, onSpatialDragEnd]);
989
+ useEffect6(() => {
990
+ if (spatializedElement) {
991
+ spatializedElement.onSpatialRotate = onSpatialRotate;
992
+ }
993
+ }, [spatializedElement, onSpatialRotate]);
994
+ useEffect6(() => {
995
+ if (spatializedElement) {
996
+ spatializedElement.onSpatialRotateEnd = onSpatialRotateEnd;
997
+ }
998
+ }, [spatializedElement, onSpatialRotateEnd]);
999
+ useEffect6(() => {
1000
+ if (spatializedElement) {
1001
+ spatializedElement.onSpatialMagnify = onSpatialMagnify;
1002
+ }
1003
+ }, [spatializedElement, onSpatialMagnify]);
1004
+ useEffect6(() => {
1005
+ if (spatializedElement) {
1006
+ spatializedElement.onSpatialMagnifyEnd = onSpatialMagnifyEnd;
1007
+ }
1008
+ }, [spatializedElement, onSpatialMagnifyEnd]);
1009
+ useEffect6(() => {
1010
+ if (spatializedElement) {
1011
+ spatializedElement.onSpatialDragStart = onSpatialDragStart;
1012
+ }
1013
+ }, [spatializedElement, onSpatialDragStart]);
1014
+ const rotateConstraintKey = constrainedAxisKey(
1015
+ spatialEventOptions?.constrainedToAxis
1016
+ );
1017
+ useEffect6(() => {
1018
+ if (!spatializedElement) return;
1019
+ const axis = constrainedAxisToVec3(spatialEventOptions?.constrainedToAxis);
1020
+ void spatializedElement.updateProperties({ rotateConstrainedToAxis: axis });
1021
+ }, [spatializedElement, rotateConstraintKey]);
1022
+ return /* @__PURE__ */ jsxs(PortalInstanceContext.Provider, { value: portalInstanceObject, children: [
1023
+ spatializedElement && portalInstanceObject.dom && /* @__PURE__ */ jsx3(Content, { spatializedElement, ...restProps }),
1024
+ PlaceholderEl
1025
+ ] });
1026
+ }
1027
+
1028
+ // src/spatialized-container/hooks/useDomProxy.ts
1029
+ import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef4 } from "react";
1030
+ import { supports } from "@webspatial/core-sdk";
1031
+ var SpatialContainerRefProxy = class {
1032
+ transformVisibilityTaskContainerDom = null;
1033
+ /** Raw Standard host element (styled root). Used to mirror class onto the transform probe. */
1034
+ standardRawDom = null;
1035
+ standardClassObserver = null;
1036
+ /**
1037
+ * When set, Standard's DOM className is forwarded here so TransformVisibilityTaskContainer
1038
+ * can render it from React state (avoids React clobbering imperative class updates).
1039
+ */
1040
+ mirrorClassNotify = null;
1041
+ /** Last class string applied to the probe + used to skip redundant syncs. */
1042
+ lastMirroredClassName = null;
1043
+ /** Coalesce multiple class sync triggers in the same turn (Observer + classList, etc.). */
1044
+ classSyncMicrotaskQueued = false;
1045
+ ref;
1046
+ domProxy;
1047
+ /** Last value dispatched to `ref` (undefined = none yet); avoids duplicate null/proxy writes. */
1048
+ lastOutgoingToRef = void 0;
1049
+ installedProperties = [];
1050
+ // extre ref props, used to add extra props to ref
1051
+ extraRefProps;
1052
+ constructor(ref, extraRefProps) {
1053
+ this.ref = ref;
1054
+ this.extraRefProps = extraRefProps;
1055
+ }
1056
+ setMirrorClassNotify(fn) {
1057
+ this.mirrorClassNotify = fn;
1058
+ if (fn && this.standardRawDom) {
1059
+ this.flushSyncTransformClassFromStandard(true);
1060
+ }
1061
+ }
1062
+ disconnectStandardClassObserver() {
1063
+ this.standardClassObserver?.disconnect();
1064
+ this.standardClassObserver = null;
1065
+ }
1066
+ attachStandardClassObserver() {
1067
+ this.disconnectStandardClassObserver();
1068
+ if (!this.standardRawDom) {
1069
+ return;
1070
+ }
1071
+ this.standardClassObserver = new MutationObserver(() => {
1072
+ this.ensureSpatialDefaultClass();
1073
+ this.scheduleSyncTransformClassFromStandard();
1074
+ });
1075
+ this.standardClassObserver.observe(this.standardRawDom, {
1076
+ attributes: true,
1077
+ attributeFilter: ["class"]
1078
+ });
1079
+ }
1080
+ ensureSpatialDefaultClass() {
1081
+ const dom = this.standardRawDom;
1082
+ if (!dom) return;
1083
+ if (dom.classList.contains("xr-spatial-default")) return;
1084
+ dom.classList.add("xr-spatial-default");
1085
+ }
1086
+ /**
1087
+ * Merge multiple sync requests (e.g. classList hook + MutationObserver) into one microtask.
1088
+ */
1089
+ scheduleSyncTransformClassFromStandard() {
1090
+ if (this.classSyncMicrotaskQueued) {
1091
+ return;
1092
+ }
1093
+ this.classSyncMicrotaskQueued = true;
1094
+ queueMicrotask(() => {
1095
+ this.classSyncMicrotaskQueued = false;
1096
+ this.flushSyncTransformClassFromStandard(false);
1097
+ });
1098
+ }
1099
+ /**
1100
+ * Source of truth: Standard host DOM (incl. styled-components runtime class changes).
1101
+ * @param force when true, skip same-string short-circuit (e.g. mirror notify just registered).
1102
+ */
1103
+ flushSyncTransformClassFromStandard(force) {
1104
+ if (!this.standardRawDom) {
1105
+ return;
1106
+ }
1107
+ const name = this.standardRawDom.className;
1108
+ const probe = this.transformVisibilityTaskContainerDom;
1109
+ if (!force && probe && probe.className === name && this.lastMirroredClassName === name) {
1110
+ return;
1111
+ }
1112
+ this.lastMirroredClassName = name;
1113
+ if (probe) {
1114
+ probe.className = name;
1115
+ }
1116
+ this.mirrorClassNotify?.(name);
1117
+ }
1118
+ updateStandardSpatializedContainerDom(dom) {
1119
+ if (!dom) {
1120
+ this.disconnectStandardClassObserver();
1121
+ this.clearInstalledProperties();
1122
+ this.standardRawDom = null;
1123
+ this.lastMirroredClassName = null;
1124
+ this.domProxy = void 0;
1125
+ this.updateDomProxyToRef();
1126
+ return;
1127
+ }
1128
+ if (this.standardRawDom === dom && this.domProxy) {
1129
+ this.scheduleSyncTransformClassFromStandard();
1130
+ return;
1131
+ }
1132
+ this.clearInstalledProperties();
1133
+ this.standardRawDom = dom;
1134
+ this.domProxy = dom;
1135
+ this.installSpatialRefBehavior(dom);
1136
+ const root = dom.getRootNode();
1137
+ if (root === document || root instanceof ShadowRoot) {
1138
+ ensureSpatialDefaultStyleInRoot(root);
1139
+ }
1140
+ this.attachStandardClassObserver();
1141
+ this.updateDomProxyToRef();
1142
+ this.scheduleSyncTransformClassFromStandard();
1143
+ }
1144
+ clearInstalledProperties() {
1145
+ const dom = this.standardRawDom;
1146
+ if (!dom) return;
1147
+ for (const prop of this.installedProperties) {
1148
+ delete dom[prop];
1149
+ }
1150
+ this.installedProperties = [];
1151
+ }
1152
+ defineDomProperty(dom, prop, descriptor) {
1153
+ Object.defineProperty(dom, prop, {
1154
+ configurable: true,
1155
+ ...descriptor
1156
+ });
1157
+ this.installedProperties.push(prop);
1158
+ }
1159
+ installSpatialRefBehavior(dom) {
1160
+ const self = this;
1161
+ const rawStyle = dom.style;
1162
+ const rawRemoveProperty = rawStyle.removeProperty.bind(rawStyle);
1163
+ const rawGetPropertyValue = rawStyle.getPropertyValue.bind(rawStyle);
1164
+ const rawRemoveAttribute = dom.removeAttribute.bind(dom);
1165
+ const spatialStyleProperties = ["visibility", "transform"];
1166
+ const styleProxy = new Proxy(rawStyle, {
1167
+ get(target, prop) {
1168
+ if (prop === "visibility" || prop === "transform") {
1169
+ return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
1170
+ prop
1171
+ );
1172
+ }
1173
+ const value = Reflect.get(target, prop);
1174
+ if (typeof value === "function") {
1175
+ if (prop === "setProperty" || prop === "removeProperty" || prop === "getPropertyValue") {
1176
+ return function(...args) {
1177
+ const [property] = args;
1178
+ if (spatialStyleProperties.includes(property)) {
1179
+ if (prop === "setProperty") {
1180
+ const [, kValue, priority] = args;
1181
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1182
+ property,
1183
+ kValue,
1184
+ priority
1185
+ );
1186
+ } else if (prop === "removeProperty") {
1187
+ return self.transformVisibilityTaskContainerDom?.style.removeProperty(
1188
+ property
1189
+ );
1190
+ } else if (prop === "getPropertyValue") {
1191
+ return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
1192
+ property
1193
+ );
1194
+ }
1195
+ return void 0;
1196
+ }
1197
+ return value.apply(this, args);
1198
+ }.bind(target);
1199
+ }
1200
+ return value.bind(target);
1201
+ }
1202
+ return value;
1203
+ },
1204
+ set(target, prop, value) {
1205
+ if (prop === "visibility") {
1206
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1207
+ "visibility",
1208
+ value
1209
+ );
1210
+ return true;
1211
+ }
1212
+ if (prop === "transform") {
1213
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1214
+ "transform",
1215
+ value
1216
+ );
1217
+ return true;
1218
+ }
1219
+ if (Object.values(SpatialCustomStyleVars).includes(prop)) {
1220
+ target.setProperty(prop, value);
1221
+ return true;
1222
+ }
1223
+ if (prop === "cssText") {
1224
+ const { extractedValues, filteredCssText } = extractAndRemoveCustomProperties(
1225
+ value,
1226
+ spatialStyleProperties
1227
+ );
1228
+ spatialStyleProperties.forEach((key) => {
1229
+ if (extractedValues[key]) {
1230
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1231
+ key,
1232
+ extractedValues[key]
1233
+ );
1234
+ } else {
1235
+ rawRemoveProperty(key);
1236
+ }
1237
+ });
1238
+ const appendedCSSText = joinToCSSText({
1239
+ transform: "none",
1240
+ visibility: "hidden"
1241
+ });
1242
+ return Reflect.set(
1243
+ target,
1244
+ prop,
1245
+ [appendedCSSText, filteredCssText].join(";")
1246
+ );
1247
+ }
1248
+ return Reflect.set(target, prop, value);
1249
+ }
1250
+ });
1251
+ this.defineDomProperty(dom, "style", {
1252
+ get() {
1253
+ return styleProxy;
1254
+ },
1255
+ set(value) {
1256
+ styleProxy.cssText = String(value);
1257
+ }
1258
+ });
1259
+ this.defineDomProperty(dom, "className", {
1260
+ get() {
1261
+ return dom.getAttribute("class") ?? "";
1262
+ },
1263
+ set(value) {
1264
+ dom.setAttribute("class", String(value));
1265
+ if (!dom.classList.contains("xr-spatial-default")) {
1266
+ dom.classList.add("xr-spatial-default");
1267
+ }
1268
+ self.scheduleSyncTransformClassFromStandard();
1269
+ }
1270
+ });
1271
+ this.defineDomProperty(dom, "removeAttribute", {
1272
+ value(property) {
1273
+ if (property === "style") {
1274
+ rawStyle.cssText = "visibility: hidden; transition: none; transform: none;";
1275
+ self.transformVisibilityTaskContainerDom?.style.removeProperty(
1276
+ "visibility"
1277
+ );
1278
+ self.transformVisibilityTaskContainerDom?.style.removeProperty(
1279
+ "transform"
1280
+ );
1281
+ return;
1282
+ }
1283
+ if (property === "class") {
1284
+ dom.className = "xr-spatial-default";
1285
+ return;
1286
+ }
1287
+ return rawRemoveAttribute(property);
1288
+ }
1289
+ });
1290
+ if (supports("xrClientDepth")) {
1291
+ this.defineDomProperty(dom, "xrClientDepth", {
1292
+ get() {
1293
+ return rawGetPropertyValue(SpatialCustomStyleVars.depth);
1294
+ }
1295
+ });
1296
+ }
1297
+ if (supports("xrOffsetBack")) {
1298
+ this.defineDomProperty(dom, "xrOffsetBack", {
1299
+ get() {
1300
+ return rawGetPropertyValue(SpatialCustomStyleVars.back);
1301
+ }
1302
+ });
1293
1303
  }
1294
- }, [spatializedElement, onSpatialDragEnd]);
1295
- useEffect7(() => {
1296
- if (spatializedElement) {
1297
- spatializedElement.onSpatialRotate = onSpatialRotate;
1304
+ if (this.extraRefProps) {
1305
+ const extraProps = this.extraRefProps(dom);
1306
+ for (const prop of Object.keys(extraProps)) {
1307
+ this.defineDomProperty(dom, prop, {
1308
+ get() {
1309
+ return extraProps[prop];
1310
+ },
1311
+ set(value) {
1312
+ extraProps[prop] = value;
1313
+ }
1314
+ });
1315
+ }
1298
1316
  }
1299
- }, [spatializedElement, onSpatialRotate]);
1300
- useEffect7(() => {
1301
- if (spatializedElement) {
1302
- spatializedElement.onSpatialRotateEnd = onSpatialRotateEnd;
1317
+ }
1318
+ updateTransformVisibilityTaskContainerDom(dom) {
1319
+ this.transformVisibilityTaskContainerDom = dom;
1320
+ if (!dom) {
1321
+ this.lastMirroredClassName = null;
1303
1322
  }
1304
- }, [spatializedElement, onSpatialRotateEnd]);
1305
- useEffect7(() => {
1306
- if (spatializedElement) {
1307
- spatializedElement.onSpatialMagnify = onSpatialMagnify;
1323
+ this.scheduleSyncTransformClassFromStandard();
1324
+ this.updateDomProxyToRef();
1325
+ }
1326
+ updateDomProxyToRef() {
1327
+ const ref = this.ref;
1328
+ if (!ref) {
1329
+ return;
1308
1330
  }
1309
- }, [spatializedElement, onSpatialMagnify]);
1310
- useEffect7(() => {
1311
- if (spatializedElement) {
1312
- spatializedElement.onSpatialMagnifyEnd = onSpatialMagnifyEnd;
1331
+ const next = this.domProxy && this.transformVisibilityTaskContainerDom ? this.domProxy : null;
1332
+ if (this.lastOutgoingToRef === next) {
1333
+ return;
1313
1334
  }
1314
- }, [spatializedElement, onSpatialMagnifyEnd]);
1315
- useEffect7(() => {
1316
- if (spatializedElement) {
1317
- spatializedElement.onSpatialDragStart = onSpatialDragStart;
1335
+ this.lastOutgoingToRef = next;
1336
+ if (next) {
1337
+ if (typeof ref === "function") {
1338
+ ref(next);
1339
+ } else {
1340
+ ref.current = next;
1341
+ }
1342
+ } else {
1343
+ if (typeof ref === "function") {
1344
+ ref(null);
1345
+ } else {
1346
+ ref.current = null;
1347
+ }
1318
1348
  }
1319
- }, [spatializedElement, onSpatialDragStart]);
1320
- const rotateConstraintKey = constrainedAxisKey(
1321
- spatialEventOptions?.constrainedToAxis
1349
+ }
1350
+ updateRef(ref) {
1351
+ if (this.ref === ref) {
1352
+ return;
1353
+ }
1354
+ this.ref = ref;
1355
+ this.lastOutgoingToRef = void 0;
1356
+ this.updateDomProxyToRef();
1357
+ }
1358
+ };
1359
+ function useDomProxy(ref, extraRefProps) {
1360
+ const spatialContainerRefProxy = useRef4(
1361
+ new SpatialContainerRefProxy(ref, extraRefProps)
1322
1362
  );
1323
1363
  useEffect7(() => {
1324
- if (!spatializedElement) return;
1325
- const axis = constrainedAxisToVec3(spatialEventOptions?.constrainedToAxis);
1326
- void spatializedElement.updateProperties({ rotateConstrainedToAxis: axis });
1327
- }, [spatializedElement, rotateConstraintKey]);
1328
- return /* @__PURE__ */ jsxs(PortalInstanceContext.Provider, { value: portalInstanceObject, children: [
1329
- spatializedElement && portalInstanceObject.dom && /* @__PURE__ */ jsx3(Content, { spatializedElement, ...restProps }),
1330
- PlaceholderEl
1331
- ] });
1364
+ spatialContainerRefProxy.current.updateRef(ref);
1365
+ }, [ref]);
1366
+ const transformVisibilityTaskContainerCallback = useCallback5(
1367
+ (el) => {
1368
+ spatialContainerRefProxy.current.updateTransformVisibilityTaskContainerDom(
1369
+ el
1370
+ );
1371
+ },
1372
+ []
1373
+ );
1374
+ const standardSpatializedContainerCallback = useCallback5(
1375
+ (el) => {
1376
+ spatialContainerRefProxy.current.updateStandardSpatializedContainerDom(el);
1377
+ },
1378
+ []
1379
+ );
1380
+ return {
1381
+ transformVisibilityTaskContainerCallback,
1382
+ standardSpatializedContainerCallback,
1383
+ spatialContainerRefProxy
1384
+ };
1332
1385
  }
1333
1386
 
1334
1387
  // src/reality/context/InsideAttachmentContext.tsx
@@ -1570,6 +1623,7 @@ function withSSRSupported(Component) {
1570
1623
  import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1571
1624
  function DegradedContainer({
1572
1625
  innerRef,
1626
+ enableOnSpatialContentReadyFallback,
1573
1627
  ...inprops
1574
1628
  }) {
1575
1629
  const {
@@ -1590,9 +1644,48 @@ function DegradedContainer({
1590
1644
  getExtraSpatializedElementProperties: _getExtra,
1591
1645
  extraRefProps: _extraRef,
1592
1646
  sizingMode: _sizingMode,
1647
+ onSpatialContentReady: _onSpatialContentReady,
1593
1648
  ...restProps
1594
1649
  } = inprops;
1595
- return /* @__PURE__ */ jsx6(Component, { ref: innerRef, ...restProps, children });
1650
+ const [hostEl, setHostEl] = useState6(null);
1651
+ const callbackRef = useRef5(_onSpatialContentReady);
1652
+ callbackRef.current = _onSpatialContentReady;
1653
+ useLayoutEffect2(() => {
1654
+ if (!enableOnSpatialContentReadyFallback || !hostEl || !hostEl.isConnected || !callbackRef.current) {
1655
+ return () => {
1656
+ };
1657
+ }
1658
+ let cleanup;
1659
+ try {
1660
+ cleanup = callbackRef.current({ host: hostEl });
1661
+ } catch (e) {
1662
+ if (process.env.NODE_ENV !== "production") {
1663
+ console.error("[WebSpatial] onSpatialContentReady threw", e);
1664
+ }
1665
+ }
1666
+ return () => {
1667
+ if (typeof cleanup !== "function") return;
1668
+ try {
1669
+ cleanup();
1670
+ } catch (e) {
1671
+ if (process.env.NODE_ENV !== "production") {
1672
+ console.error("[WebSpatial] onSpatialContentReady cleanup threw", e);
1673
+ }
1674
+ }
1675
+ };
1676
+ }, [enableOnSpatialContentReadyFallback, hostEl]);
1677
+ const setHostRef = useCallback6(
1678
+ (node) => {
1679
+ if (typeof innerRef === "function") {
1680
+ innerRef(node);
1681
+ } else if (innerRef != null) {
1682
+ innerRef.current = node;
1683
+ }
1684
+ setHostEl(node);
1685
+ },
1686
+ [innerRef]
1687
+ );
1688
+ return /* @__PURE__ */ jsx6(Component, { ref: setHostRef, ...restProps, children });
1596
1689
  }
1597
1690
  function SpatializedContainerBase(inprops, ref) {
1598
1691
  const isWebSpatialEnv = getSession() !== null;
@@ -1603,7 +1696,14 @@ function SpatializedContainerBase(inprops, ref) {
1603
1696
  `[WebSpatial] ${inprops.component || "Spatial element"} cannot be used inside AttachmentAsset. Rendering as plain HTML.`
1604
1697
  );
1605
1698
  }
1606
- return /* @__PURE__ */ jsx6(DegradedContainer, { ...inprops, innerRef: ref });
1699
+ return /* @__PURE__ */ jsx6(
1700
+ DegradedContainer,
1701
+ {
1702
+ ...inprops,
1703
+ innerRef: ref,
1704
+ enableOnSpatialContentReadyFallback: !isWebSpatialEnv && !insideAttachment
1705
+ }
1706
+ );
1607
1707
  }
1608
1708
  const layer = useContext7(SpatialLayerContext) + 1;
1609
1709
  const rootSpatializedContainerObject = useContext7(
@@ -1686,6 +1786,7 @@ function SpatializedContainerBase(inprops, ref) {
1686
1786
  createSpatializedElement: createSpatializedElement2,
1687
1787
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1688
1788
  spatialEventOptions: _nestedSpatialEventOptions,
1789
+ onSpatialContentReady: _nestedOnSpatialContentReady,
1689
1790
  ...restProps
1690
1791
  } = props;
1691
1792
  return /* @__PURE__ */ jsxs2(SpatialLayerContext.Provider, { value: layer, children: [
@@ -1749,6 +1850,7 @@ function SpatializedContainerBase(inprops, ref) {
1749
1850
  createSpatializedElement: createSpatializedElement2,
1750
1851
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1751
1852
  spatialEventOptions: _rootSpatialEventOptions,
1853
+ onSpatialContentReady: _rootOnSpatialContentReady,
1752
1854
  ...restProps
1753
1855
  } = props;
1754
1856
  return /* @__PURE__ */ jsx6(SpatialLayerContext.Provider, { value: layer, children: /* @__PURE__ */ jsxs2(
@@ -1795,8 +1897,10 @@ var SpatializedContainer = withSSRSupported(
1795
1897
  import { createPortal as createPortal2 } from "react-dom";
1796
1898
  import {
1797
1899
  forwardRef as forwardRef5,
1900
+ useCallback as useCallback7,
1798
1901
  useContext as useContext8,
1799
- useEffect as useEffect12
1902
+ useEffect as useEffect12,
1903
+ useState as useState7
1800
1904
  } from "react";
1801
1905
 
1802
1906
  // src/utils/windowStyleSync.ts
@@ -1909,55 +2013,145 @@ async function syncParentHeadToChild(childWindow) {
1909
2013
 
1910
2014
  // src/utils/useSyncHeadStyles.ts
1911
2015
  import { useEffect as useEffect11 } from "react";
1912
- function defaultShouldSync(mutations) {
1913
- if (!Array.isArray(mutations) || mutations.length === 0) return false;
2016
+ function getSyncTiming(mutations) {
2017
+ if (!Array.isArray(mutations) || mutations.length === 0) return null;
2018
+ let hasDelayed = false;
1914
2019
  for (const mutation of mutations) {
2020
+ if (mutation.type === "characterData") {
2021
+ const parent = mutation.target.parentElement;
2022
+ if (parent?.tagName === "STYLE") return "immediate";
2023
+ }
1915
2024
  const nodes = [
2025
+ mutation.target,
1916
2026
  ...Array.from(mutation.addedNodes),
1917
2027
  ...Array.from(mutation.removedNodes)
1918
2028
  ];
1919
2029
  for (const node of nodes) {
1920
2030
  if (!(node instanceof Element)) continue;
1921
2031
  const tag = node.tagName;
1922
- if (tag === "STYLE") return true;
2032
+ if (tag === "STYLE") return "immediate";
1923
2033
  if (tag === "LINK") {
1924
2034
  const { rel } = node;
1925
- if (rel && rel.toLowerCase() === "stylesheet") return true;
2035
+ if (rel && rel.toLowerCase() === "stylesheet") hasDelayed = true;
1926
2036
  }
1927
2037
  }
1928
2038
  }
1929
- return false;
2039
+ return hasDelayed ? "delayed" : null;
1930
2040
  }
1931
2041
  function useSyncHeadStyles(childWindow, options) {
1932
2042
  const delayMs = 100;
1933
- const subtree = options?.subtree ?? false;
2043
+ const subtree = options?.subtree ?? true;
1934
2044
  const immediate = options?.immediate ?? true;
1935
2045
  useEffect11(() => {
1936
2046
  if (!childWindow) return;
1937
2047
  let timer;
1938
- const scheduleSync = () => {
2048
+ let immediateQueued = false;
2049
+ let disposed = false;
2050
+ const scheduleSync = (timing = "delayed") => {
1939
2051
  if (timer) window.clearTimeout(timer);
2052
+ if (timing === "immediate") {
2053
+ if (immediateQueued) return;
2054
+ immediateQueued = true;
2055
+ queueMicrotask(() => {
2056
+ immediateQueued = false;
2057
+ if (disposed) return;
2058
+ syncParentHeadToChild(childWindow);
2059
+ });
2060
+ return;
2061
+ }
1940
2062
  timer = window.setTimeout(() => {
2063
+ if (disposed) return;
1941
2064
  syncParentHeadToChild(childWindow);
1942
2065
  }, delayMs);
1943
2066
  };
1944
2067
  if (immediate) scheduleSync();
1945
2068
  const observer = new MutationObserver((mutations) => {
1946
- if (!defaultShouldSync(mutations)) return;
1947
- scheduleSync();
2069
+ const timing = getSyncTiming(mutations);
2070
+ if (!timing) return;
2071
+ scheduleSync(timing);
2072
+ });
2073
+ observer.observe(document.head, {
2074
+ childList: true,
2075
+ characterData: true,
2076
+ subtree
1948
2077
  });
1949
- observer.observe(document.head, { childList: true, subtree });
1950
2078
  return () => {
2079
+ disposed = true;
1951
2080
  if (timer) window.clearTimeout(timer);
1952
2081
  observer.disconnect();
1953
2082
  };
1954
2083
  }, [childWindow, delayMs, subtree, immediate]);
1955
2084
  }
1956
2085
 
2086
+ // src/spatialized-container/hooks/useSpatialContentReady.ts
2087
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef6 } from "react";
2088
+ var isDev = process.env.NODE_ENV !== "production";
2089
+ function safeInvokeCleanup(cleanup) {
2090
+ if (!cleanup) return;
2091
+ try {
2092
+ cleanup();
2093
+ } catch (e) {
2094
+ if (isDev) {
2095
+ console.error("[WebSpatial] onSpatialContentReady cleanup threw", e);
2096
+ }
2097
+ }
2098
+ }
2099
+ function useSpatialContentReady(params) {
2100
+ const {
2101
+ spatializedElement,
2102
+ portalInstanceObject,
2103
+ hostElement,
2104
+ onSpatialContentReady
2105
+ } = params;
2106
+ const callbackRef = useRef6(onSpatialContentReady);
2107
+ callbackRef.current = onSpatialContentReady;
2108
+ useLayoutEffect3(() => {
2109
+ const dom = portalInstanceObject.dom;
2110
+ const isReady = !!(spatializedElement && dom && hostElement && hostElement.isConnected);
2111
+ if (!isReady || !hostElement) {
2112
+ return () => {
2113
+ };
2114
+ }
2115
+ const cb = callbackRef.current;
2116
+ let cleanupFromCallback;
2117
+ if (cb) {
2118
+ try {
2119
+ const ret = cb({ host: hostElement });
2120
+ cleanupFromCallback = typeof ret === "function" ? ret : void 0;
2121
+ } catch (e) {
2122
+ if (isDev) {
2123
+ console.error("[WebSpatial] onSpatialContentReady threw", e);
2124
+ }
2125
+ }
2126
+ }
2127
+ return () => {
2128
+ safeInvokeCleanup(cleanupFromCallback);
2129
+ };
2130
+ }, [spatializedElement, portalInstanceObject.dom, hostElement]);
2131
+ }
2132
+
1957
2133
  // src/spatialized-container/Spatialized2DElementContainer.tsx
1958
2134
  import { jsx as jsx7 } from "react/jsx-runtime";
1959
- function getJSXPortalInstance(inProps, portalInstanceObject) {
1960
- const { component: El, style: inStyle = {}, ...props } = inProps;
2135
+ function mergeRefs(...refs) {
2136
+ return (value) => {
2137
+ for (const ref of refs) {
2138
+ if (ref == null) continue;
2139
+ if (typeof ref === "function") {
2140
+ ref(value);
2141
+ } else {
2142
+ ;
2143
+ ref.current = value;
2144
+ }
2145
+ }
2146
+ };
2147
+ }
2148
+ function getJSXPortalInstance(inProps, portalInstanceObject, hostRef) {
2149
+ const {
2150
+ component: El,
2151
+ style: inStyle = {},
2152
+ ref: userRef,
2153
+ ...props
2154
+ } = inProps;
1961
2155
  const extraStyle = {
1962
2156
  visibility: "visible",
1963
2157
  position: "relative",
@@ -1979,7 +2173,8 @@ function getJSXPortalInstance(inProps, portalInstanceObject) {
1979
2173
  ...inheritedPortalStyle,
1980
2174
  ...extraStyle
1981
2175
  };
1982
- return /* @__PURE__ */ jsx7(El, { style, ...props });
2176
+ const mergedRef = hostRef != null ? mergeRefs(hostRef, userRef) : userRef;
2177
+ return /* @__PURE__ */ jsx7(El, { ref: mergedRef, style, ...props });
1983
2178
  }
1984
2179
  function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
1985
2180
  useEffect12(() => {
@@ -1990,20 +2185,31 @@ function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
1990
2185
  }, [name]);
1991
2186
  }
1992
2187
  function SpatializedContent(props) {
1993
- const { spatializedElement, ...restProps } = props;
2188
+ const { spatializedElement, onSpatialContentReady, ...restProps } = props;
1994
2189
  const spatialized2DElement = spatializedElement;
1995
2190
  const { windowProxy } = spatialized2DElement;
2191
+ const [hostEl, setHostEl] = useState7(null);
2192
+ const portalInstanceObject = useContext8(
2193
+ PortalInstanceContext
2194
+ );
2195
+ useSpatialContentReady({
2196
+ spatializedElement,
2197
+ portalInstanceObject,
2198
+ hostElement: hostEl,
2199
+ onSpatialContentReady
2200
+ });
1996
2201
  useSyncHeadStyles(windowProxy, {
1997
2202
  subtree: false
1998
2203
  });
1999
2204
  const name = restProps["data-name"] || "";
2000
2205
  useSyncDocumentTitle(windowProxy, spatialized2DElement, name);
2001
- const portalInstanceObject = useContext8(
2002
- PortalInstanceContext
2003
- );
2206
+ const setHostCallback = useCallback7((el) => {
2207
+ setHostEl(el);
2208
+ }, []);
2004
2209
  const JSXPortalInstance = getJSXPortalInstance(
2005
2210
  restProps,
2006
- portalInstanceObject
2211
+ portalInstanceObject,
2212
+ setHostCallback
2007
2213
  );
2008
2214
  return createPortal2(JSXPortalInstance, windowProxy.document.body);
2009
2215
  }
@@ -2061,11 +2267,11 @@ import {
2061
2267
  Children,
2062
2268
  forwardRef as forwardRef6,
2063
2269
  isValidElement,
2064
- useCallback as useCallback7,
2270
+ useCallback as useCallback8,
2065
2271
  useContext as useContext9,
2066
2272
  useEffect as useEffect13,
2067
2273
  useMemo as useMemo3,
2068
- useRef as useRef5
2274
+ useRef as useRef7
2069
2275
  } from "react";
2070
2276
  import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
2071
2277
  function getAbsoluteURL(url) {
@@ -2110,24 +2316,57 @@ function collectSources(children) {
2110
2316
  return sources;
2111
2317
  }
2112
2318
  function SpatializedContent2(props) {
2113
- const { src, children, spatializedElement, onLoad, onError, autoPlay, loop } = props;
2319
+ const {
2320
+ src,
2321
+ poster,
2322
+ children,
2323
+ spatializedElement,
2324
+ onLoad,
2325
+ onError,
2326
+ autoPlay,
2327
+ loop,
2328
+ loading = "eager"
2329
+ } = props;
2114
2330
  const portalInstanceObject = useContext9(PortalInstanceContext);
2331
+ const wasVisible = useRef7(false);
2115
2332
  const modelURL = useMemo3(() => getAbsoluteURL(src), [src]);
2333
+ const posterURL = useMemo3(() => getAbsoluteURL(poster), [poster]);
2116
2334
  const sources = useMemo3(() => collectSources(children), [children]);
2335
+ const sourcesKey = useMemo3(() => JSON.stringify(sources), [sources]);
2336
+ useEffect13(() => {
2337
+ wasVisible.current = false;
2338
+ const target = portalInstanceObject.dom;
2339
+ if (loading !== "lazy" || !target) {
2340
+ wasVisible.current = true;
2341
+ return;
2342
+ }
2343
+ const observer = new IntersectionObserver((entries) => {
2344
+ if (entries.some((entry) => entry.isIntersecting)) {
2345
+ wasVisible.current = true;
2346
+ observer.disconnect();
2347
+ spatializedElement.updateProperties({ loading: "eager" });
2348
+ }
2349
+ });
2350
+ observer.observe(target);
2351
+ return () => observer.disconnect();
2352
+ }, [modelURL, sourcesKey, portalInstanceObject]);
2117
2353
  useEffect13(() => {
2354
+ if (loading !== "lazy") wasVisible.current = true;
2118
2355
  spatializedElement.updateProperties({
2119
- modelURL: modelURL ?? "",
2356
+ modelURL: modelURL ?? (spatializedElement.modelUrl ? "" : modelURL),
2120
2357
  sources,
2121
2358
  autoplay: autoPlay,
2122
- loop
2359
+ loop,
2360
+ posterURL: posterURL ?? "",
2361
+ loading: loading === "lazy" && wasVisible.current ? "eager" : loading
2123
2362
  });
2124
- }, [modelURL, JSON.stringify(sources), autoPlay, loop]);
2363
+ }, [modelURL, sourcesKey, autoPlay, loop, posterURL, loading]);
2125
2364
  useEffect13(() => {
2126
2365
  if (onLoad) {
2127
2366
  spatializedElement.onLoadCallback = () => {
2128
2367
  onLoad(
2129
2368
  createLoadSuccessEvent(
2130
- () => portalInstanceObject.dom.__targetProxy
2369
+ () => portalInstanceObject.dom
2131
2370
  )
2132
2371
  );
2133
2372
  };
@@ -2140,7 +2379,7 @@ function SpatializedContent2(props) {
2140
2379
  spatializedElement.onLoadFailureCallback = () => {
2141
2380
  onError(
2142
2381
  createLoadFailureEvent(
2143
- () => portalInstanceObject.dom.__targetProxy
2382
+ () => portalInstanceObject.dom
2144
2383
  )
2145
2384
  );
2146
2385
  };
@@ -2151,15 +2390,16 @@ function SpatializedContent2(props) {
2151
2390
  return /* @__PURE__ */ jsx8(Fragment2, {});
2152
2391
  }
2153
2392
  function SpatializedStatic3DElementContainerBase(props, ref) {
2154
- const promiseRef = useRef5(null);
2155
- const createSpatializedElement2 = useCallback7(() => {
2393
+ const promiseRef = useRef7(null);
2394
+ const createSpatializedElement2 = useCallback8(() => {
2156
2395
  promiseRef.current = getSession().createSpatializedStatic3DElement(
2157
2396
  getAbsoluteURL(props.src),
2158
- collectSources(props.children)
2397
+ collectSources(props.children),
2398
+ props.loading === "lazy" ? "lazy" : "eager"
2159
2399
  );
2160
2400
  return promiseRef.current;
2161
2401
  }, []);
2162
- const extraRefProps = useCallback7(
2402
+ const extraRefProps = useCallback8(
2163
2403
  (domProxy) => {
2164
2404
  let modelTransform = new DOMMatrixReadOnly();
2165
2405
  return {
@@ -2206,6 +2446,16 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
2206
2446
  if (spatializedElement) {
2207
2447
  spatializedElement.playbackRate = value;
2208
2448
  }
2449
+ },
2450
+ get currentTime() {
2451
+ const spatializedElement = domProxy.__spatializedElement;
2452
+ return spatializedElement?.currentTime ?? 0;
2453
+ },
2454
+ set currentTime(value) {
2455
+ const spatializedElement = domProxy.__spatializedElement;
2456
+ if (spatializedElement) {
2457
+ spatializedElement.currentTime = value;
2458
+ }
2209
2459
  }
2210
2460
  };
2211
2461
  },
@@ -2262,11 +2512,19 @@ function withSpatialized2DElementContainer(Component) {
2262
2512
 
2263
2513
  // src/spatialized-container/index.ts
2264
2514
  function initPolyfill() {
2265
- hijackGetComputedStyle();
2266
2515
  injectSpatialDefaultStyle();
2267
2516
  initCSSParserDivContainer();
2268
2517
  }
2269
2518
 
2519
+ // src/webSpatialRuntime.ts
2520
+ import { supports as supports2 } from "@webspatial/core-sdk";
2521
+ var WebSpatialRuntime = {
2522
+ supports: supports2
2523
+ };
2524
+
2525
+ // src/index.ts
2526
+ import { WebSpatialRuntimeError } from "@webspatial/core-sdk";
2527
+
2270
2528
  // src/initScene.ts
2271
2529
  function initScene(name, callback, options) {
2272
2530
  return getSession()?.initScene(name, callback, options);
@@ -2276,9 +2534,9 @@ function initScene(name, callback, options) {
2276
2534
  import { forwardRef as forwardRef9 } from "react";
2277
2535
 
2278
2536
  // src/spatialized-container-monitor/useMonitorDomChange.tsx
2279
- import { useRef as useRef6, useEffect as useEffect14, useMemo as useMemo4 } from "react";
2537
+ import { useRef as useRef8, useEffect as useEffect14, useMemo as useMemo4 } from "react";
2280
2538
  function useMonitorDomChange(inRef) {
2281
- const ref = useRef6(null);
2539
+ const ref = useRef8(null);
2282
2540
  useEffect14(() => {
2283
2541
  const observer = new MutationObserver((mutationsList) => {
2284
2542
  notifyDOMUpdate(mutationsList);
@@ -2440,37 +2698,87 @@ var AttachmentRegistry = class {
2440
2698
  var AttachmentContext = createContext8(null);
2441
2699
 
2442
2700
  // src/reality/hooks/useEntityTransform.tsx
2443
- import { useEffect as useEffect16, useRef as useRef7 } from "react";
2701
+ import { useEffect as useEffect16, useRef as useRef9 } from "react";
2444
2702
 
2445
2703
  // src/reality/utils/ResourceRegistry.ts
2446
2704
  var ResourceRegistry = class {
2705
+ /** Every id maps to a promise — either the real resource from add(), or a placeholder until then. */
2447
2706
  resources = /* @__PURE__ */ new Map();
2707
+ /** If get() ran first, we stash resolve/reject so add() can complete the waiting promise. */
2708
+ deferreds = /* @__PURE__ */ new Map();
2709
+ /** Subscribers are notified whenever an id gets a fresh resolved or failed resource attempt. */
2710
+ listeners = /* @__PURE__ */ new Map();
2448
2711
  add(id, resource) {
2449
2712
  this.resources.set(id, resource);
2713
+ const deferred = this.deferreds.get(id);
2714
+ if (deferred) {
2715
+ resource.then(deferred.resolve).catch(
2716
+ (err) => deferred.reject(err instanceof Error ? err : new Error(String(err)))
2717
+ );
2718
+ this.deferreds.delete(id);
2719
+ }
2720
+ resource.then(() => this.notify(id)).catch(() => this.notify(id));
2721
+ }
2722
+ subscribe(id, listener) {
2723
+ const listeners = this.listeners.get(id) ?? /* @__PURE__ */ new Set();
2724
+ listeners.add(listener);
2725
+ this.listeners.set(id, listeners);
2726
+ return () => {
2727
+ listeners.delete(listener);
2728
+ if (listeners.size === 0) {
2729
+ this.listeners.delete(id);
2730
+ }
2731
+ };
2732
+ }
2733
+ notify(id) {
2734
+ const listeners = this.listeners.get(id);
2735
+ if (!listeners) return;
2736
+ for (const listener of Array.from(listeners)) {
2737
+ listener();
2738
+ }
2739
+ }
2740
+ get(id) {
2741
+ const existing = this.resources.get(id);
2742
+ if (existing) {
2743
+ return existing;
2744
+ }
2745
+ const promise = new Promise((resolve, reject) => {
2746
+ this.deferreds.set(id, { resolve, reject });
2747
+ });
2748
+ this.resources.set(id, promise);
2749
+ return promise;
2450
2750
  }
2451
2751
  remove(id) {
2452
2752
  this.resources.delete(id);
2753
+ this.deferreds.delete(id);
2754
+ this.notify(id);
2453
2755
  }
2454
- // Remove the resource by id and destroy it once resolved
2455
- // This does not cancel in-flight creation; it schedules destruction after resolution
2756
+ // Same as remove, but when the promise resolves, destroy the spatial object (best-effort).
2456
2757
  removeAndDestroy(id) {
2457
2758
  const p = this.resources.get(id);
2458
2759
  if (p) {
2459
- p.then((spatialObj) => spatialObj.destroy()).catch(() => {
2760
+ p.then((obj) => obj.destroy()).catch(() => {
2460
2761
  });
2461
2762
  }
2462
2763
  this.resources.delete(id);
2463
- }
2464
- get(id) {
2465
- return this.resources.get(id);
2764
+ this.deferreds.delete(id);
2765
+ this.notify(id);
2466
2766
  }
2467
2767
  destroy() {
2768
+ for (const [id, deferred] of this.deferreds) {
2769
+ deferred.reject(
2770
+ new Error(`ResourceRegistry destroyed \u2014 "${id}" never resolved`)
2771
+ );
2772
+ }
2773
+ this.deferreds.clear();
2774
+ const ids = Array.from(this.resources.keys());
2775
+ for (const id of ids) {
2776
+ this.notify(id);
2777
+ }
2778
+ this.listeners.clear();
2468
2779
  const pending = Array.from(this.resources.values());
2469
2780
  this.resources.clear();
2470
- pending.forEach(
2471
- (promise) => promise.then((spatialObj) => spatialObj.destroy()).catch(() => {
2472
- })
2473
- );
2781
+ void Promise.allSettled(pending.map((p) => p.then((obj) => obj.destroy())));
2474
2782
  }
2475
2783
  };
2476
2784
 
@@ -2502,15 +2810,14 @@ function shallowEqualArray(a, b) {
2502
2810
 
2503
2811
  // src/reality/utils/AbortResourceManager.ts
2504
2812
  var AbortResourceManager = class {
2813
+ resources = [];
2814
+ aborted = false;
2505
2815
  constructor(signal) {
2506
- this.signal = signal;
2507
2816
  signal.addEventListener("abort", () => {
2508
2817
  this.aborted = true;
2509
2818
  void this.dispose();
2510
2819
  });
2511
2820
  }
2512
- resources = [];
2513
- aborted = false;
2514
2821
  async addResource(factory) {
2515
2822
  if (this.aborted) throw new DOMException("Aborted", "AbortError");
2516
2823
  const resource = await factory();
@@ -2535,7 +2842,7 @@ var AbortResourceManager = class {
2535
2842
 
2536
2843
  // src/reality/hooks/useEntityTransform.tsx
2537
2844
  function useEntityTransform(entity, { position, rotation, scale }) {
2538
- const last = useRef7({});
2845
+ const last = useRef9({});
2539
2846
  useEffect16(() => {
2540
2847
  if (!entity) return;
2541
2848
  const shouldUpdate = !shallowEqualVec3(last.current.position, position) || !shallowEqualRotation(last.current.rotation, rotation) || !shallowEqualVec3(last.current.scale, scale);
@@ -2555,7 +2862,7 @@ function useEntityTransform(entity, { position, rotation, scale }) {
2555
2862
  }
2556
2863
 
2557
2864
  // src/reality/hooks/useEntityEvent.tsx
2558
- import { useEffect as useEffect18, useRef as useRef9 } from "react";
2865
+ import { useEffect as useEffect17, useRef as useRef10 } from "react";
2559
2866
 
2560
2867
  // src/reality/type.ts
2561
2868
  var eventMap = {
@@ -2757,8 +3064,8 @@ function createEventProxy2(ev, instance) {
2757
3064
  });
2758
3065
  }
2759
3066
  var useEntityEvent = ({ instance, ...handlers }) => {
2760
- const eventsSetRef = useRef9(/* @__PURE__ */ new Set());
2761
- useEffect18(() => {
3067
+ const eventsSetRef = useRef10(/* @__PURE__ */ new Set());
3068
+ useEffect17(() => {
2762
3069
  const entity = instance.entity;
2763
3070
  if (!entity) return;
2764
3071
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
@@ -2771,7 +3078,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2771
3078
  return () => {
2772
3079
  };
2773
3080
  }, [instance.entity, ...Object.values(handlers)]);
2774
- useEffect18(() => {
3081
+ useEffect17(() => {
2775
3082
  const entity = instance.entity;
2776
3083
  if (!entity) return;
2777
3084
  return () => {
@@ -2785,7 +3092,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2785
3092
  };
2786
3093
 
2787
3094
  // src/reality/hooks/useRealityEvents.tsx
2788
- import { useEffect as useEffect19, useRef as useRef10 } from "react";
3095
+ import { useEffect as useEffect18, useRef as useRef11 } from "react";
2789
3096
  function createEventProxy3(ev, instance) {
2790
3097
  return new Proxy(ev, {
2791
3098
  get(target, prop) {
@@ -2899,8 +3206,8 @@ function createEventProxy3(ev, instance) {
2899
3206
  });
2900
3207
  }
2901
3208
  var useRealityEvents = ({ instance, ...handlers }) => {
2902
- const eventsSetRef = useRef10(/* @__PURE__ */ new Set());
2903
- useEffect19(() => {
3209
+ const eventsSetRef = useRef11(/* @__PURE__ */ new Set());
3210
+ useEffect18(() => {
2904
3211
  if (!instance) return;
2905
3212
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
2906
3213
  const handlerFn = handlers[reactKey];
@@ -2921,10 +3228,10 @@ var useRealityEvents = ({ instance, ...handlers }) => {
2921
3228
  };
2922
3229
 
2923
3230
  // src/reality/hooks/useEntityId.tsx
2924
- import { useEffect as useEffect20 } from "react";
3231
+ import { useEffect as useEffect19 } from "react";
2925
3232
  var useEntityId = ({ id, entity }) => {
2926
3233
  const ctx = useRealityContext();
2927
- useEffect20(() => {
3234
+ useEffect19(() => {
2928
3235
  if (!id || !entity || !ctx) return;
2929
3236
  ctx.resourceRegistry.add(id, Promise.resolve(entity));
2930
3237
  return () => {
@@ -2935,7 +3242,7 @@ var useEntityId = ({ id, entity }) => {
2935
3242
  };
2936
3243
 
2937
3244
  // src/reality/hooks/useEntity.tsx
2938
- import { useEffect as useEffect21, useRef as useRef11 } from "react";
3245
+ import { useEffect as useEffect20, useRef as useRef12 } from "react";
2939
3246
  var useEntity = ({
2940
3247
  ref,
2941
3248
  id,
@@ -2956,9 +3263,9 @@ var useEntity = ({
2956
3263
  }) => {
2957
3264
  const ctx = useRealityContext();
2958
3265
  const parent = useParentContext();
2959
- const instanceRef = useRef11(new EntityRef(null, ctx));
3266
+ const instanceRef = useRef12(new EntityRef(null, ctx));
2960
3267
  const forceUpdate = useForceUpdate2();
2961
- useEffect21(() => {
3268
+ useEffect20(() => {
2962
3269
  if (!ctx) return;
2963
3270
  const controller = new AbortController();
2964
3271
  const init = async () => {
@@ -3007,7 +3314,7 @@ var useEntity = ({
3007
3314
  onSpatialMagnify,
3008
3315
  onSpatialMagnifyEnd
3009
3316
  });
3010
- useEffect21(() => {
3317
+ useEffect20(() => {
3011
3318
  const ent = instanceRef.current.entity;
3012
3319
  if (!ent) return;
3013
3320
  if (enableInput !== void 0) {
@@ -3018,10 +3325,10 @@ var useEntity = ({
3018
3325
  };
3019
3326
 
3020
3327
  // src/reality/hooks/useForceUpdate.tsx
3021
- import { useCallback as useCallback8, useState as useState7 } from "react";
3328
+ import { useCallback as useCallback9, useState as useState9 } from "react";
3022
3329
  var useForceUpdate2 = () => {
3023
- const [, setTick] = useState7(0);
3024
- return useCallback8(() => setTick((tick) => tick + 1), []);
3330
+ const [, setTick] = useState9(0);
3331
+ return useCallback9(() => setTick((tick) => tick + 1), []);
3025
3332
  };
3026
3333
 
3027
3334
  // src/reality/components/BaseEntity.tsx
@@ -3060,18 +3367,18 @@ var Entity = forwardRef11((props, ref) => {
3060
3367
  import { forwardRef as forwardRef13 } from "react";
3061
3368
 
3062
3369
  // src/reality/components/GeometryEntity.tsx
3063
- import { forwardRef as forwardRef12, useEffect as useEffect22, useRef as useRef12 } from "react";
3370
+ import { forwardRef as forwardRef12, useEffect as useEffect21, useRef as useRef13 } from "react";
3064
3371
  import { jsx as jsx14 } from "react/jsx-runtime";
3065
3372
  var GeometryEntity = forwardRef12(
3066
3373
  ({ id, children, name, materials, geometryOptions, createGeometry, ...rest }, ref) => {
3067
3374
  const ctx = useRealityContext();
3068
- const entityRef = useRef12(null);
3069
- const componentRef = useRef12(null);
3070
- const mutableRef = useRef12({
3375
+ const entityRef = useRef13(null);
3376
+ const componentRef = useRef13(null);
3377
+ const mutableRef = useRef13({
3071
3378
  lastSnapshot: null,
3072
3379
  rebuildGen: 0
3073
3380
  });
3074
- useEffect22(() => {
3381
+ useEffect21(() => {
3075
3382
  const { lastSnapshot } = mutableRef.current;
3076
3383
  if (!ctx || !entityRef.current || lastSnapshot === null) return;
3077
3384
  const geometryChanged = !shallowEqualObject(
@@ -3193,22 +3500,50 @@ var BoxEntity = forwardRef13(
3193
3500
  );
3194
3501
 
3195
3502
  // src/reality/components/UnlitMaterial.tsx
3196
- import { useEffect as useEffect23, useRef as useRef13 } from "react";
3503
+ import { useEffect as useEffect22, useRef as useRef14, useState as useState10 } from "react";
3197
3504
  var UnlitMaterial = ({
3198
3505
  children,
3199
3506
  ...options
3200
3507
  }) => {
3201
3508
  const ctx = useRealityContext();
3202
- const materialRef = useRef13();
3203
- const isInitializedRef = useRef13(false);
3204
- useEffect23(() => {
3509
+ const materialRef = useRef14(void 0);
3510
+ const isInitializedRef = useRef14(false);
3511
+ const [textureRevision, setTextureRevision] = useState10(0);
3512
+ useEffect22(() => {
3513
+ if (!ctx || !options.textureId) return;
3514
+ return ctx.resourceRegistry.subscribe(options.textureId, () => {
3515
+ setTextureRevision((v) => v + 1);
3516
+ });
3517
+ }, [ctx, options.textureId]);
3518
+ useEffect22(() => {
3205
3519
  if (!ctx) return;
3206
- const { session, reality, resourceRegistry } = ctx;
3520
+ let cancelled = false;
3521
+ const materialId = options.id;
3522
+ const { session, resourceRegistry } = ctx;
3207
3523
  const init = async () => {
3208
- const materialPromise = session.createUnlitMaterial(options);
3209
- resourceRegistry.add(options.id, materialPromise);
3210
3524
  try {
3525
+ let textureIdForNative = options.textureId;
3526
+ if (options.textureId) {
3527
+ const texturePromise = resourceRegistry.get(options.textureId);
3528
+ try {
3529
+ const textureResource = await texturePromise;
3530
+ if (cancelled) return;
3531
+ textureIdForNative = textureResource.id;
3532
+ } catch {
3533
+ textureIdForNative = "";
3534
+ }
3535
+ }
3536
+ if (cancelled) return;
3537
+ const commandOptions = {
3538
+ color: options.color,
3539
+ textureId: textureIdForNative,
3540
+ transparent: options.transparent,
3541
+ opacity: options.opacity
3542
+ };
3543
+ const materialPromise = session.createUnlitMaterial(commandOptions);
3544
+ resourceRegistry.add(materialId, materialPromise);
3211
3545
  const mat = await materialPromise;
3546
+ if (cancelled) return;
3212
3547
  materialRef.current = mat;
3213
3548
  isInitializedRef.current = true;
3214
3549
  } catch (error) {
@@ -3217,22 +3552,110 @@ var UnlitMaterial = ({
3217
3552
  };
3218
3553
  init();
3219
3554
  return () => {
3220
- resourceRegistry.removeAndDestroy(options.id);
3555
+ cancelled = true;
3556
+ resourceRegistry.removeAndDestroy(materialId);
3221
3557
  materialRef.current = void 0;
3222
3558
  isInitializedRef.current = false;
3223
3559
  };
3224
- }, [ctx]);
3560
+ }, [ctx, options.id]);
3561
+ useEffect22(() => {
3562
+ if (!ctx || !isInitializedRef.current || !materialRef.current) return;
3563
+ let cancelled = false;
3564
+ void (async () => {
3565
+ const updates = {};
3566
+ if (options.color !== void 0) updates.color = options.color;
3567
+ if (options.transparent !== void 0)
3568
+ updates.transparent = options.transparent;
3569
+ if (options.opacity !== void 0) updates.opacity = options.opacity;
3570
+ if (options.textureId !== void 0) {
3571
+ if (options.textureId === "") {
3572
+ updates.textureId = "";
3573
+ } else {
3574
+ const texturePromise = ctx.resourceRegistry.get(options.textureId);
3575
+ try {
3576
+ const textureResource = await texturePromise;
3577
+ if (cancelled) return;
3578
+ updates.textureId = textureResource.id;
3579
+ } catch {
3580
+ updates.textureId = "";
3581
+ }
3582
+ }
3583
+ }
3584
+ if (cancelled || Object.keys(updates).length === 0) return;
3585
+ const mat = materialRef.current;
3586
+ if (mat) {
3587
+ void mat.updateProperties(updates).catch(() => {
3588
+ });
3589
+ }
3590
+ })();
3591
+ return () => {
3592
+ cancelled = true;
3593
+ };
3594
+ }, [
3595
+ ctx,
3596
+ options.color,
3597
+ options.textureId,
3598
+ options.transparent,
3599
+ options.opacity,
3600
+ textureRevision
3601
+ ]);
3602
+ return null;
3603
+ };
3604
+
3605
+ // src/reality/components/Texture.tsx
3606
+ import { useEffect as useEffect23, useRef as useRef15 } from "react";
3607
+ var Texture = ({
3608
+ children,
3609
+ id,
3610
+ url,
3611
+ onLoad,
3612
+ onError
3613
+ }) => {
3614
+ const ctx = useRealityContext();
3615
+ const textureRef = useRef15(null);
3616
+ const urlRevisionRef = useRef15(0);
3617
+ useEffect23(() => {
3618
+ if (!ctx) return;
3619
+ const { resourceRegistry } = ctx;
3620
+ const capturedId = id;
3621
+ return () => {
3622
+ textureRef.current = null;
3623
+ resourceRegistry.removeAndDestroy(capturedId);
3624
+ };
3625
+ }, [ctx, id]);
3225
3626
  useEffect23(() => {
3226
- if (!isInitializedRef.current || !materialRef.current) return;
3227
- const updates = {};
3228
- if (options.color !== void 0) updates.color = options.color;
3229
- if (options.transparent !== void 0)
3230
- updates.transparent = options.transparent;
3231
- if (options.opacity !== void 0) updates.opacity = options.opacity;
3232
- if (Object.keys(updates).length > 0) {
3233
- materialRef.current.updateProperties(updates);
3234
- }
3235
- }, [options.color, options.transparent, options.opacity]);
3627
+ if (!ctx) return;
3628
+ const { session, resourceRegistry } = ctx;
3629
+ const revision = ++urlRevisionRef.current;
3630
+ let cancelled = false;
3631
+ void (async () => {
3632
+ const resolvedUrl = getAbsoluteUrl(url);
3633
+ try {
3634
+ if (textureRef.current) {
3635
+ await textureRef.current.updateProperties({ url: resolvedUrl });
3636
+ if (cancelled || revision !== urlRevisionRef.current) return;
3637
+ resourceRegistry.notify(id);
3638
+ onLoad?.();
3639
+ return;
3640
+ }
3641
+ const texturePromise = session.createTexture({ url: resolvedUrl });
3642
+ resourceRegistry.add(id, texturePromise);
3643
+ const tex = await texturePromise;
3644
+ if (cancelled || revision !== urlRevisionRef.current) {
3645
+ tex.destroy();
3646
+ return;
3647
+ }
3648
+ textureRef.current = tex;
3649
+ onLoad?.();
3650
+ } catch (error) {
3651
+ if (cancelled || revision !== urlRevisionRef.current) return;
3652
+ onError?.(error);
3653
+ }
3654
+ })();
3655
+ return () => {
3656
+ cancelled = true;
3657
+ };
3658
+ }, [ctx, id, url]);
3236
3659
  return null;
3237
3660
  };
3238
3661
 
@@ -3331,23 +3754,17 @@ var SceneGraph = ({ children }) => {
3331
3754
  };
3332
3755
 
3333
3756
  // src/reality/components/ModelAsset.tsx
3334
- import { useEffect as useEffect24, useRef as useRef14 } from "react";
3335
- var resolveAssetUrl = (url) => {
3336
- if (url.startsWith("http://") || url.startsWith("https://")) {
3337
- return url;
3338
- }
3339
- return new URL(url, window.location.href).href;
3340
- };
3757
+ import { useEffect as useEffect24, useRef as useRef16 } from "react";
3341
3758
  var ModelAsset = ({ children, ...options }) => {
3342
3759
  const ctx = useRealityContext();
3343
- const materialRef = useRef14();
3760
+ const materialRef = useRef16(void 0);
3344
3761
  useEffect24(() => {
3345
3762
  const controller = new AbortController();
3346
3763
  if (!ctx) return;
3347
- const { session, reality, resourceRegistry } = ctx;
3764
+ const { session, resourceRegistry } = ctx;
3348
3765
  const init = async () => {
3349
3766
  try {
3350
- const resolvedUrl = resolveAssetUrl(options.src);
3767
+ const resolvedUrl = getAbsoluteUrl(options.src);
3351
3768
  const modelAssetPromise = session.createModelAsset({ url: resolvedUrl });
3352
3769
  resourceRegistry.add(options.id, modelAssetPromise);
3353
3770
  const mat = await modelAssetPromise;
@@ -3371,13 +3788,13 @@ var ModelAsset = ({ children, ...options }) => {
3371
3788
  };
3372
3789
 
3373
3790
  // src/reality/components/ModelEntity.tsx
3374
- import { forwardRef as forwardRef18, useEffect as useEffect25, useRef as useRef15 } from "react";
3791
+ import { forwardRef as forwardRef18, useEffect as useEffect25, useRef as useRef17 } from "react";
3375
3792
  import { jsx as jsx21 } from "react/jsx-runtime";
3376
3793
  var ModelEntity = forwardRef18(
3377
3794
  ({ id, model, children, name, materials, ...rest }, ref) => {
3378
3795
  const ctx = useRealityContext();
3379
- const entityRef = useRef15(null);
3380
- const lastMaterialsRef = useRef15(void 0);
3796
+ const entityRef = useRef17(null);
3797
+ const lastMaterialsRef = useRef17(void 0);
3381
3798
  useEffect25(() => {
3382
3799
  if (!ctx || !entityRef.current) return;
3383
3800
  const next = materials ?? [];
@@ -3386,9 +3803,7 @@ var ModelEntity = forwardRef18(
3386
3803
  lastMaterialsRef.current = next;
3387
3804
  const apply = async () => {
3388
3805
  try {
3389
- const materialList = (await Promise.all(
3390
- next.map((mid) => ctx.resourceRegistry.get(mid))
3391
- )).filter(Boolean);
3806
+ const materialList = (await Promise.all(next.map((mid) => ctx.resourceRegistry.get(mid)))).filter(Boolean);
3392
3807
  if (entityRef.current) {
3393
3808
  await entityRef.current.setMaterials(materialList);
3394
3809
  }
@@ -3421,9 +3836,7 @@ var ModelEntity = forwardRef18(
3421
3836
  entityRef.current = ent;
3422
3837
  if (materials && materials.length > 0) {
3423
3838
  const materialList = (await Promise.all(
3424
- materials.map(
3425
- (mid) => ctx2.resourceRegistry.get(mid)
3426
- )
3839
+ materials.map((mid) => ctx2.resourceRegistry.get(mid))
3427
3840
  )).filter(Boolean);
3428
3841
  if (materialList.length > 0 && !signal.aborted) {
3429
3842
  await ent.setMaterials(materialList);
@@ -3444,10 +3857,10 @@ var ModelEntity = forwardRef18(
3444
3857
  // src/reality/components/Reality.tsx
3445
3858
  import {
3446
3859
  forwardRef as forwardRef19,
3447
- useCallback as useCallback9,
3860
+ useCallback as useCallback10,
3448
3861
  useEffect as useEffect26,
3449
- useRef as useRef16,
3450
- useState as useState8
3862
+ useRef as useRef18,
3863
+ useState as useState11
3451
3864
  } from "react";
3452
3865
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
3453
3866
  var Reality = forwardRef19(
@@ -3470,10 +3883,10 @@ var Reality = forwardRef19(
3470
3883
  onSpatialMagnifyEnd,
3471
3884
  ...props
3472
3885
  } = inProps;
3473
- const ctxRef = useRef16(null);
3474
- const creationId = useRef16(0);
3475
- const [isReady, setIsReady] = useState8(false);
3476
- const cleanupReality = useCallback9(() => {
3886
+ const ctxRef = useRef18(null);
3887
+ const creationId = useRef18(0);
3888
+ const [isReady, setIsReady] = useState11(false);
3889
+ const cleanupReality = useCallback10(() => {
3477
3890
  ctxRef.current?.attachmentRegistry.destroy();
3478
3891
  ctxRef.current?.resourceRegistry.destroy();
3479
3892
  ctxRef.current?.reality.destroy();
@@ -3486,7 +3899,7 @@ var Reality = forwardRef19(
3486
3899
  cleanupReality();
3487
3900
  };
3488
3901
  }, [cleanupReality]);
3489
- const createReality = useCallback9(async () => {
3902
+ const createReality = useCallback10(async () => {
3490
3903
  const id = ++creationId.current;
3491
3904
  const resourceRegistry = new ResourceRegistry();
3492
3905
  const attachmentRegistry = new AttachmentRegistry();
@@ -3529,7 +3942,7 @@ var Reality = forwardRef19(
3529
3942
  return null;
3530
3943
  }
3531
3944
  }, [cleanupReality]);
3532
- const content = useCallback9(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
3945
+ const content = useCallback10(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
3533
3946
  useRealityEvents({
3534
3947
  instance: ctxRef.current?.reality ?? null,
3535
3948
  onSpatialTap,
@@ -3558,7 +3971,7 @@ var Reality = forwardRef19(
3558
3971
  );
3559
3972
 
3560
3973
  // src/reality/components/AttachmentAsset.tsx
3561
- import { useEffect as useEffect27, useState as useState9 } from "react";
3974
+ import { useEffect as useEffect27, useState as useState12 } from "react";
3562
3975
  import { createPortal as createPortal3 } from "react-dom";
3563
3976
  import { jsx as jsx23 } from "react/jsx-runtime";
3564
3977
  var AttachmentAsset = ({
@@ -3566,7 +3979,7 @@ var AttachmentAsset = ({
3566
3979
  children
3567
3980
  }) => {
3568
3981
  const ctx = useRealityContext();
3569
- const [containers, setContainers] = useState9([]);
3982
+ const [containers, setContainers] = useState12([]);
3570
3983
  useEffect27(() => {
3571
3984
  if (!ctx) return;
3572
3985
  return ctx.attachmentRegistry.onContainersChange(name, setContainers);
@@ -3578,7 +3991,7 @@ var AttachmentAsset = ({
3578
3991
  };
3579
3992
 
3580
3993
  // src/reality/components/AttachmentEntity.tsx
3581
- import { useEffect as useEffect28, useRef as useRef17, useState as useState10 } from "react";
3994
+ import { useEffect as useEffect28, useRef as useRef19, useState as useState13 } from "react";
3582
3995
  var instanceCounter = 0;
3583
3996
  var AttachmentEntity = ({
3584
3997
  attachment: attachmentName,
@@ -3587,11 +4000,11 @@ var AttachmentEntity = ({
3587
4000
  }) => {
3588
4001
  const ctx = useRealityContext();
3589
4002
  const parent = useParentContext();
3590
- const attachmentRef = useRef17(null);
3591
- const parentIdRef = useRef17(null);
3592
- const instanceIdRef = useRef17(`att_${++instanceCounter}`);
3593
- const attachmentNameRef = useRef17(attachmentName);
3594
- const [childWindow, setChildWindow] = useState10(null);
4003
+ const attachmentRef = useRef19(null);
4004
+ const parentIdRef = useRef19(null);
4005
+ const instanceIdRef = useRef19(`att_${++instanceCounter}`);
4006
+ const attachmentNameRef = useRef19(attachmentName);
4007
+ const [childWindow, setChildWindow] = useState13(null);
3595
4008
  useEffect28(() => {
3596
4009
  if (!ctx || !parent) return;
3597
4010
  if (attachmentRef.current) return;
@@ -3670,7 +4083,7 @@ var AttachmentEntity = ({
3670
4083
  attachmentNameRef.current = attachmentName;
3671
4084
  }
3672
4085
  }, [ctx, attachmentName]);
3673
- useSyncHeadStyles(childWindow, { subtree: false });
4086
+ useSyncHeadStyles(childWindow);
3674
4087
  useEffect28(() => {
3675
4088
  if (!attachmentRef.current) return;
3676
4089
  attachmentRef.current.update({ position, size });
@@ -3734,7 +4147,7 @@ var styleFlag = "enableXr";
3734
4147
  var classFlag = "__enableXr__";
3735
4148
  var xrMonitorFlag = "enable-xr-monitor";
3736
4149
  function replaceToSpatialPrimitiveType(type, props) {
3737
- if (type === Model) {
4150
+ if (type === Model || type === Reality) {
3738
4151
  return type;
3739
4152
  }
3740
4153
  const propsObject = props;
@@ -3777,7 +4190,7 @@ function resolveSpatialObjectId(target) {
3777
4190
  if (maybeEntity && typeof maybeEntity === "object" && "entity" in maybeEntity) {
3778
4191
  return maybeEntity.entity?.id ?? null;
3779
4192
  }
3780
- const dom = target?.__raw ?? target;
4193
+ const dom = target;
3781
4194
  if (dom && typeof dom === "object") {
3782
4195
  const spatializedElement = dom.__spatializedElement ?? dom.__innerSpatializedElement?.();
3783
4196
  if (spatializedElement && spatializedElement.id) {
@@ -3807,7 +4220,7 @@ async function convertCoordinate(position, { from, to }) {
3807
4220
  }
3808
4221
 
3809
4222
  // src/index.ts
3810
- var version = "1.6.1";
4223
+ var version = "1.7.0";
3811
4224
  if (typeof window !== "undefined") {
3812
4225
  initPolyfill();
3813
4226
  }
@@ -3836,12 +4249,16 @@ export {
3836
4249
  SpatializedStatic3DElementContainer,
3837
4250
  SphereEntity as Sphere,
3838
4251
  SphereEntity,
4252
+ Texture,
3839
4253
  UnlitMaterial,
4254
+ WebSpatialRuntime,
4255
+ WebSpatialRuntimeError,
3840
4256
  SceneGraph as World,
3841
4257
  convertCoordinate,
3842
4258
  createElement,
3843
4259
  enableDebugTool,
3844
4260
  eventMap,
4261
+ getAbsoluteUrl,
3845
4262
  initPolyfill,
3846
4263
  initScene,
3847
4264
  useMetrics,