@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.
package/dist/web/index.js CHANGED
@@ -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'] = "web"
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,11 +455,29 @@ 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
 
916
464
  // src/noRuntime.ts
465
+ function supports(name, _tokens) {
466
+ if (name === "useMetrics" || name === "convertCoordinate") {
467
+ return true;
468
+ }
469
+ return false;
470
+ }
471
+ var WebSpatialRuntimeError = class extends Error {
472
+ capability;
473
+ constructor(capability, message) {
474
+ super(
475
+ message ?? `Capability "${capability}" is not supported in this WebSpatial runtime`
476
+ );
477
+ this.name = "WebSpatialRuntimeError";
478
+ this.capability = capability;
479
+ }
480
+ };
917
481
  var Spatial = class {
918
482
  /**
919
483
  * Requests a session object from the browser
@@ -984,19 +548,153 @@ function getSession() {
984
548
  if (_currentSession) {
985
549
  return _currentSession;
986
550
  }
987
- _currentSession = spatial.requestSession();
988
- return _currentSession;
551
+ _currentSession = spatial.requestSession();
552
+ return _currentSession;
553
+ }
554
+
555
+ // src/spatialized-container/context/SpatialLayerContext.ts
556
+ import { createContext as createContext2 } from "react";
557
+ var SpatialLayerContext = createContext2(0);
558
+
559
+ // src/spatialized-container/PortalSpatializedContainer.tsx
560
+ import { useMemo, useContext as useContext4, useEffect as useEffect6 } from "react";
561
+
562
+ // src/spatialized-container/context/PortalInstanceContext.ts
563
+ import { createContext as createContext3 } from "react";
564
+
565
+ // src/spatialized-container/utils.ts
566
+ function getInheritedStyleProps(computedStyle) {
567
+ var propNames = [
568
+ "azimuth",
569
+ "borderCollapse",
570
+ "borderSpacing",
571
+ "captionSide",
572
+ "color",
573
+ "cursor",
574
+ "direction",
575
+ // 'elevation',
576
+ "emptyCells",
577
+ "fontFamily",
578
+ "fontSize",
579
+ "fontStyle",
580
+ "fontVariant",
581
+ "fontWeight",
582
+ "font",
583
+ "letterSpacing",
584
+ "lineHeight",
585
+ "listStyleImage",
586
+ "listStylePosition",
587
+ "listStyleType",
588
+ "listStyle",
589
+ "orphans",
590
+ // 'pitchRange',
591
+ // 'pitch',
592
+ "quotes",
593
+ // 'richness',
594
+ // 'speakHeader',
595
+ // 'speakNumeral',
596
+ // 'speakPunctuation',
597
+ // 'speak',
598
+ // 'speechRate',
599
+ // 'stress',
600
+ "textAlign",
601
+ "textIndent",
602
+ "textTransform",
603
+ "visibility",
604
+ // 'voiceFamily',
605
+ // 'volume',
606
+ "whiteSpace",
607
+ "widows",
608
+ "wordSpacing",
609
+ // background also need to be synced
610
+ "background",
611
+ // position also need to be synced
612
+ "position",
613
+ "width",
614
+ "height",
615
+ "display",
616
+ // content-visibility also need to be synced
617
+ "contentVisibility"
618
+ ];
619
+ var props = {};
620
+ for (var cssName of propNames) {
621
+ if (computedStyle[cssName]) {
622
+ props[cssName] = computedStyle[cssName];
623
+ }
624
+ }
625
+ return props;
626
+ }
627
+ function parseTransformOrigin(computedStyle) {
628
+ const transformOriginProperty = computedStyle.getPropertyValue("transform-origin");
629
+ const [x, y] = transformOriginProperty.split(" ").map(parseFloat);
630
+ const width = parseFloat(computedStyle.getPropertyValue("width"));
631
+ const height = parseFloat(computedStyle.getPropertyValue("height"));
632
+ return {
633
+ x: width > 0 ? x / width : 0.5,
634
+ y: height > 0 ? y / height : 0.5,
635
+ z: 0.5
636
+ };
637
+ }
638
+ function parseBorderRadius(borderProperty, width) {
639
+ if (borderProperty === "") {
640
+ return 0;
641
+ }
642
+ if (borderProperty.endsWith("%")) {
643
+ return width * parseFloat(borderProperty) / 100;
644
+ }
645
+ return parseFloat(borderProperty);
646
+ }
647
+ function parseCornerRadius(computedStyle) {
648
+ const width = parseFloat(computedStyle.getPropertyValue("width"));
649
+ const topLeftPropertyValue = computedStyle.getPropertyValue(
650
+ "border-top-left-radius"
651
+ );
652
+ const topRightPropertyValue = computedStyle.getPropertyValue(
653
+ "border-top-right-radius"
654
+ );
655
+ const bottomLeftPropertyValue = computedStyle.getPropertyValue(
656
+ "border-bottom-left-radius"
657
+ );
658
+ const bottomRightPropertyValue = computedStyle.getPropertyValue(
659
+ "border-bottom-right-radius"
660
+ );
661
+ const cornerRadius = {
662
+ topLeading: parseBorderRadius(topLeftPropertyValue, width),
663
+ bottomLeading: parseBorderRadius(bottomLeftPropertyValue, width),
664
+ topTrailing: parseBorderRadius(topRightPropertyValue, width),
665
+ bottomTrailing: parseBorderRadius(bottomRightPropertyValue, width)
666
+ };
667
+ return cornerRadius;
668
+ }
669
+ function extractAndRemoveCustomProperties(cssText, properties) {
670
+ if (!cssText) {
671
+ return { extractedValues: {}, filteredCssText: "" };
672
+ }
673
+ const extractedValues = {};
674
+ const rules = cssText.split(";");
675
+ const filteredRules = rules.filter((rule) => {
676
+ const [key, value] = rule.split(":").map((part) => part.trim());
677
+ if (properties.includes(key)) {
678
+ extractedValues[key] = value;
679
+ return false;
680
+ }
681
+ return true;
682
+ });
683
+ const filteredCssText = filteredRules.join(";").trim();
684
+ return { extractedValues, filteredCssText };
685
+ }
686
+ function joinToCSSText(cssKV) {
687
+ const rules = Object.entries(cssKV).map(([key, value]) => `${key}: ${value}`);
688
+ return rules.join(";");
989
689
  }
990
690
 
991
- // src/spatialized-container/context/SpatialLayerContext.ts
992
- import { createContext as createContext2 } from "react";
993
- var SpatialLayerContext = createContext2(0);
994
-
995
- // src/spatialized-container/PortalSpatializedContainer.tsx
996
- import { useMemo, useContext as useContext4, useEffect as useEffect7 } from "react";
997
-
998
- // src/spatialized-container/context/PortalInstanceContext.ts
999
- import { createContext as createContext3 } from "react";
691
+ // src/spatialized-container/types.ts
692
+ var SpatialCustomStyleVars = {
693
+ back: "--xr-back",
694
+ depth: "--xr-depth",
695
+ backgroundMaterial: "--xr-background-material",
696
+ xrZIndex: "--xr-z-index"
697
+ };
1000
698
 
1001
699
  // src/utils/debugTool.ts
1002
700
  async function inspectCurrentSpatialScene() {
@@ -1014,6 +712,18 @@ function enableDebugTool() {
1014
712
  });
1015
713
  }
1016
714
 
715
+ // src/utils/urlUtils.ts
716
+ function getAbsoluteUrl(url) {
717
+ if (typeof window === "undefined" || !window.location?.href) {
718
+ return url;
719
+ }
720
+ try {
721
+ return new URL(url, window.location.href).href;
722
+ } catch {
723
+ return url;
724
+ }
725
+ }
726
+
1017
727
  // src/spatialized-container/context/PortalInstanceContext.ts
1018
728
  var PortalInstanceObject = class {
1019
729
  spatialId;
@@ -1198,14 +908,14 @@ var PortalInstanceContext = createContext3(
1198
908
  );
1199
909
 
1200
910
  // src/spatialized-container/hooks/useSync2DFrame.ts
1201
- import { useEffect as useEffect5, useState as useState2 } from "react";
911
+ import { useEffect as useEffect4, useState as useState2 } from "react";
1202
912
  function useForceUpdate() {
1203
913
  const [, setToggle] = useState2(false);
1204
914
  return () => setToggle((toggle) => !toggle);
1205
915
  }
1206
916
  function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObject) {
1207
917
  const forceUpdate = useForceUpdate();
1208
- useEffect5(() => {
918
+ useEffect4(() => {
1209
919
  spatializedContainerObject.on2DFrameChange(spatialId, () => {
1210
920
  portalInstanceObject.notify2DFrameChange();
1211
921
  forceUpdate();
@@ -1217,11 +927,11 @@ function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObj
1217
927
  }
1218
928
 
1219
929
  // src/spatialized-container/hooks/useSpatializedElement.ts
1220
- import { useEffect as useEffect6, useRef as useRef4, useState as useState3 } from "react";
930
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState3 } from "react";
1221
931
  function useSpatializedElement(createSpatializedElement2, portalInstanceObject) {
1222
932
  const [spatializedElement, setSpatializedElement] = useState3();
1223
- const elementRef = useRef4(void 0);
1224
- useEffect6(() => {
933
+ const elementRef = useRef3(void 0);
934
+ useEffect5(() => {
1225
935
  let isDestroyed = false;
1226
936
  createSpatializedElement2().then(
1227
937
  (inSpatializedElement) => {
@@ -1317,7 +1027,7 @@ function PortalSpatializedContainer(props) {
1317
1027
  ),
1318
1028
  []
1319
1029
  );
1320
- useEffect7(() => {
1030
+ useEffect6(() => {
1321
1031
  portalInstanceObject.init();
1322
1032
  return () => {
1323
1033
  portalInstanceObject.destroy();
@@ -1332,58 +1042,416 @@ function PortalSpatializedContainer(props) {
1332
1042
  portalInstanceObject,
1333
1043
  props.component
1334
1044
  );
1335
- useEffect7(() => {
1045
+ useEffect6(() => {
1336
1046
  if (spatializedElement) {
1337
1047
  spatializedElement.onSpatialTap = onSpatialTap;
1338
1048
  }
1339
- }, [spatializedElement, onSpatialTap]);
1340
- useEffect7(() => {
1341
- if (spatializedElement) {
1342
- spatializedElement.onSpatialDrag = onSpatialDrag;
1049
+ }, [spatializedElement, onSpatialTap]);
1050
+ useEffect6(() => {
1051
+ if (spatializedElement) {
1052
+ spatializedElement.onSpatialDrag = onSpatialDrag;
1053
+ }
1054
+ }, [spatializedElement, onSpatialDrag]);
1055
+ useEffect6(() => {
1056
+ if (spatializedElement) {
1057
+ spatializedElement.onSpatialDragEnd = onSpatialDragEnd;
1058
+ }
1059
+ }, [spatializedElement, onSpatialDragEnd]);
1060
+ useEffect6(() => {
1061
+ if (spatializedElement) {
1062
+ spatializedElement.onSpatialRotate = onSpatialRotate;
1063
+ }
1064
+ }, [spatializedElement, onSpatialRotate]);
1065
+ useEffect6(() => {
1066
+ if (spatializedElement) {
1067
+ spatializedElement.onSpatialRotateEnd = onSpatialRotateEnd;
1068
+ }
1069
+ }, [spatializedElement, onSpatialRotateEnd]);
1070
+ useEffect6(() => {
1071
+ if (spatializedElement) {
1072
+ spatializedElement.onSpatialMagnify = onSpatialMagnify;
1073
+ }
1074
+ }, [spatializedElement, onSpatialMagnify]);
1075
+ useEffect6(() => {
1076
+ if (spatializedElement) {
1077
+ spatializedElement.onSpatialMagnifyEnd = onSpatialMagnifyEnd;
1078
+ }
1079
+ }, [spatializedElement, onSpatialMagnifyEnd]);
1080
+ useEffect6(() => {
1081
+ if (spatializedElement) {
1082
+ spatializedElement.onSpatialDragStart = onSpatialDragStart;
1083
+ }
1084
+ }, [spatializedElement, onSpatialDragStart]);
1085
+ const rotateConstraintKey = constrainedAxisKey(
1086
+ spatialEventOptions?.constrainedToAxis
1087
+ );
1088
+ useEffect6(() => {
1089
+ if (!spatializedElement) return;
1090
+ const axis = constrainedAxisToVec3(spatialEventOptions?.constrainedToAxis);
1091
+ void spatializedElement.updateProperties({ rotateConstrainedToAxis: axis });
1092
+ }, [spatializedElement, rotateConstraintKey]);
1093
+ return /* @__PURE__ */ jsxs(PortalInstanceContext.Provider, { value: portalInstanceObject, children: [
1094
+ spatializedElement && portalInstanceObject.dom && /* @__PURE__ */ jsx3(Content, { spatializedElement, ...restProps }),
1095
+ PlaceholderEl
1096
+ ] });
1097
+ }
1098
+
1099
+ // src/spatialized-container/hooks/useDomProxy.ts
1100
+ import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef4 } from "react";
1101
+ var SpatialContainerRefProxy = class {
1102
+ transformVisibilityTaskContainerDom = null;
1103
+ /** Raw Standard host element (styled root). Used to mirror class onto the transform probe. */
1104
+ standardRawDom = null;
1105
+ standardClassObserver = null;
1106
+ /**
1107
+ * When set, Standard's DOM className is forwarded here so TransformVisibilityTaskContainer
1108
+ * can render it from React state (avoids React clobbering imperative class updates).
1109
+ */
1110
+ mirrorClassNotify = null;
1111
+ /** Last class string applied to the probe + used to skip redundant syncs. */
1112
+ lastMirroredClassName = null;
1113
+ /** Coalesce multiple class sync triggers in the same turn (Observer + classList, etc.). */
1114
+ classSyncMicrotaskQueued = false;
1115
+ ref;
1116
+ domProxy;
1117
+ /** Last value dispatched to `ref` (undefined = none yet); avoids duplicate null/proxy writes. */
1118
+ lastOutgoingToRef = void 0;
1119
+ installedProperties = [];
1120
+ // extre ref props, used to add extra props to ref
1121
+ extraRefProps;
1122
+ constructor(ref, extraRefProps) {
1123
+ this.ref = ref;
1124
+ this.extraRefProps = extraRefProps;
1125
+ }
1126
+ setMirrorClassNotify(fn) {
1127
+ this.mirrorClassNotify = fn;
1128
+ if (fn && this.standardRawDom) {
1129
+ this.flushSyncTransformClassFromStandard(true);
1130
+ }
1131
+ }
1132
+ disconnectStandardClassObserver() {
1133
+ this.standardClassObserver?.disconnect();
1134
+ this.standardClassObserver = null;
1135
+ }
1136
+ attachStandardClassObserver() {
1137
+ this.disconnectStandardClassObserver();
1138
+ if (!this.standardRawDom) {
1139
+ return;
1140
+ }
1141
+ this.standardClassObserver = new MutationObserver(() => {
1142
+ this.ensureSpatialDefaultClass();
1143
+ this.scheduleSyncTransformClassFromStandard();
1144
+ });
1145
+ this.standardClassObserver.observe(this.standardRawDom, {
1146
+ attributes: true,
1147
+ attributeFilter: ["class"]
1148
+ });
1149
+ }
1150
+ ensureSpatialDefaultClass() {
1151
+ const dom = this.standardRawDom;
1152
+ if (!dom) return;
1153
+ if (dom.classList.contains("xr-spatial-default")) return;
1154
+ dom.classList.add("xr-spatial-default");
1155
+ }
1156
+ /**
1157
+ * Merge multiple sync requests (e.g. classList hook + MutationObserver) into one microtask.
1158
+ */
1159
+ scheduleSyncTransformClassFromStandard() {
1160
+ if (this.classSyncMicrotaskQueued) {
1161
+ return;
1162
+ }
1163
+ this.classSyncMicrotaskQueued = true;
1164
+ queueMicrotask(() => {
1165
+ this.classSyncMicrotaskQueued = false;
1166
+ this.flushSyncTransformClassFromStandard(false);
1167
+ });
1168
+ }
1169
+ /**
1170
+ * Source of truth: Standard host DOM (incl. styled-components runtime class changes).
1171
+ * @param force when true, skip same-string short-circuit (e.g. mirror notify just registered).
1172
+ */
1173
+ flushSyncTransformClassFromStandard(force) {
1174
+ if (!this.standardRawDom) {
1175
+ return;
1176
+ }
1177
+ const name = this.standardRawDom.className;
1178
+ const probe = this.transformVisibilityTaskContainerDom;
1179
+ if (!force && probe && probe.className === name && this.lastMirroredClassName === name) {
1180
+ return;
1181
+ }
1182
+ this.lastMirroredClassName = name;
1183
+ if (probe) {
1184
+ probe.className = name;
1185
+ }
1186
+ this.mirrorClassNotify?.(name);
1187
+ }
1188
+ updateStandardSpatializedContainerDom(dom) {
1189
+ if (!dom) {
1190
+ this.disconnectStandardClassObserver();
1191
+ this.clearInstalledProperties();
1192
+ this.standardRawDom = null;
1193
+ this.lastMirroredClassName = null;
1194
+ this.domProxy = void 0;
1195
+ this.updateDomProxyToRef();
1196
+ return;
1197
+ }
1198
+ if (this.standardRawDom === dom && this.domProxy) {
1199
+ this.scheduleSyncTransformClassFromStandard();
1200
+ return;
1201
+ }
1202
+ this.clearInstalledProperties();
1203
+ this.standardRawDom = dom;
1204
+ this.domProxy = dom;
1205
+ this.installSpatialRefBehavior(dom);
1206
+ const root = dom.getRootNode();
1207
+ if (root === document || root instanceof ShadowRoot) {
1208
+ ensureSpatialDefaultStyleInRoot(root);
1209
+ }
1210
+ this.attachStandardClassObserver();
1211
+ this.updateDomProxyToRef();
1212
+ this.scheduleSyncTransformClassFromStandard();
1213
+ }
1214
+ clearInstalledProperties() {
1215
+ const dom = this.standardRawDom;
1216
+ if (!dom) return;
1217
+ for (const prop of this.installedProperties) {
1218
+ delete dom[prop];
1219
+ }
1220
+ this.installedProperties = [];
1221
+ }
1222
+ defineDomProperty(dom, prop, descriptor) {
1223
+ Object.defineProperty(dom, prop, {
1224
+ configurable: true,
1225
+ ...descriptor
1226
+ });
1227
+ this.installedProperties.push(prop);
1228
+ }
1229
+ installSpatialRefBehavior(dom) {
1230
+ const self = this;
1231
+ const rawStyle = dom.style;
1232
+ const rawRemoveProperty = rawStyle.removeProperty.bind(rawStyle);
1233
+ const rawGetPropertyValue = rawStyle.getPropertyValue.bind(rawStyle);
1234
+ const rawRemoveAttribute = dom.removeAttribute.bind(dom);
1235
+ const spatialStyleProperties = ["visibility", "transform"];
1236
+ const styleProxy = new Proxy(rawStyle, {
1237
+ get(target, prop) {
1238
+ if (prop === "visibility" || prop === "transform") {
1239
+ return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
1240
+ prop
1241
+ );
1242
+ }
1243
+ const value = Reflect.get(target, prop);
1244
+ if (typeof value === "function") {
1245
+ if (prop === "setProperty" || prop === "removeProperty" || prop === "getPropertyValue") {
1246
+ return function(...args) {
1247
+ const [property] = args;
1248
+ if (spatialStyleProperties.includes(property)) {
1249
+ if (prop === "setProperty") {
1250
+ const [, kValue, priority] = args;
1251
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1252
+ property,
1253
+ kValue,
1254
+ priority
1255
+ );
1256
+ } else if (prop === "removeProperty") {
1257
+ return self.transformVisibilityTaskContainerDom?.style.removeProperty(
1258
+ property
1259
+ );
1260
+ } else if (prop === "getPropertyValue") {
1261
+ return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
1262
+ property
1263
+ );
1264
+ }
1265
+ return void 0;
1266
+ }
1267
+ return value.apply(this, args);
1268
+ }.bind(target);
1269
+ }
1270
+ return value.bind(target);
1271
+ }
1272
+ return value;
1273
+ },
1274
+ set(target, prop, value) {
1275
+ if (prop === "visibility") {
1276
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1277
+ "visibility",
1278
+ value
1279
+ );
1280
+ return true;
1281
+ }
1282
+ if (prop === "transform") {
1283
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1284
+ "transform",
1285
+ value
1286
+ );
1287
+ return true;
1288
+ }
1289
+ if (Object.values(SpatialCustomStyleVars).includes(prop)) {
1290
+ target.setProperty(prop, value);
1291
+ return true;
1292
+ }
1293
+ if (prop === "cssText") {
1294
+ const { extractedValues, filteredCssText } = extractAndRemoveCustomProperties(
1295
+ value,
1296
+ spatialStyleProperties
1297
+ );
1298
+ spatialStyleProperties.forEach((key) => {
1299
+ if (extractedValues[key]) {
1300
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
1301
+ key,
1302
+ extractedValues[key]
1303
+ );
1304
+ } else {
1305
+ rawRemoveProperty(key);
1306
+ }
1307
+ });
1308
+ const appendedCSSText = joinToCSSText({
1309
+ transform: "none",
1310
+ visibility: "hidden"
1311
+ });
1312
+ return Reflect.set(
1313
+ target,
1314
+ prop,
1315
+ [appendedCSSText, filteredCssText].join(";")
1316
+ );
1317
+ }
1318
+ return Reflect.set(target, prop, value);
1319
+ }
1320
+ });
1321
+ this.defineDomProperty(dom, "style", {
1322
+ get() {
1323
+ return styleProxy;
1324
+ },
1325
+ set(value) {
1326
+ styleProxy.cssText = String(value);
1327
+ }
1328
+ });
1329
+ this.defineDomProperty(dom, "className", {
1330
+ get() {
1331
+ return dom.getAttribute("class") ?? "";
1332
+ },
1333
+ set(value) {
1334
+ dom.setAttribute("class", String(value));
1335
+ if (!dom.classList.contains("xr-spatial-default")) {
1336
+ dom.classList.add("xr-spatial-default");
1337
+ }
1338
+ self.scheduleSyncTransformClassFromStandard();
1339
+ }
1340
+ });
1341
+ this.defineDomProperty(dom, "removeAttribute", {
1342
+ value(property) {
1343
+ if (property === "style") {
1344
+ rawStyle.cssText = "visibility: hidden; transition: none; transform: none;";
1345
+ self.transformVisibilityTaskContainerDom?.style.removeProperty(
1346
+ "visibility"
1347
+ );
1348
+ self.transformVisibilityTaskContainerDom?.style.removeProperty(
1349
+ "transform"
1350
+ );
1351
+ return;
1352
+ }
1353
+ if (property === "class") {
1354
+ dom.className = "xr-spatial-default";
1355
+ return;
1356
+ }
1357
+ return rawRemoveAttribute(property);
1358
+ }
1359
+ });
1360
+ if (supports("xrClientDepth")) {
1361
+ this.defineDomProperty(dom, "xrClientDepth", {
1362
+ get() {
1363
+ return rawGetPropertyValue(SpatialCustomStyleVars.depth);
1364
+ }
1365
+ });
1366
+ }
1367
+ if (supports("xrOffsetBack")) {
1368
+ this.defineDomProperty(dom, "xrOffsetBack", {
1369
+ get() {
1370
+ return rawGetPropertyValue(SpatialCustomStyleVars.back);
1371
+ }
1372
+ });
1343
1373
  }
1344
- }, [spatializedElement, onSpatialDrag]);
1345
- useEffect7(() => {
1346
- if (spatializedElement) {
1347
- spatializedElement.onSpatialDragEnd = onSpatialDragEnd;
1374
+ if (this.extraRefProps) {
1375
+ const extraProps = this.extraRefProps(dom);
1376
+ for (const prop of Object.keys(extraProps)) {
1377
+ this.defineDomProperty(dom, prop, {
1378
+ get() {
1379
+ return extraProps[prop];
1380
+ },
1381
+ set(value) {
1382
+ extraProps[prop] = value;
1383
+ }
1384
+ });
1385
+ }
1348
1386
  }
1349
- }, [spatializedElement, onSpatialDragEnd]);
1350
- useEffect7(() => {
1351
- if (spatializedElement) {
1352
- spatializedElement.onSpatialRotate = onSpatialRotate;
1387
+ }
1388
+ updateTransformVisibilityTaskContainerDom(dom) {
1389
+ this.transformVisibilityTaskContainerDom = dom;
1390
+ if (!dom) {
1391
+ this.lastMirroredClassName = null;
1353
1392
  }
1354
- }, [spatializedElement, onSpatialRotate]);
1355
- useEffect7(() => {
1356
- if (spatializedElement) {
1357
- spatializedElement.onSpatialRotateEnd = onSpatialRotateEnd;
1393
+ this.scheduleSyncTransformClassFromStandard();
1394
+ this.updateDomProxyToRef();
1395
+ }
1396
+ updateDomProxyToRef() {
1397
+ const ref = this.ref;
1398
+ if (!ref) {
1399
+ return;
1358
1400
  }
1359
- }, [spatializedElement, onSpatialRotateEnd]);
1360
- useEffect7(() => {
1361
- if (spatializedElement) {
1362
- spatializedElement.onSpatialMagnify = onSpatialMagnify;
1401
+ const next = this.domProxy && this.transformVisibilityTaskContainerDom ? this.domProxy : null;
1402
+ if (this.lastOutgoingToRef === next) {
1403
+ return;
1363
1404
  }
1364
- }, [spatializedElement, onSpatialMagnify]);
1365
- useEffect7(() => {
1366
- if (spatializedElement) {
1367
- spatializedElement.onSpatialMagnifyEnd = onSpatialMagnifyEnd;
1405
+ this.lastOutgoingToRef = next;
1406
+ if (next) {
1407
+ if (typeof ref === "function") {
1408
+ ref(next);
1409
+ } else {
1410
+ ref.current = next;
1411
+ }
1412
+ } else {
1413
+ if (typeof ref === "function") {
1414
+ ref(null);
1415
+ } else {
1416
+ ref.current = null;
1417
+ }
1368
1418
  }
1369
- }, [spatializedElement, onSpatialMagnifyEnd]);
1370
- useEffect7(() => {
1371
- if (spatializedElement) {
1372
- spatializedElement.onSpatialDragStart = onSpatialDragStart;
1419
+ }
1420
+ updateRef(ref) {
1421
+ if (this.ref === ref) {
1422
+ return;
1373
1423
  }
1374
- }, [spatializedElement, onSpatialDragStart]);
1375
- const rotateConstraintKey = constrainedAxisKey(
1376
- spatialEventOptions?.constrainedToAxis
1424
+ this.ref = ref;
1425
+ this.lastOutgoingToRef = void 0;
1426
+ this.updateDomProxyToRef();
1427
+ }
1428
+ };
1429
+ function useDomProxy(ref, extraRefProps) {
1430
+ const spatialContainerRefProxy = useRef4(
1431
+ new SpatialContainerRefProxy(ref, extraRefProps)
1377
1432
  );
1378
1433
  useEffect7(() => {
1379
- if (!spatializedElement) return;
1380
- const axis = constrainedAxisToVec3(spatialEventOptions?.constrainedToAxis);
1381
- void spatializedElement.updateProperties({ rotateConstrainedToAxis: axis });
1382
- }, [spatializedElement, rotateConstraintKey]);
1383
- return /* @__PURE__ */ jsxs(PortalInstanceContext.Provider, { value: portalInstanceObject, children: [
1384
- spatializedElement && portalInstanceObject.dom && /* @__PURE__ */ jsx3(Content, { spatializedElement, ...restProps }),
1385
- PlaceholderEl
1386
- ] });
1434
+ spatialContainerRefProxy.current.updateRef(ref);
1435
+ }, [ref]);
1436
+ const transformVisibilityTaskContainerCallback = useCallback5(
1437
+ (el) => {
1438
+ spatialContainerRefProxy.current.updateTransformVisibilityTaskContainerDom(
1439
+ el
1440
+ );
1441
+ },
1442
+ []
1443
+ );
1444
+ const standardSpatializedContainerCallback = useCallback5(
1445
+ (el) => {
1446
+ spatialContainerRefProxy.current.updateStandardSpatializedContainerDom(el);
1447
+ },
1448
+ []
1449
+ );
1450
+ return {
1451
+ transformVisibilityTaskContainerCallback,
1452
+ standardSpatializedContainerCallback,
1453
+ spatialContainerRefProxy
1454
+ };
1387
1455
  }
1388
1456
 
1389
1457
  // src/reality/context/InsideAttachmentContext.tsx
@@ -1625,6 +1693,7 @@ function withSSRSupported(Component) {
1625
1693
  import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1626
1694
  function DegradedContainer({
1627
1695
  innerRef,
1696
+ enableOnSpatialContentReadyFallback,
1628
1697
  ...inprops
1629
1698
  }) {
1630
1699
  const {
@@ -1645,9 +1714,48 @@ function DegradedContainer({
1645
1714
  getExtraSpatializedElementProperties: _getExtra,
1646
1715
  extraRefProps: _extraRef,
1647
1716
  sizingMode: _sizingMode,
1717
+ onSpatialContentReady: _onSpatialContentReady,
1648
1718
  ...restProps
1649
1719
  } = inprops;
1650
- return /* @__PURE__ */ jsx6(Component, { ref: innerRef, ...restProps, children });
1720
+ const [hostEl, setHostEl] = useState6(null);
1721
+ const callbackRef = useRef5(_onSpatialContentReady);
1722
+ callbackRef.current = _onSpatialContentReady;
1723
+ useLayoutEffect2(() => {
1724
+ if (!enableOnSpatialContentReadyFallback || !hostEl || !hostEl.isConnected || !callbackRef.current) {
1725
+ return () => {
1726
+ };
1727
+ }
1728
+ let cleanup;
1729
+ try {
1730
+ cleanup = callbackRef.current({ host: hostEl });
1731
+ } catch (e) {
1732
+ if (process.env.NODE_ENV !== "production") {
1733
+ console.error("[WebSpatial] onSpatialContentReady threw", e);
1734
+ }
1735
+ }
1736
+ return () => {
1737
+ if (typeof cleanup !== "function") return;
1738
+ try {
1739
+ cleanup();
1740
+ } catch (e) {
1741
+ if (process.env.NODE_ENV !== "production") {
1742
+ console.error("[WebSpatial] onSpatialContentReady cleanup threw", e);
1743
+ }
1744
+ }
1745
+ };
1746
+ }, [enableOnSpatialContentReadyFallback, hostEl]);
1747
+ const setHostRef = useCallback6(
1748
+ (node) => {
1749
+ if (typeof innerRef === "function") {
1750
+ innerRef(node);
1751
+ } else if (innerRef != null) {
1752
+ innerRef.current = node;
1753
+ }
1754
+ setHostEl(node);
1755
+ },
1756
+ [innerRef]
1757
+ );
1758
+ return /* @__PURE__ */ jsx6(Component, { ref: setHostRef, ...restProps, children });
1651
1759
  }
1652
1760
  function SpatializedContainerBase(inprops, ref) {
1653
1761
  const isWebSpatialEnv = getSession() !== null;
@@ -1658,7 +1766,14 @@ function SpatializedContainerBase(inprops, ref) {
1658
1766
  `[WebSpatial] ${inprops.component || "Spatial element"} cannot be used inside AttachmentAsset. Rendering as plain HTML.`
1659
1767
  );
1660
1768
  }
1661
- return /* @__PURE__ */ jsx6(DegradedContainer, { ...inprops, innerRef: ref });
1769
+ return /* @__PURE__ */ jsx6(
1770
+ DegradedContainer,
1771
+ {
1772
+ ...inprops,
1773
+ innerRef: ref,
1774
+ enableOnSpatialContentReadyFallback: !isWebSpatialEnv && !insideAttachment
1775
+ }
1776
+ );
1662
1777
  }
1663
1778
  const layer = useContext7(SpatialLayerContext) + 1;
1664
1779
  const rootSpatializedContainerObject = useContext7(
@@ -1741,6 +1856,7 @@ function SpatializedContainerBase(inprops, ref) {
1741
1856
  createSpatializedElement: createSpatializedElement2,
1742
1857
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1743
1858
  spatialEventOptions: _nestedSpatialEventOptions,
1859
+ onSpatialContentReady: _nestedOnSpatialContentReady,
1744
1860
  ...restProps
1745
1861
  } = props;
1746
1862
  return /* @__PURE__ */ jsxs2(SpatialLayerContext.Provider, { value: layer, children: [
@@ -1804,6 +1920,7 @@ function SpatializedContainerBase(inprops, ref) {
1804
1920
  createSpatializedElement: createSpatializedElement2,
1805
1921
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1806
1922
  spatialEventOptions: _rootSpatialEventOptions,
1923
+ onSpatialContentReady: _rootOnSpatialContentReady,
1807
1924
  ...restProps
1808
1925
  } = props;
1809
1926
  return /* @__PURE__ */ jsx6(SpatialLayerContext.Provider, { value: layer, children: /* @__PURE__ */ jsxs2(
@@ -1850,8 +1967,10 @@ var SpatializedContainer = withSSRSupported(
1850
1967
  import { createPortal as createPortal2 } from "react-dom";
1851
1968
  import {
1852
1969
  forwardRef as forwardRef5,
1970
+ useCallback as useCallback7,
1853
1971
  useContext as useContext8,
1854
- useEffect as useEffect12
1972
+ useEffect as useEffect12,
1973
+ useState as useState7
1855
1974
  } from "react";
1856
1975
 
1857
1976
  // src/utils/windowStyleSync.ts
@@ -1964,55 +2083,145 @@ async function syncParentHeadToChild(childWindow) {
1964
2083
 
1965
2084
  // src/utils/useSyncHeadStyles.ts
1966
2085
  import { useEffect as useEffect11 } from "react";
1967
- function defaultShouldSync(mutations) {
1968
- if (!Array.isArray(mutations) || mutations.length === 0) return false;
2086
+ function getSyncTiming(mutations) {
2087
+ if (!Array.isArray(mutations) || mutations.length === 0) return null;
2088
+ let hasDelayed = false;
1969
2089
  for (const mutation of mutations) {
2090
+ if (mutation.type === "characterData") {
2091
+ const parent = mutation.target.parentElement;
2092
+ if (parent?.tagName === "STYLE") return "immediate";
2093
+ }
1970
2094
  const nodes = [
2095
+ mutation.target,
1971
2096
  ...Array.from(mutation.addedNodes),
1972
2097
  ...Array.from(mutation.removedNodes)
1973
2098
  ];
1974
2099
  for (const node of nodes) {
1975
2100
  if (!(node instanceof Element)) continue;
1976
2101
  const tag = node.tagName;
1977
- if (tag === "STYLE") return true;
2102
+ if (tag === "STYLE") return "immediate";
1978
2103
  if (tag === "LINK") {
1979
2104
  const { rel } = node;
1980
- if (rel && rel.toLowerCase() === "stylesheet") return true;
2105
+ if (rel && rel.toLowerCase() === "stylesheet") hasDelayed = true;
1981
2106
  }
1982
2107
  }
1983
2108
  }
1984
- return false;
2109
+ return hasDelayed ? "delayed" : null;
1985
2110
  }
1986
2111
  function useSyncHeadStyles(childWindow, options) {
1987
2112
  const delayMs = 100;
1988
- const subtree = options?.subtree ?? false;
2113
+ const subtree = options?.subtree ?? true;
1989
2114
  const immediate = options?.immediate ?? true;
1990
2115
  useEffect11(() => {
1991
2116
  if (!childWindow) return;
1992
2117
  let timer;
1993
- const scheduleSync = () => {
2118
+ let immediateQueued = false;
2119
+ let disposed = false;
2120
+ const scheduleSync = (timing = "delayed") => {
1994
2121
  if (timer) window.clearTimeout(timer);
2122
+ if (timing === "immediate") {
2123
+ if (immediateQueued) return;
2124
+ immediateQueued = true;
2125
+ queueMicrotask(() => {
2126
+ immediateQueued = false;
2127
+ if (disposed) return;
2128
+ syncParentHeadToChild(childWindow);
2129
+ });
2130
+ return;
2131
+ }
1995
2132
  timer = window.setTimeout(() => {
2133
+ if (disposed) return;
1996
2134
  syncParentHeadToChild(childWindow);
1997
2135
  }, delayMs);
1998
2136
  };
1999
2137
  if (immediate) scheduleSync();
2000
2138
  const observer = new MutationObserver((mutations) => {
2001
- if (!defaultShouldSync(mutations)) return;
2002
- scheduleSync();
2139
+ const timing = getSyncTiming(mutations);
2140
+ if (!timing) return;
2141
+ scheduleSync(timing);
2142
+ });
2143
+ observer.observe(document.head, {
2144
+ childList: true,
2145
+ characterData: true,
2146
+ subtree
2003
2147
  });
2004
- observer.observe(document.head, { childList: true, subtree });
2005
2148
  return () => {
2149
+ disposed = true;
2006
2150
  if (timer) window.clearTimeout(timer);
2007
2151
  observer.disconnect();
2008
2152
  };
2009
2153
  }, [childWindow, delayMs, subtree, immediate]);
2010
2154
  }
2011
2155
 
2156
+ // src/spatialized-container/hooks/useSpatialContentReady.ts
2157
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef6 } from "react";
2158
+ var isDev = process.env.NODE_ENV !== "production";
2159
+ function safeInvokeCleanup(cleanup) {
2160
+ if (!cleanup) return;
2161
+ try {
2162
+ cleanup();
2163
+ } catch (e) {
2164
+ if (isDev) {
2165
+ console.error("[WebSpatial] onSpatialContentReady cleanup threw", e);
2166
+ }
2167
+ }
2168
+ }
2169
+ function useSpatialContentReady(params) {
2170
+ const {
2171
+ spatializedElement,
2172
+ portalInstanceObject,
2173
+ hostElement,
2174
+ onSpatialContentReady
2175
+ } = params;
2176
+ const callbackRef = useRef6(onSpatialContentReady);
2177
+ callbackRef.current = onSpatialContentReady;
2178
+ useLayoutEffect3(() => {
2179
+ const dom = portalInstanceObject.dom;
2180
+ const isReady = !!(spatializedElement && dom && hostElement && hostElement.isConnected);
2181
+ if (!isReady || !hostElement) {
2182
+ return () => {
2183
+ };
2184
+ }
2185
+ const cb = callbackRef.current;
2186
+ let cleanupFromCallback;
2187
+ if (cb) {
2188
+ try {
2189
+ const ret = cb({ host: hostElement });
2190
+ cleanupFromCallback = typeof ret === "function" ? ret : void 0;
2191
+ } catch (e) {
2192
+ if (isDev) {
2193
+ console.error("[WebSpatial] onSpatialContentReady threw", e);
2194
+ }
2195
+ }
2196
+ }
2197
+ return () => {
2198
+ safeInvokeCleanup(cleanupFromCallback);
2199
+ };
2200
+ }, [spatializedElement, portalInstanceObject.dom, hostElement]);
2201
+ }
2202
+
2012
2203
  // src/spatialized-container/Spatialized2DElementContainer.tsx
2013
2204
  import { jsx as jsx7 } from "react/jsx-runtime";
2014
- function getJSXPortalInstance(inProps, portalInstanceObject) {
2015
- const { component: El, style: inStyle = {}, ...props } = inProps;
2205
+ function mergeRefs(...refs) {
2206
+ return (value) => {
2207
+ for (const ref of refs) {
2208
+ if (ref == null) continue;
2209
+ if (typeof ref === "function") {
2210
+ ref(value);
2211
+ } else {
2212
+ ;
2213
+ ref.current = value;
2214
+ }
2215
+ }
2216
+ };
2217
+ }
2218
+ function getJSXPortalInstance(inProps, portalInstanceObject, hostRef) {
2219
+ const {
2220
+ component: El,
2221
+ style: inStyle = {},
2222
+ ref: userRef,
2223
+ ...props
2224
+ } = inProps;
2016
2225
  const extraStyle = {
2017
2226
  visibility: "visible",
2018
2227
  position: "relative",
@@ -2034,7 +2243,8 @@ function getJSXPortalInstance(inProps, portalInstanceObject) {
2034
2243
  ...inheritedPortalStyle,
2035
2244
  ...extraStyle
2036
2245
  };
2037
- return /* @__PURE__ */ jsx7(El, { style, ...props });
2246
+ const mergedRef = hostRef != null ? mergeRefs(hostRef, userRef) : userRef;
2247
+ return /* @__PURE__ */ jsx7(El, { ref: mergedRef, style, ...props });
2038
2248
  }
2039
2249
  function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
2040
2250
  useEffect12(() => {
@@ -2045,20 +2255,31 @@ function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
2045
2255
  }, [name]);
2046
2256
  }
2047
2257
  function SpatializedContent(props) {
2048
- const { spatializedElement, ...restProps } = props;
2258
+ const { spatializedElement, onSpatialContentReady, ...restProps } = props;
2049
2259
  const spatialized2DElement = spatializedElement;
2050
2260
  const { windowProxy } = spatialized2DElement;
2261
+ const [hostEl, setHostEl] = useState7(null);
2262
+ const portalInstanceObject = useContext8(
2263
+ PortalInstanceContext
2264
+ );
2265
+ useSpatialContentReady({
2266
+ spatializedElement,
2267
+ portalInstanceObject,
2268
+ hostElement: hostEl,
2269
+ onSpatialContentReady
2270
+ });
2051
2271
  useSyncHeadStyles(windowProxy, {
2052
2272
  subtree: false
2053
2273
  });
2054
2274
  const name = restProps["data-name"] || "";
2055
2275
  useSyncDocumentTitle(windowProxy, spatialized2DElement, name);
2056
- const portalInstanceObject = useContext8(
2057
- PortalInstanceContext
2058
- );
2276
+ const setHostCallback = useCallback7((el) => {
2277
+ setHostEl(el);
2278
+ }, []);
2059
2279
  const JSXPortalInstance = getJSXPortalInstance(
2060
2280
  restProps,
2061
- portalInstanceObject
2281
+ portalInstanceObject,
2282
+ setHostCallback
2062
2283
  );
2063
2284
  return createPortal2(JSXPortalInstance, windowProxy.document.body);
2064
2285
  }
@@ -2116,11 +2337,11 @@ import {
2116
2337
  Children,
2117
2338
  forwardRef as forwardRef6,
2118
2339
  isValidElement,
2119
- useCallback as useCallback7,
2340
+ useCallback as useCallback8,
2120
2341
  useContext as useContext9,
2121
2342
  useEffect as useEffect13,
2122
2343
  useMemo as useMemo3,
2123
- useRef as useRef5
2344
+ useRef as useRef7
2124
2345
  } from "react";
2125
2346
  import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
2126
2347
  function getAbsoluteURL(url) {
@@ -2165,24 +2386,57 @@ function collectSources(children) {
2165
2386
  return sources;
2166
2387
  }
2167
2388
  function SpatializedContent2(props) {
2168
- const { src, children, spatializedElement, onLoad, onError, autoPlay, loop } = props;
2389
+ const {
2390
+ src,
2391
+ poster,
2392
+ children,
2393
+ spatializedElement,
2394
+ onLoad,
2395
+ onError,
2396
+ autoPlay,
2397
+ loop,
2398
+ loading = "eager"
2399
+ } = props;
2169
2400
  const portalInstanceObject = useContext9(PortalInstanceContext);
2401
+ const wasVisible = useRef7(false);
2170
2402
  const modelURL = useMemo3(() => getAbsoluteURL(src), [src]);
2403
+ const posterURL = useMemo3(() => getAbsoluteURL(poster), [poster]);
2171
2404
  const sources = useMemo3(() => collectSources(children), [children]);
2405
+ const sourcesKey = useMemo3(() => JSON.stringify(sources), [sources]);
2406
+ useEffect13(() => {
2407
+ wasVisible.current = false;
2408
+ const target = portalInstanceObject.dom;
2409
+ if (loading !== "lazy" || !target) {
2410
+ wasVisible.current = true;
2411
+ return;
2412
+ }
2413
+ const observer = new IntersectionObserver((entries) => {
2414
+ if (entries.some((entry) => entry.isIntersecting)) {
2415
+ wasVisible.current = true;
2416
+ observer.disconnect();
2417
+ spatializedElement.updateProperties({ loading: "eager" });
2418
+ }
2419
+ });
2420
+ observer.observe(target);
2421
+ return () => observer.disconnect();
2422
+ }, [modelURL, sourcesKey, portalInstanceObject]);
2172
2423
  useEffect13(() => {
2424
+ if (loading !== "lazy") wasVisible.current = true;
2173
2425
  spatializedElement.updateProperties({
2174
- modelURL: modelURL ?? "",
2426
+ modelURL: modelURL ?? (spatializedElement.modelUrl ? "" : modelURL),
2175
2427
  sources,
2176
2428
  autoplay: autoPlay,
2177
- loop
2429
+ loop,
2430
+ posterURL: posterURL ?? "",
2431
+ loading: loading === "lazy" && wasVisible.current ? "eager" : loading
2178
2432
  });
2179
- }, [modelURL, JSON.stringify(sources), autoPlay, loop]);
2433
+ }, [modelURL, sourcesKey, autoPlay, loop, posterURL, loading]);
2180
2434
  useEffect13(() => {
2181
2435
  if (onLoad) {
2182
2436
  spatializedElement.onLoadCallback = () => {
2183
2437
  onLoad(
2184
2438
  createLoadSuccessEvent(
2185
- () => portalInstanceObject.dom.__targetProxy
2439
+ () => portalInstanceObject.dom
2186
2440
  )
2187
2441
  );
2188
2442
  };
@@ -2195,7 +2449,7 @@ function SpatializedContent2(props) {
2195
2449
  spatializedElement.onLoadFailureCallback = () => {
2196
2450
  onError(
2197
2451
  createLoadFailureEvent(
2198
- () => portalInstanceObject.dom.__targetProxy
2452
+ () => portalInstanceObject.dom
2199
2453
  )
2200
2454
  );
2201
2455
  };
@@ -2206,15 +2460,16 @@ function SpatializedContent2(props) {
2206
2460
  return /* @__PURE__ */ jsx8(Fragment2, {});
2207
2461
  }
2208
2462
  function SpatializedStatic3DElementContainerBase(props, ref) {
2209
- const promiseRef = useRef5(null);
2210
- const createSpatializedElement2 = useCallback7(() => {
2463
+ const promiseRef = useRef7(null);
2464
+ const createSpatializedElement2 = useCallback8(() => {
2211
2465
  promiseRef.current = getSession().createSpatializedStatic3DElement(
2212
2466
  getAbsoluteURL(props.src),
2213
- collectSources(props.children)
2467
+ collectSources(props.children),
2468
+ props.loading === "lazy" ? "lazy" : "eager"
2214
2469
  );
2215
2470
  return promiseRef.current;
2216
2471
  }, []);
2217
- const extraRefProps = useCallback7(
2472
+ const extraRefProps = useCallback8(
2218
2473
  (domProxy) => {
2219
2474
  let modelTransform = new DOMMatrixReadOnly();
2220
2475
  return {
@@ -2261,6 +2516,16 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
2261
2516
  if (spatializedElement) {
2262
2517
  spatializedElement.playbackRate = value;
2263
2518
  }
2519
+ },
2520
+ get currentTime() {
2521
+ const spatializedElement = domProxy.__spatializedElement;
2522
+ return spatializedElement?.currentTime ?? 0;
2523
+ },
2524
+ set currentTime(value) {
2525
+ const spatializedElement = domProxy.__spatializedElement;
2526
+ if (spatializedElement) {
2527
+ spatializedElement.currentTime = value;
2528
+ }
2264
2529
  }
2265
2530
  };
2266
2531
  },
@@ -2317,11 +2582,15 @@ function withSpatialized2DElementContainer(Component) {
2317
2582
 
2318
2583
  // src/spatialized-container/index.ts
2319
2584
  function initPolyfill() {
2320
- hijackGetComputedStyle();
2321
2585
  injectSpatialDefaultStyle();
2322
2586
  initCSSParserDivContainer();
2323
2587
  }
2324
2588
 
2589
+ // src/webSpatialRuntime.ts
2590
+ var WebSpatialRuntime = {
2591
+ supports
2592
+ };
2593
+
2325
2594
  // src/initScene.web.ts
2326
2595
  function initScene(name, callback) {
2327
2596
  return;
@@ -2331,9 +2600,9 @@ function initScene(name, callback) {
2331
2600
  import { forwardRef as forwardRef9 } from "react";
2332
2601
 
2333
2602
  // src/spatialized-container-monitor/useMonitorDomChange.tsx
2334
- import { useRef as useRef6, useEffect as useEffect14, useMemo as useMemo4 } from "react";
2603
+ import { useRef as useRef8, useEffect as useEffect14, useMemo as useMemo4 } from "react";
2335
2604
  function useMonitorDomChange(inRef) {
2336
- const ref = useRef6(null);
2605
+ const ref = useRef8(null);
2337
2606
  useEffect14(() => {
2338
2607
  const observer = new MutationObserver((mutationsList) => {
2339
2608
  notifyDOMUpdate(mutationsList);
@@ -2495,37 +2764,87 @@ var AttachmentRegistry = class {
2495
2764
  var AttachmentContext = createContext8(null);
2496
2765
 
2497
2766
  // src/reality/hooks/useEntityTransform.tsx
2498
- import { useEffect as useEffect16, useRef as useRef7 } from "react";
2767
+ import { useEffect as useEffect16, useRef as useRef9 } from "react";
2499
2768
 
2500
2769
  // src/reality/utils/ResourceRegistry.ts
2501
2770
  var ResourceRegistry = class {
2771
+ /** Every id maps to a promise — either the real resource from add(), or a placeholder until then. */
2502
2772
  resources = /* @__PURE__ */ new Map();
2773
+ /** If get() ran first, we stash resolve/reject so add() can complete the waiting promise. */
2774
+ deferreds = /* @__PURE__ */ new Map();
2775
+ /** Subscribers are notified whenever an id gets a fresh resolved or failed resource attempt. */
2776
+ listeners = /* @__PURE__ */ new Map();
2503
2777
  add(id, resource) {
2504
2778
  this.resources.set(id, resource);
2779
+ const deferred = this.deferreds.get(id);
2780
+ if (deferred) {
2781
+ resource.then(deferred.resolve).catch(
2782
+ (err) => deferred.reject(err instanceof Error ? err : new Error(String(err)))
2783
+ );
2784
+ this.deferreds.delete(id);
2785
+ }
2786
+ resource.then(() => this.notify(id)).catch(() => this.notify(id));
2787
+ }
2788
+ subscribe(id, listener) {
2789
+ const listeners = this.listeners.get(id) ?? /* @__PURE__ */ new Set();
2790
+ listeners.add(listener);
2791
+ this.listeners.set(id, listeners);
2792
+ return () => {
2793
+ listeners.delete(listener);
2794
+ if (listeners.size === 0) {
2795
+ this.listeners.delete(id);
2796
+ }
2797
+ };
2798
+ }
2799
+ notify(id) {
2800
+ const listeners = this.listeners.get(id);
2801
+ if (!listeners) return;
2802
+ for (const listener of Array.from(listeners)) {
2803
+ listener();
2804
+ }
2805
+ }
2806
+ get(id) {
2807
+ const existing = this.resources.get(id);
2808
+ if (existing) {
2809
+ return existing;
2810
+ }
2811
+ const promise = new Promise((resolve, reject) => {
2812
+ this.deferreds.set(id, { resolve, reject });
2813
+ });
2814
+ this.resources.set(id, promise);
2815
+ return promise;
2505
2816
  }
2506
2817
  remove(id) {
2507
2818
  this.resources.delete(id);
2819
+ this.deferreds.delete(id);
2820
+ this.notify(id);
2508
2821
  }
2509
- // Remove the resource by id and destroy it once resolved
2510
- // This does not cancel in-flight creation; it schedules destruction after resolution
2822
+ // Same as remove, but when the promise resolves, destroy the spatial object (best-effort).
2511
2823
  removeAndDestroy(id) {
2512
2824
  const p = this.resources.get(id);
2513
2825
  if (p) {
2514
- p.then((spatialObj) => spatialObj.destroy()).catch(() => {
2826
+ p.then((obj) => obj.destroy()).catch(() => {
2515
2827
  });
2516
2828
  }
2517
2829
  this.resources.delete(id);
2518
- }
2519
- get(id) {
2520
- return this.resources.get(id);
2830
+ this.deferreds.delete(id);
2831
+ this.notify(id);
2521
2832
  }
2522
2833
  destroy() {
2834
+ for (const [id, deferred] of this.deferreds) {
2835
+ deferred.reject(
2836
+ new Error(`ResourceRegistry destroyed \u2014 "${id}" never resolved`)
2837
+ );
2838
+ }
2839
+ this.deferreds.clear();
2840
+ const ids = Array.from(this.resources.keys());
2841
+ for (const id of ids) {
2842
+ this.notify(id);
2843
+ }
2844
+ this.listeners.clear();
2523
2845
  const pending = Array.from(this.resources.values());
2524
2846
  this.resources.clear();
2525
- pending.forEach(
2526
- (promise) => promise.then((spatialObj) => spatialObj.destroy()).catch(() => {
2527
- })
2528
- );
2847
+ void Promise.allSettled(pending.map((p) => p.then((obj) => obj.destroy())));
2529
2848
  }
2530
2849
  };
2531
2850
 
@@ -2557,15 +2876,14 @@ function shallowEqualArray(a, b) {
2557
2876
 
2558
2877
  // src/reality/utils/AbortResourceManager.ts
2559
2878
  var AbortResourceManager = class {
2879
+ resources = [];
2880
+ aborted = false;
2560
2881
  constructor(signal) {
2561
- this.signal = signal;
2562
2882
  signal.addEventListener("abort", () => {
2563
2883
  this.aborted = true;
2564
2884
  void this.dispose();
2565
2885
  });
2566
2886
  }
2567
- resources = [];
2568
- aborted = false;
2569
2887
  async addResource(factory) {
2570
2888
  if (this.aborted) throw new DOMException("Aborted", "AbortError");
2571
2889
  const resource = await factory();
@@ -2590,7 +2908,7 @@ var AbortResourceManager = class {
2590
2908
 
2591
2909
  // src/reality/hooks/useEntityTransform.tsx
2592
2910
  function useEntityTransform(entity, { position, rotation, scale }) {
2593
- const last = useRef7({});
2911
+ const last = useRef9({});
2594
2912
  useEffect16(() => {
2595
2913
  if (!entity) return;
2596
2914
  const shouldUpdate = !shallowEqualVec3(last.current.position, position) || !shallowEqualRotation(last.current.rotation, rotation) || !shallowEqualVec3(last.current.scale, scale);
@@ -2610,7 +2928,7 @@ function useEntityTransform(entity, { position, rotation, scale }) {
2610
2928
  }
2611
2929
 
2612
2930
  // src/reality/hooks/useEntityEvent.tsx
2613
- import { useEffect as useEffect18, useRef as useRef9 } from "react";
2931
+ import { useEffect as useEffect17, useRef as useRef10 } from "react";
2614
2932
 
2615
2933
  // src/reality/type.ts
2616
2934
  var eventMap = {
@@ -2812,8 +3130,8 @@ function createEventProxy2(ev, instance) {
2812
3130
  });
2813
3131
  }
2814
3132
  var useEntityEvent = ({ instance, ...handlers }) => {
2815
- const eventsSetRef = useRef9(/* @__PURE__ */ new Set());
2816
- useEffect18(() => {
3133
+ const eventsSetRef = useRef10(/* @__PURE__ */ new Set());
3134
+ useEffect17(() => {
2817
3135
  const entity = instance.entity;
2818
3136
  if (!entity) return;
2819
3137
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
@@ -2826,7 +3144,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2826
3144
  return () => {
2827
3145
  };
2828
3146
  }, [instance.entity, ...Object.values(handlers)]);
2829
- useEffect18(() => {
3147
+ useEffect17(() => {
2830
3148
  const entity = instance.entity;
2831
3149
  if (!entity) return;
2832
3150
  return () => {
@@ -2840,7 +3158,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2840
3158
  };
2841
3159
 
2842
3160
  // src/reality/hooks/useRealityEvents.tsx
2843
- import { useEffect as useEffect19, useRef as useRef10 } from "react";
3161
+ import { useEffect as useEffect18, useRef as useRef11 } from "react";
2844
3162
  function createEventProxy3(ev, instance) {
2845
3163
  return new Proxy(ev, {
2846
3164
  get(target, prop) {
@@ -2954,8 +3272,8 @@ function createEventProxy3(ev, instance) {
2954
3272
  });
2955
3273
  }
2956
3274
  var useRealityEvents = ({ instance, ...handlers }) => {
2957
- const eventsSetRef = useRef10(/* @__PURE__ */ new Set());
2958
- useEffect19(() => {
3275
+ const eventsSetRef = useRef11(/* @__PURE__ */ new Set());
3276
+ useEffect18(() => {
2959
3277
  if (!instance) return;
2960
3278
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
2961
3279
  const handlerFn = handlers[reactKey];
@@ -2976,10 +3294,10 @@ var useRealityEvents = ({ instance, ...handlers }) => {
2976
3294
  };
2977
3295
 
2978
3296
  // src/reality/hooks/useEntityId.tsx
2979
- import { useEffect as useEffect20 } from "react";
3297
+ import { useEffect as useEffect19 } from "react";
2980
3298
  var useEntityId = ({ id, entity }) => {
2981
3299
  const ctx = useRealityContext();
2982
- useEffect20(() => {
3300
+ useEffect19(() => {
2983
3301
  if (!id || !entity || !ctx) return;
2984
3302
  ctx.resourceRegistry.add(id, Promise.resolve(entity));
2985
3303
  return () => {
@@ -2990,7 +3308,7 @@ var useEntityId = ({ id, entity }) => {
2990
3308
  };
2991
3309
 
2992
3310
  // src/reality/hooks/useEntity.tsx
2993
- import { useEffect as useEffect21, useRef as useRef11 } from "react";
3311
+ import { useEffect as useEffect20, useRef as useRef12 } from "react";
2994
3312
  var useEntity = ({
2995
3313
  ref,
2996
3314
  id,
@@ -3011,9 +3329,9 @@ var useEntity = ({
3011
3329
  }) => {
3012
3330
  const ctx = useRealityContext();
3013
3331
  const parent = useParentContext();
3014
- const instanceRef = useRef11(new EntityRef(null, ctx));
3332
+ const instanceRef = useRef12(new EntityRef(null, ctx));
3015
3333
  const forceUpdate = useForceUpdate2();
3016
- useEffect21(() => {
3334
+ useEffect20(() => {
3017
3335
  if (!ctx) return;
3018
3336
  const controller = new AbortController();
3019
3337
  const init = async () => {
@@ -3062,7 +3380,7 @@ var useEntity = ({
3062
3380
  onSpatialMagnify,
3063
3381
  onSpatialMagnifyEnd
3064
3382
  });
3065
- useEffect21(() => {
3383
+ useEffect20(() => {
3066
3384
  const ent = instanceRef.current.entity;
3067
3385
  if (!ent) return;
3068
3386
  if (enableInput !== void 0) {
@@ -3073,10 +3391,10 @@ var useEntity = ({
3073
3391
  };
3074
3392
 
3075
3393
  // src/reality/hooks/useForceUpdate.tsx
3076
- import { useCallback as useCallback8, useState as useState7 } from "react";
3394
+ import { useCallback as useCallback9, useState as useState9 } from "react";
3077
3395
  var useForceUpdate2 = () => {
3078
- const [, setTick] = useState7(0);
3079
- return useCallback8(() => setTick((tick) => tick + 1), []);
3396
+ const [, setTick] = useState9(0);
3397
+ return useCallback9(() => setTick((tick) => tick + 1), []);
3080
3398
  };
3081
3399
 
3082
3400
  // src/reality/components/BaseEntity.tsx
@@ -3115,18 +3433,18 @@ var Entity = forwardRef11((props, ref) => {
3115
3433
  import { forwardRef as forwardRef13 } from "react";
3116
3434
 
3117
3435
  // src/reality/components/GeometryEntity.tsx
3118
- import { forwardRef as forwardRef12, useEffect as useEffect22, useRef as useRef12 } from "react";
3436
+ import { forwardRef as forwardRef12, useEffect as useEffect21, useRef as useRef13 } from "react";
3119
3437
  import { jsx as jsx14 } from "react/jsx-runtime";
3120
3438
  var GeometryEntity = forwardRef12(
3121
3439
  ({ id, children, name, materials, geometryOptions, createGeometry, ...rest }, ref) => {
3122
3440
  const ctx = useRealityContext();
3123
- const entityRef = useRef12(null);
3124
- const componentRef = useRef12(null);
3125
- const mutableRef = useRef12({
3441
+ const entityRef = useRef13(null);
3442
+ const componentRef = useRef13(null);
3443
+ const mutableRef = useRef13({
3126
3444
  lastSnapshot: null,
3127
3445
  rebuildGen: 0
3128
3446
  });
3129
- useEffect22(() => {
3447
+ useEffect21(() => {
3130
3448
  const { lastSnapshot } = mutableRef.current;
3131
3449
  if (!ctx || !entityRef.current || lastSnapshot === null) return;
3132
3450
  const geometryChanged = !shallowEqualObject(
@@ -3248,22 +3566,50 @@ var BoxEntity = forwardRef13(
3248
3566
  );
3249
3567
 
3250
3568
  // src/reality/components/UnlitMaterial.tsx
3251
- import { useEffect as useEffect23, useRef as useRef13 } from "react";
3569
+ import { useEffect as useEffect22, useRef as useRef14, useState as useState10 } from "react";
3252
3570
  var UnlitMaterial = ({
3253
3571
  children,
3254
3572
  ...options
3255
3573
  }) => {
3256
3574
  const ctx = useRealityContext();
3257
- const materialRef = useRef13();
3258
- const isInitializedRef = useRef13(false);
3259
- useEffect23(() => {
3575
+ const materialRef = useRef14(void 0);
3576
+ const isInitializedRef = useRef14(false);
3577
+ const [textureRevision, setTextureRevision] = useState10(0);
3578
+ useEffect22(() => {
3579
+ if (!ctx || !options.textureId) return;
3580
+ return ctx.resourceRegistry.subscribe(options.textureId, () => {
3581
+ setTextureRevision((v) => v + 1);
3582
+ });
3583
+ }, [ctx, options.textureId]);
3584
+ useEffect22(() => {
3260
3585
  if (!ctx) return;
3261
- const { session, reality, resourceRegistry } = ctx;
3586
+ let cancelled = false;
3587
+ const materialId = options.id;
3588
+ const { session, resourceRegistry } = ctx;
3262
3589
  const init = async () => {
3263
- const materialPromise = session.createUnlitMaterial(options);
3264
- resourceRegistry.add(options.id, materialPromise);
3265
3590
  try {
3591
+ let textureIdForNative = options.textureId;
3592
+ if (options.textureId) {
3593
+ const texturePromise = resourceRegistry.get(options.textureId);
3594
+ try {
3595
+ const textureResource = await texturePromise;
3596
+ if (cancelled) return;
3597
+ textureIdForNative = textureResource.id;
3598
+ } catch {
3599
+ textureIdForNative = "";
3600
+ }
3601
+ }
3602
+ if (cancelled) return;
3603
+ const commandOptions = {
3604
+ color: options.color,
3605
+ textureId: textureIdForNative,
3606
+ transparent: options.transparent,
3607
+ opacity: options.opacity
3608
+ };
3609
+ const materialPromise = session.createUnlitMaterial(commandOptions);
3610
+ resourceRegistry.add(materialId, materialPromise);
3266
3611
  const mat = await materialPromise;
3612
+ if (cancelled) return;
3267
3613
  materialRef.current = mat;
3268
3614
  isInitializedRef.current = true;
3269
3615
  } catch (error) {
@@ -3272,22 +3618,110 @@ var UnlitMaterial = ({
3272
3618
  };
3273
3619
  init();
3274
3620
  return () => {
3275
- resourceRegistry.removeAndDestroy(options.id);
3621
+ cancelled = true;
3622
+ resourceRegistry.removeAndDestroy(materialId);
3276
3623
  materialRef.current = void 0;
3277
3624
  isInitializedRef.current = false;
3278
3625
  };
3279
- }, [ctx]);
3626
+ }, [ctx, options.id]);
3627
+ useEffect22(() => {
3628
+ if (!ctx || !isInitializedRef.current || !materialRef.current) return;
3629
+ let cancelled = false;
3630
+ void (async () => {
3631
+ const updates = {};
3632
+ if (options.color !== void 0) updates.color = options.color;
3633
+ if (options.transparent !== void 0)
3634
+ updates.transparent = options.transparent;
3635
+ if (options.opacity !== void 0) updates.opacity = options.opacity;
3636
+ if (options.textureId !== void 0) {
3637
+ if (options.textureId === "") {
3638
+ updates.textureId = "";
3639
+ } else {
3640
+ const texturePromise = ctx.resourceRegistry.get(options.textureId);
3641
+ try {
3642
+ const textureResource = await texturePromise;
3643
+ if (cancelled) return;
3644
+ updates.textureId = textureResource.id;
3645
+ } catch {
3646
+ updates.textureId = "";
3647
+ }
3648
+ }
3649
+ }
3650
+ if (cancelled || Object.keys(updates).length === 0) return;
3651
+ const mat = materialRef.current;
3652
+ if (mat) {
3653
+ void mat.updateProperties(updates).catch(() => {
3654
+ });
3655
+ }
3656
+ })();
3657
+ return () => {
3658
+ cancelled = true;
3659
+ };
3660
+ }, [
3661
+ ctx,
3662
+ options.color,
3663
+ options.textureId,
3664
+ options.transparent,
3665
+ options.opacity,
3666
+ textureRevision
3667
+ ]);
3668
+ return null;
3669
+ };
3670
+
3671
+ // src/reality/components/Texture.tsx
3672
+ import { useEffect as useEffect23, useRef as useRef15 } from "react";
3673
+ var Texture = ({
3674
+ children,
3675
+ id,
3676
+ url,
3677
+ onLoad,
3678
+ onError
3679
+ }) => {
3680
+ const ctx = useRealityContext();
3681
+ const textureRef = useRef15(null);
3682
+ const urlRevisionRef = useRef15(0);
3683
+ useEffect23(() => {
3684
+ if (!ctx) return;
3685
+ const { resourceRegistry } = ctx;
3686
+ const capturedId = id;
3687
+ return () => {
3688
+ textureRef.current = null;
3689
+ resourceRegistry.removeAndDestroy(capturedId);
3690
+ };
3691
+ }, [ctx, id]);
3280
3692
  useEffect23(() => {
3281
- if (!isInitializedRef.current || !materialRef.current) return;
3282
- const updates = {};
3283
- if (options.color !== void 0) updates.color = options.color;
3284
- if (options.transparent !== void 0)
3285
- updates.transparent = options.transparent;
3286
- if (options.opacity !== void 0) updates.opacity = options.opacity;
3287
- if (Object.keys(updates).length > 0) {
3288
- materialRef.current.updateProperties(updates);
3289
- }
3290
- }, [options.color, options.transparent, options.opacity]);
3693
+ if (!ctx) return;
3694
+ const { session, resourceRegistry } = ctx;
3695
+ const revision = ++urlRevisionRef.current;
3696
+ let cancelled = false;
3697
+ void (async () => {
3698
+ const resolvedUrl = getAbsoluteUrl(url);
3699
+ try {
3700
+ if (textureRef.current) {
3701
+ await textureRef.current.updateProperties({ url: resolvedUrl });
3702
+ if (cancelled || revision !== urlRevisionRef.current) return;
3703
+ resourceRegistry.notify(id);
3704
+ onLoad?.();
3705
+ return;
3706
+ }
3707
+ const texturePromise = session.createTexture({ url: resolvedUrl });
3708
+ resourceRegistry.add(id, texturePromise);
3709
+ const tex = await texturePromise;
3710
+ if (cancelled || revision !== urlRevisionRef.current) {
3711
+ tex.destroy();
3712
+ return;
3713
+ }
3714
+ textureRef.current = tex;
3715
+ onLoad?.();
3716
+ } catch (error) {
3717
+ if (cancelled || revision !== urlRevisionRef.current) return;
3718
+ onError?.(error);
3719
+ }
3720
+ })();
3721
+ return () => {
3722
+ cancelled = true;
3723
+ };
3724
+ }, [ctx, id, url]);
3291
3725
  return null;
3292
3726
  };
3293
3727
 
@@ -3386,23 +3820,17 @@ var SceneGraph = ({ children }) => {
3386
3820
  };
3387
3821
 
3388
3822
  // src/reality/components/ModelAsset.tsx
3389
- import { useEffect as useEffect24, useRef as useRef14 } from "react";
3390
- var resolveAssetUrl = (url) => {
3391
- if (url.startsWith("http://") || url.startsWith("https://")) {
3392
- return url;
3393
- }
3394
- return new URL(url, window.location.href).href;
3395
- };
3823
+ import { useEffect as useEffect24, useRef as useRef16 } from "react";
3396
3824
  var ModelAsset = ({ children, ...options }) => {
3397
3825
  const ctx = useRealityContext();
3398
- const materialRef = useRef14();
3826
+ const materialRef = useRef16(void 0);
3399
3827
  useEffect24(() => {
3400
3828
  const controller = new AbortController();
3401
3829
  if (!ctx) return;
3402
- const { session, reality, resourceRegistry } = ctx;
3830
+ const { session, resourceRegistry } = ctx;
3403
3831
  const init = async () => {
3404
3832
  try {
3405
- const resolvedUrl = resolveAssetUrl(options.src);
3833
+ const resolvedUrl = getAbsoluteUrl(options.src);
3406
3834
  const modelAssetPromise = session.createModelAsset({ url: resolvedUrl });
3407
3835
  resourceRegistry.add(options.id, modelAssetPromise);
3408
3836
  const mat = await modelAssetPromise;
@@ -3426,13 +3854,13 @@ var ModelAsset = ({ children, ...options }) => {
3426
3854
  };
3427
3855
 
3428
3856
  // src/reality/components/ModelEntity.tsx
3429
- import { forwardRef as forwardRef18, useEffect as useEffect25, useRef as useRef15 } from "react";
3857
+ import { forwardRef as forwardRef18, useEffect as useEffect25, useRef as useRef17 } from "react";
3430
3858
  import { jsx as jsx21 } from "react/jsx-runtime";
3431
3859
  var ModelEntity = forwardRef18(
3432
3860
  ({ id, model, children, name, materials, ...rest }, ref) => {
3433
3861
  const ctx = useRealityContext();
3434
- const entityRef = useRef15(null);
3435
- const lastMaterialsRef = useRef15(void 0);
3862
+ const entityRef = useRef17(null);
3863
+ const lastMaterialsRef = useRef17(void 0);
3436
3864
  useEffect25(() => {
3437
3865
  if (!ctx || !entityRef.current) return;
3438
3866
  const next = materials ?? [];
@@ -3441,9 +3869,7 @@ var ModelEntity = forwardRef18(
3441
3869
  lastMaterialsRef.current = next;
3442
3870
  const apply = async () => {
3443
3871
  try {
3444
- const materialList = (await Promise.all(
3445
- next.map((mid) => ctx.resourceRegistry.get(mid))
3446
- )).filter(Boolean);
3872
+ const materialList = (await Promise.all(next.map((mid) => ctx.resourceRegistry.get(mid)))).filter(Boolean);
3447
3873
  if (entityRef.current) {
3448
3874
  await entityRef.current.setMaterials(materialList);
3449
3875
  }
@@ -3476,9 +3902,7 @@ var ModelEntity = forwardRef18(
3476
3902
  entityRef.current = ent;
3477
3903
  if (materials && materials.length > 0) {
3478
3904
  const materialList = (await Promise.all(
3479
- materials.map(
3480
- (mid) => ctx2.resourceRegistry.get(mid)
3481
- )
3905
+ materials.map((mid) => ctx2.resourceRegistry.get(mid))
3482
3906
  )).filter(Boolean);
3483
3907
  if (materialList.length > 0 && !signal.aborted) {
3484
3908
  await ent.setMaterials(materialList);
@@ -3499,10 +3923,10 @@ var ModelEntity = forwardRef18(
3499
3923
  // src/reality/components/Reality.tsx
3500
3924
  import {
3501
3925
  forwardRef as forwardRef19,
3502
- useCallback as useCallback9,
3926
+ useCallback as useCallback10,
3503
3927
  useEffect as useEffect26,
3504
- useRef as useRef16,
3505
- useState as useState8
3928
+ useRef as useRef18,
3929
+ useState as useState11
3506
3930
  } from "react";
3507
3931
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
3508
3932
  var Reality = forwardRef19(
@@ -3525,10 +3949,10 @@ var Reality = forwardRef19(
3525
3949
  onSpatialMagnifyEnd,
3526
3950
  ...props
3527
3951
  } = inProps;
3528
- const ctxRef = useRef16(null);
3529
- const creationId = useRef16(0);
3530
- const [isReady, setIsReady] = useState8(false);
3531
- const cleanupReality = useCallback9(() => {
3952
+ const ctxRef = useRef18(null);
3953
+ const creationId = useRef18(0);
3954
+ const [isReady, setIsReady] = useState11(false);
3955
+ const cleanupReality = useCallback10(() => {
3532
3956
  ctxRef.current?.attachmentRegistry.destroy();
3533
3957
  ctxRef.current?.resourceRegistry.destroy();
3534
3958
  ctxRef.current?.reality.destroy();
@@ -3541,7 +3965,7 @@ var Reality = forwardRef19(
3541
3965
  cleanupReality();
3542
3966
  };
3543
3967
  }, [cleanupReality]);
3544
- const createReality = useCallback9(async () => {
3968
+ const createReality = useCallback10(async () => {
3545
3969
  const id = ++creationId.current;
3546
3970
  const resourceRegistry = new ResourceRegistry();
3547
3971
  const attachmentRegistry = new AttachmentRegistry();
@@ -3584,7 +4008,7 @@ var Reality = forwardRef19(
3584
4008
  return null;
3585
4009
  }
3586
4010
  }, [cleanupReality]);
3587
- const content = useCallback9(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
4011
+ const content = useCallback10(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
3588
4012
  useRealityEvents({
3589
4013
  instance: ctxRef.current?.reality ?? null,
3590
4014
  onSpatialTap,
@@ -3613,7 +4037,7 @@ var Reality = forwardRef19(
3613
4037
  );
3614
4038
 
3615
4039
  // src/reality/components/AttachmentAsset.tsx
3616
- import { useEffect as useEffect27, useState as useState9 } from "react";
4040
+ import { useEffect as useEffect27, useState as useState12 } from "react";
3617
4041
  import { createPortal as createPortal3 } from "react-dom";
3618
4042
  import { jsx as jsx23 } from "react/jsx-runtime";
3619
4043
  var AttachmentAsset = ({
@@ -3621,7 +4045,7 @@ var AttachmentAsset = ({
3621
4045
  children
3622
4046
  }) => {
3623
4047
  const ctx = useRealityContext();
3624
- const [containers, setContainers] = useState9([]);
4048
+ const [containers, setContainers] = useState12([]);
3625
4049
  useEffect27(() => {
3626
4050
  if (!ctx) return;
3627
4051
  return ctx.attachmentRegistry.onContainersChange(name, setContainers);
@@ -3633,7 +4057,7 @@ var AttachmentAsset = ({
3633
4057
  };
3634
4058
 
3635
4059
  // src/reality/components/AttachmentEntity.tsx
3636
- import { useEffect as useEffect28, useRef as useRef17, useState as useState10 } from "react";
4060
+ import { useEffect as useEffect28, useRef as useRef19, useState as useState13 } from "react";
3637
4061
  var instanceCounter = 0;
3638
4062
  var AttachmentEntity = ({
3639
4063
  attachment: attachmentName,
@@ -3642,11 +4066,11 @@ var AttachmentEntity = ({
3642
4066
  }) => {
3643
4067
  const ctx = useRealityContext();
3644
4068
  const parent = useParentContext();
3645
- const attachmentRef = useRef17(null);
3646
- const parentIdRef = useRef17(null);
3647
- const instanceIdRef = useRef17(`att_${++instanceCounter}`);
3648
- const attachmentNameRef = useRef17(attachmentName);
3649
- const [childWindow, setChildWindow] = useState10(null);
4069
+ const attachmentRef = useRef19(null);
4070
+ const parentIdRef = useRef19(null);
4071
+ const instanceIdRef = useRef19(`att_${++instanceCounter}`);
4072
+ const attachmentNameRef = useRef19(attachmentName);
4073
+ const [childWindow, setChildWindow] = useState13(null);
3650
4074
  useEffect28(() => {
3651
4075
  if (!ctx || !parent) return;
3652
4076
  if (attachmentRef.current) return;
@@ -3725,7 +4149,7 @@ var AttachmentEntity = ({
3725
4149
  attachmentNameRef.current = attachmentName;
3726
4150
  }
3727
4151
  }, [ctx, attachmentName]);
3728
- useSyncHeadStyles(childWindow, { subtree: false });
4152
+ useSyncHeadStyles(childWindow);
3729
4153
  useEffect28(() => {
3730
4154
  if (!attachmentRef.current) return;
3731
4155
  attachmentRef.current.update({ position, size });
@@ -3787,7 +4211,7 @@ var styleFlag = "enableXr";
3787
4211
  var classFlag = "__enableXr__";
3788
4212
  var xrMonitorFlag = "enable-xr-monitor";
3789
4213
  function replaceToSpatialPrimitiveType(type, props) {
3790
- if (type === Model) {
4214
+ if (type === Model || type === Reality) {
3791
4215
  return type;
3792
4216
  }
3793
4217
  const propsObject = props;
@@ -3830,7 +4254,7 @@ function resolveSpatialObjectId(target) {
3830
4254
  if (maybeEntity && typeof maybeEntity === "object" && "entity" in maybeEntity) {
3831
4255
  return maybeEntity.entity?.id ?? null;
3832
4256
  }
3833
- const dom = target?.__raw ?? target;
4257
+ const dom = target;
3834
4258
  if (dom && typeof dom === "object") {
3835
4259
  const spatializedElement = dom.__spatializedElement ?? dom.__innerSpatializedElement?.();
3836
4260
  if (spatializedElement && spatializedElement.id) {
@@ -3860,7 +4284,7 @@ async function convertCoordinate(position, { from, to }) {
3860
4284
  }
3861
4285
 
3862
4286
  // src/index.ts
3863
- var version = "1.6.1";
4287
+ var version = "1.7.0";
3864
4288
  if (typeof window !== "undefined") {
3865
4289
  initPolyfill();
3866
4290
  }
@@ -3889,12 +4313,16 @@ export {
3889
4313
  SpatializedStatic3DElementContainer,
3890
4314
  SphereEntity as Sphere,
3891
4315
  SphereEntity,
4316
+ Texture,
3892
4317
  UnlitMaterial,
4318
+ WebSpatialRuntime,
4319
+ WebSpatialRuntimeError,
3893
4320
  SceneGraph as World,
3894
4321
  convertCoordinate,
3895
4322
  createElement,
3896
4323
  enableDebugTool,
3897
4324
  eventMap,
4325
+ getAbsoluteUrl,
3898
4326
  initPolyfill,
3899
4327
  initScene,
3900
4328
  useMetrics,