@webspatial/react-sdk 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  (function(){
3
3
  if(typeof window === 'undefined') return;
4
4
  if(!window.__webspatialsdk__) window.__webspatialsdk__ = {}
5
- window.__webspatialsdk__['react-sdk-version'] = "1.4.0"
5
+ window.__webspatialsdk__['react-sdk-version'] = "1.6.0"
6
6
  window.__webspatialsdk__['XR_ENV'] = "avp"
7
7
  })()
8
8
 
@@ -145,11 +145,20 @@ function joinToCSSText(cssKV) {
145
145
  }
146
146
 
147
147
  // src/spatialized-container/hooks/useDomProxy.ts
148
- function makeOriginalKey(key) {
149
- return `__original_${key}`;
150
- }
151
148
  var SpatialContainerRefProxy = class {
152
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;
153
162
  ref;
154
163
  domProxy;
155
164
  styleProxy;
@@ -159,211 +168,263 @@ var SpatialContainerRefProxy = class {
159
168
  this.ref = ref;
160
169
  this.extraRefProps = extraRefProps;
161
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
+ }
162
226
  updateStandardSpatializedContainerDom(dom) {
163
227
  const self = this;
164
- if (dom) {
165
- let cacheExtraRefProps;
166
- const domProxy = new Proxy(
167
- dom,
168
- {
169
- get(target, prop) {
170
- if (prop === "__raw") {
171
- return target;
172
- }
173
- if (prop === "xrClientDepth") {
174
- return target.style.getPropertyValue(SpatialCustomStyleVars.depth);
175
- }
176
- if (prop === "xrOffsetBack") {
177
- return target.style.getPropertyValue(SpatialCustomStyleVars.back);
178
- }
179
- if (prop === "style") {
180
- if (!self.styleProxy) {
181
- self.styleProxy = new Proxy(target.style, {
182
- get(target2, prop2) {
183
- if (prop2 === "visibility" || prop2 === "transform") {
184
- return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
185
- prop2
186
- );
187
- }
188
- const value2 = Reflect.get(target2, prop2);
189
- if (typeof value2 === "function") {
190
- if (prop2 === "setProperty" || prop2 === "removeProperty" || prop2 === "getPropertyValue") {
191
- return function(...args) {
192
- const validProperties = ["visibility", "transform"];
193
- const [property] = args;
194
- if (validProperties.includes(property)) {
195
- if (prop2 === "setProperty") {
196
- const [, kValue] = args;
197
- self.transformVisibilityTaskContainerDom?.style.setProperty(
198
- property,
199
- kValue
200
- );
201
- } else if (prop2 === "removeProperty") {
202
- self.transformVisibilityTaskContainerDom?.style.removeProperty(
203
- property
204
- );
205
- } else if (prop2 === "getPropertyValue") {
206
- return self.transformVisibilityTaskContainerDom?.style.getPropertyValue(
207
- property
208
- );
209
- }
210
- } else {
211
- return value2.apply(this, args);
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
+ );
212
282
  }
213
- }.bind(target2);
214
- } else {
215
- return value2.bind(target2);
216
- }
217
- } else {
218
- return value2;
219
- }
220
- },
221
- set(target2, prop2, value2) {
222
- if (prop2 === "visibility") {
223
- self.transformVisibilityTaskContainerDom?.style.setProperty(
224
- "visibility",
225
- value2
226
- );
227
- return true;
228
- }
229
- if (prop2 === "transform") {
230
- self.transformVisibilityTaskContainerDom?.style.setProperty(
231
- "transform",
232
- value2
233
- );
234
- return true;
235
- }
236
- if (prop2 === SpatialCustomStyleVars.backgroundMaterial) {
237
- target2.setProperty(
238
- SpatialCustomStyleVars.backgroundMaterial,
239
- value2
240
- );
241
- } else if (prop2 === SpatialCustomStyleVars.back) {
242
- target2.setProperty(
243
- SpatialCustomStyleVars.back,
244
- value2
245
- );
246
- } else if (prop2 === SpatialCustomStyleVars.xrZIndex) {
247
- target2.setProperty(
248
- SpatialCustomStyleVars.xrZIndex,
249
- value2
250
- );
251
- } else if (prop2 === SpatialCustomStyleVars.depth) {
252
- target2.setProperty(
253
- SpatialCustomStyleVars.depth,
254
- value2
255
- );
256
- } else if (prop2 === "cssText") {
257
- const toFilteredCSSProperties = [
258
- "transform",
259
- "visibility"
260
- ];
261
- const { extractedValues, filteredCssText } = extractAndRemoveCustomProperties(
262
- value2,
263
- toFilteredCSSProperties
264
- );
265
- toFilteredCSSProperties.forEach((key) => {
266
- if (extractedValues[key]) {
267
- self.transformVisibilityTaskContainerDom?.style.setProperty(
268
- key,
269
- extractedValues[key]
270
- );
271
283
  } else {
272
- target2.removeProperty(key);
284
+ return value2.apply(this, args);
273
285
  }
274
- });
275
- const appendedCSSText = joinToCSSText({
276
- transform: "none",
277
- visibility: "hidden"
278
- });
279
- return Reflect.set(
280
- target2,
281
- prop2,
282
- [appendedCSSText, filteredCssText].join(";")
283
- );
286
+ }.bind(target2);
287
+ } else {
288
+ return value2.bind(target2);
284
289
  }
285
- return Reflect.set(target2, prop2, value2);
290
+ } else {
291
+ return value2;
286
292
  }
287
- });
288
- }
289
- return self.styleProxy;
290
- }
291
- if (typeof prop === "string" && self.extraRefProps) {
292
- if (!cacheExtraRefProps) {
293
- cacheExtraRefProps = self.extraRefProps(domProxy);
294
- }
295
- const extraProps = cacheExtraRefProps;
296
- if (extraProps.hasOwnProperty(prop)) {
297
- return extraProps[prop];
298
- }
299
- }
300
- const value = Reflect.get(target, prop);
301
- if (typeof value === "function") {
302
- if ("removeAttribute" === prop) {
303
- return function(...args) {
304
- const [property] = args;
305
- if (property === "style") {
306
- dom.style.cssText = "visibility: hidden; transition: none; transform: none;";
307
- if (self.transformVisibilityTaskContainerDom) {
308
- self.transformVisibilityTaskContainerDom.style.visibility = "";
309
- self.transformVisibilityTaskContainerDom.style.transform = "";
310
- }
293
+ },
294
+ set(target2, prop2, value2) {
295
+ if (prop2 === "visibility") {
296
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
297
+ "visibility",
298
+ value2
299
+ );
311
300
  return true;
312
301
  }
313
- if (property === "class") {
314
- domProxy.className = "xr-spatial-default";
302
+ if (prop2 === "transform") {
303
+ self.transformVisibilityTaskContainerDom?.style.setProperty(
304
+ "transform",
305
+ value2
306
+ );
315
307
  return true;
316
308
  }
317
- };
318
- }
319
- return value.bind(target);
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
+ });
320
358
  }
321
- return value;
322
- },
323
- set(target, prop, value) {
324
- if (prop === "className") {
325
- if (value && value.indexOf("xr-spatial-default") === -1) {
326
- value = value + " xr-spatial-default";
327
- }
328
- if (self.transformVisibilityTaskContainerDom) {
329
- self.transformVisibilityTaskContainerDom.className = value;
330
- }
359
+ return self.styleProxy;
360
+ }
361
+ if (typeof prop === "string" && self.extraRefProps) {
362
+ if (!cacheExtraRefProps) {
363
+ cacheExtraRefProps = self.extraRefProps(domProxy);
331
364
  }
332
- if (typeof prop === "string" && self.extraRefProps) {
333
- if (!cacheExtraRefProps) {
334
- cacheExtraRefProps = self.extraRefProps(domProxy);
335
- }
336
- cacheExtraRefProps[prop] = value;
365
+ const extraProps = cacheExtraRefProps;
366
+ if (extraProps.hasOwnProperty(prop)) {
367
+ return extraProps[prop];
337
368
  }
338
- return Reflect.set(target, prop, value);
339
369
  }
340
- }
341
- );
342
- this.domProxy = domProxy;
343
- const domClassList = dom.classList;
344
- const domClassMethodKeys = ["add", "remove", "toggle", "replace"];
345
- domClassMethodKeys.forEach((key) => {
346
- const hiddenKey = makeOriginalKey(key);
347
- const hiddenKeyExist = domClassList[hiddenKey] !== void 0;
348
- const originalMethod = hiddenKeyExist ? domClassList[hiddenKey] : domClassList[key].bind(domClassList);
349
- domClassList[hiddenKey] = originalMethod;
350
- domClassList[key] = function(...args) {
351
- const result = originalMethod(...args);
352
- if (self.transformVisibilityTaskContainerDom) {
353
- self.transformVisibilityTaskContainerDom.className = dom.className;
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);
354
390
  }
355
- return result;
356
- };
357
- });
358
- this.styleProxy = void 0;
359
- this.updateDomProxyToRef();
360
- Object.assign(dom, {
361
- __targetProxy: domProxy
362
- });
363
- }
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();
364
421
  }
365
422
  updateTransformVisibilityTaskContainerDom(dom) {
366
423
  this.transformVisibilityTaskContainerDom = dom;
424
+ if (!dom) {
425
+ this.lastMirroredClassName = null;
426
+ }
427
+ this.scheduleSyncTransformClassFromStandard();
367
428
  this.updateDomProxyToRef();
368
429
  }
369
430
  updateDomProxyToRef() {
@@ -843,7 +904,14 @@ var TransformVisibilityTaskContainer = forwardRef2(
843
904
  );
844
905
 
845
906
  // src/spatialized-container/SpatializedContainer.tsx
846
- import { forwardRef as forwardRef4, useContext as useContext7, useEffect as useEffect10, useMemo as useMemo2 } from "react";
907
+ import {
908
+ forwardRef as forwardRef4,
909
+ useCallback as useCallback6,
910
+ useContext as useContext7,
911
+ useEffect as useEffect10,
912
+ useMemo as useMemo2,
913
+ useState as useState6
914
+ } from "react";
847
915
 
848
916
  // src/utils/getSession.ts
849
917
  import { isSSREnv, Spatial } from "@webspatial/core-sdk";
@@ -1148,7 +1216,7 @@ function renderPlaceholderInSubPortal(portalInstanceObject, El) {
1148
1216
  return /* @__PURE__ */ jsx3(Fragment, {});
1149
1217
  }
1150
1218
  const { width, height } = portalInstanceObject.domRect;
1151
- const display = portalInstanceObject.computedStyle.getPropertyPriority("display");
1219
+ const display = portalInstanceObject.computedStyle.getPropertyValue("display");
1152
1220
  const spatialIdProps = { [SpatialID]: spatialId };
1153
1221
  return /* @__PURE__ */ jsx3(
1154
1222
  El,
@@ -1593,6 +1661,20 @@ function SpatializedContainerBase(inprops, ref) {
1593
1661
  standardSpatializedContainerCallback,
1594
1662
  spatialContainerRefProxy
1595
1663
  } = useDomProxy(ref, extraRefProps);
1664
+ const [probeClassName, setProbeClassName] = useState6(
1665
+ () => props.className ?? ""
1666
+ );
1667
+ const notifyProbeClass = useCallback6((name) => {
1668
+ setProbeClassName((prev) => prev === name ? prev : name);
1669
+ }, []);
1670
+ useEffect10(() => {
1671
+ spatialContainerRefProxy.current.setMirrorClassNotify?.(
1672
+ notifyProbeClass
1673
+ );
1674
+ return () => {
1675
+ spatialContainerRefProxy.current.setMirrorClassNotify?.(null);
1676
+ };
1677
+ }, [spatialContainerRefProxy, notifyProbeClass]);
1596
1678
  useEffect10(() => {
1597
1679
  rootSpatializedContainerObject.updateSpatialContainerRefProxyInfo(
1598
1680
  spatialId,
@@ -1621,7 +1703,7 @@ function SpatializedContainerBase(inprops, ref) {
1621
1703
  {
1622
1704
  ref: transformVisibilityTaskContainerCallback,
1623
1705
  ...spatialIdProps,
1624
- className: props.className,
1706
+ className: probeClassName,
1625
1707
  style: props.style
1626
1708
  }
1627
1709
  )
@@ -1633,6 +1715,18 @@ function SpatializedContainerBase(inprops, ref) {
1633
1715
  standardSpatializedContainerCallback,
1634
1716
  spatialContainerRefProxy
1635
1717
  } = useDomProxy(ref, extraRefProps);
1718
+ const [probeClassName, setProbeClassName] = useState6(
1719
+ () => props.className ?? ""
1720
+ );
1721
+ const notifyProbeClass = useCallback6((name) => {
1722
+ setProbeClassName((prev) => prev === name ? prev : name);
1723
+ }, []);
1724
+ useEffect10(() => {
1725
+ spatialContainerRefProxy.current.setMirrorClassNotify?.(notifyProbeClass);
1726
+ return () => {
1727
+ spatialContainerRefProxy.current.setMirrorClassNotify?.(null);
1728
+ };
1729
+ }, [spatialContainerRefProxy, notifyProbeClass]);
1636
1730
  const spatialEvents = useSpatialEvents(
1637
1731
  {
1638
1732
  onSpatialTap,
@@ -1684,7 +1778,7 @@ function SpatializedContainerBase(inprops, ref) {
1684
1778
  {
1685
1779
  ref: transformVisibilityTaskContainerCallback,
1686
1780
  ...spatialIdProps,
1687
- className: props.className,
1781
+ className: probeClassName,
1688
1782
  style: props.style
1689
1783
  }
1690
1784
  )
@@ -1964,8 +2058,10 @@ var Spatialized2DElementContainer = forwardRef5(
1964
2058
 
1965
2059
  // src/spatialized-container/SpatializedStatic3DElementContainer.tsx
1966
2060
  import {
2061
+ Children,
1967
2062
  forwardRef as forwardRef6,
1968
- useCallback as useCallback6,
2063
+ isValidElement,
2064
+ useCallback as useCallback7,
1969
2065
  useContext as useContext9,
1970
2066
  useEffect as useEffect13,
1971
2067
  useMemo as useMemo3,
@@ -1973,9 +2069,7 @@ import {
1973
2069
  } from "react";
1974
2070
  import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
1975
2071
  function getAbsoluteURL(url) {
1976
- if (!url) {
1977
- return "";
1978
- }
2072
+ if (!url) return url;
1979
2073
  try {
1980
2074
  return new URL(url, document.baseURI).toString();
1981
2075
  } catch {
@@ -2003,21 +2097,34 @@ function createLoadFailureEvent(targetGetter) {
2003
2097
  function createLoadSuccessEvent(targetGetter) {
2004
2098
  return createLoadEvent("modelloaded", targetGetter);
2005
2099
  }
2100
+ function collectSources(children) {
2101
+ const sources = [];
2102
+ Children.forEach(children, (child) => {
2103
+ if (isValidElement(child) && child.type === "source") {
2104
+ const { src, type } = child.props;
2105
+ if (src) {
2106
+ sources.push({ src: getAbsoluteURL(src), type });
2107
+ }
2108
+ }
2109
+ });
2110
+ return sources;
2111
+ }
2006
2112
  function SpatializedContent2(props) {
2007
- const { src, spatializedElement, onLoad, onError } = props;
2008
- const spatializedStatic3DElement = spatializedElement;
2009
- const portalInstanceObject = useContext9(
2010
- PortalInstanceContext
2011
- );
2012
- const currentSrc = useMemo3(() => getAbsoluteURL(src), [src]);
2113
+ const { src, children, spatializedElement, onLoad, onError, autoPlay, loop } = props;
2114
+ const portalInstanceObject = useContext9(PortalInstanceContext);
2115
+ const modelURL = useMemo3(() => getAbsoluteURL(src), [src]);
2116
+ const sources = useMemo3(() => collectSources(children), [children]);
2013
2117
  useEffect13(() => {
2014
- if (src) {
2015
- spatializedStatic3DElement.updateProperties({ modelURL: currentSrc });
2016
- }
2017
- }, [currentSrc]);
2118
+ spatializedElement.updateProperties({
2119
+ modelURL: modelURL ?? "",
2120
+ sources,
2121
+ autoplay: autoPlay,
2122
+ loop
2123
+ });
2124
+ }, [modelURL, JSON.stringify(sources), autoPlay, loop]);
2018
2125
  useEffect13(() => {
2019
2126
  if (onLoad) {
2020
- spatializedStatic3DElement.onLoadCallback = () => {
2127
+ spatializedElement.onLoadCallback = () => {
2021
2128
  onLoad(
2022
2129
  createLoadSuccessEvent(
2023
2130
  () => portalInstanceObject.dom.__targetProxy
@@ -2025,12 +2132,12 @@ function SpatializedContent2(props) {
2025
2132
  );
2026
2133
  };
2027
2134
  } else {
2028
- spatializedStatic3DElement.onLoadCallback = void 0;
2135
+ spatializedElement.onLoadCallback = void 0;
2029
2136
  }
2030
2137
  }, [onLoad]);
2031
2138
  useEffect13(() => {
2032
2139
  if (onError) {
2033
- spatializedStatic3DElement.onLoadFailureCallback = () => {
2140
+ spatializedElement.onLoadFailureCallback = () => {
2034
2141
  onError(
2035
2142
  createLoadFailureEvent(
2036
2143
  () => portalInstanceObject.dom.__targetProxy
@@ -2038,24 +2145,27 @@ function SpatializedContent2(props) {
2038
2145
  );
2039
2146
  };
2040
2147
  } else {
2041
- spatializedStatic3DElement.onLoadFailureCallback = void 0;
2148
+ spatializedElement.onLoadFailureCallback = void 0;
2042
2149
  }
2043
2150
  }, [onError]);
2044
2151
  return /* @__PURE__ */ jsx8(Fragment2, {});
2045
2152
  }
2046
2153
  function SpatializedStatic3DElementContainerBase(props, ref) {
2047
2154
  const promiseRef = useRef5(null);
2048
- const createSpatializedElement2 = useCallback6(() => {
2049
- const url = getAbsoluteURL(props.src);
2050
- promiseRef.current = getSession().createSpatializedStatic3DElement(url);
2155
+ const createSpatializedElement2 = useCallback7(() => {
2156
+ promiseRef.current = getSession().createSpatializedStatic3DElement(
2157
+ getAbsoluteURL(props.src),
2158
+ collectSources(props.children)
2159
+ );
2051
2160
  return promiseRef.current;
2052
2161
  }, []);
2053
- const extraRefProps = useCallback6(
2162
+ const extraRefProps = useCallback7(
2054
2163
  (domProxy) => {
2055
2164
  let modelTransform = new DOMMatrixReadOnly();
2056
2165
  return {
2057
2166
  get currentSrc() {
2058
- return getAbsoluteURL(props.src);
2167
+ const spatializedElement = domProxy.__spatializedElement;
2168
+ return spatializedElement?.currentSrc ?? "";
2059
2169
  },
2060
2170
  get ready() {
2061
2171
  return promiseRef.current.then((spatializedElement) => spatializedElement.ready).then((success) => {
@@ -2070,6 +2180,32 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
2070
2180
  modelTransform = value;
2071
2181
  const spatializedElement = domProxy.__spatializedElement;
2072
2182
  spatializedElement?.updateModelTransform(modelTransform);
2183
+ },
2184
+ async play() {
2185
+ const spatializedElement = domProxy.__spatializedElement;
2186
+ await spatializedElement?.play();
2187
+ },
2188
+ async pause() {
2189
+ const spatializedElement = domProxy.__spatializedElement;
2190
+ await spatializedElement?.pause();
2191
+ },
2192
+ get paused() {
2193
+ const spatializedElement = domProxy.__spatializedElement;
2194
+ return spatializedElement?.paused ?? true;
2195
+ },
2196
+ get duration() {
2197
+ const spatializedElement = domProxy.__spatializedElement;
2198
+ return spatializedElement?.duration ?? 0;
2199
+ },
2200
+ get playbackRate() {
2201
+ const spatializedElement = domProxy.__spatializedElement;
2202
+ return spatializedElement?.playbackRate ?? 1;
2203
+ },
2204
+ set playbackRate(value) {
2205
+ const spatializedElement = domProxy.__spatializedElement;
2206
+ if (spatializedElement) {
2207
+ spatializedElement.playbackRate = value;
2208
+ }
2073
2209
  }
2074
2210
  };
2075
2211
  },
@@ -2349,6 +2485,20 @@ function shallowEqualRotation(a, b) {
2349
2485
  if (!a || !b) return false;
2350
2486
  return a.x === b.x && a.y === b.y && a.z === b.z && ("w" in a ? a.w === b.w : true);
2351
2487
  }
2488
+ function shallowEqualObject(a, b) {
2489
+ if (a === b) return true;
2490
+ if (!a || !b) return false;
2491
+ const keysA = Object.keys(a);
2492
+ const keysB = Object.keys(b);
2493
+ if (keysA.length !== keysB.length) return false;
2494
+ return keysA.every((key) => a[key] === b[key]);
2495
+ }
2496
+ function shallowEqualArray(a, b) {
2497
+ if (a === b) return true;
2498
+ if (!a || !b) return false;
2499
+ if (a.length !== b.length) return false;
2500
+ return a.every((val, i) => val === b[i]);
2501
+ }
2352
2502
 
2353
2503
  // src/reality/utils/AbortResourceManager.ts
2354
2504
  var AbortResourceManager = class {
@@ -2493,9 +2643,150 @@ var EntityRef = class {
2493
2643
  }
2494
2644
  };
2495
2645
 
2646
+ // src/reality/hooks/useEntityEvent.tsx
2647
+ function createEventProxy2(ev, instance) {
2648
+ return new Proxy(ev, {
2649
+ get(target, prop) {
2650
+ if (prop === "currentTarget") {
2651
+ return instance;
2652
+ }
2653
+ if (prop === "target") {
2654
+ const origin = target.__origin;
2655
+ if (origin) {
2656
+ return new EntityRef(origin, null);
2657
+ }
2658
+ return instance;
2659
+ }
2660
+ if (prop === "bubbles") {
2661
+ return true;
2662
+ }
2663
+ if (prop === "offsetX") {
2664
+ const type = target.type;
2665
+ if (type === "spatialtap") {
2666
+ return target.detail?.location3D?.x ?? 0;
2667
+ }
2668
+ if (type === "spatialdragstart") {
2669
+ return target.detail?.startLocation3D?.x ?? 0;
2670
+ }
2671
+ return void 0;
2672
+ }
2673
+ if (prop === "offsetY") {
2674
+ const type = target.type;
2675
+ if (type === "spatialtap") {
2676
+ return target.detail?.location3D?.y ?? 0;
2677
+ }
2678
+ if (type === "spatialdragstart") {
2679
+ return target.detail?.startLocation3D?.y ?? 0;
2680
+ }
2681
+ return void 0;
2682
+ }
2683
+ if (prop === "offsetZ") {
2684
+ const type = target.type;
2685
+ if (type === "spatialtap") {
2686
+ return target.detail?.location3D?.z ?? 0;
2687
+ }
2688
+ if (type === "spatialdragstart") {
2689
+ return target.detail?.startLocation3D?.z ?? 0;
2690
+ }
2691
+ return void 0;
2692
+ }
2693
+ if (prop === "translationX") {
2694
+ const type = target.type;
2695
+ if (type === "spatialdrag") {
2696
+ return target.detail?.translation3D?.x ?? 0;
2697
+ }
2698
+ return void 0;
2699
+ }
2700
+ if (prop === "translationY") {
2701
+ const type = target.type;
2702
+ if (type === "spatialdrag") {
2703
+ return target.detail?.translation3D?.y ?? 0;
2704
+ }
2705
+ return void 0;
2706
+ }
2707
+ if (prop === "translationZ") {
2708
+ const type = target.type;
2709
+ if (type === "spatialdrag") {
2710
+ return target.detail?.translation3D?.z ?? 0;
2711
+ }
2712
+ return void 0;
2713
+ }
2714
+ if (prop === "quaternion") {
2715
+ const type = target.type;
2716
+ if (type === "spatialrotate") {
2717
+ return target.detail?.quaternion ?? {
2718
+ x: 0,
2719
+ y: 0,
2720
+ z: 0,
2721
+ w: 1
2722
+ };
2723
+ }
2724
+ return void 0;
2725
+ }
2726
+ if (prop === "magnification") {
2727
+ const type = target.type;
2728
+ if (type === "spatialmagnify") {
2729
+ return target.detail?.magnification ?? 1;
2730
+ }
2731
+ return void 0;
2732
+ }
2733
+ if (prop === "clientX") {
2734
+ const type = target.type;
2735
+ if (type === "spatialtap" || type === "spatialdragstart") {
2736
+ return target.detail?.globalLocation3D?.x ?? 0;
2737
+ }
2738
+ return void 0;
2739
+ }
2740
+ if (prop === "clientY") {
2741
+ const type = target.type;
2742
+ if (type === "spatialtap" || type === "spatialdragstart") {
2743
+ return target.detail?.globalLocation3D?.y ?? 0;
2744
+ }
2745
+ return void 0;
2746
+ }
2747
+ if (prop === "clientZ") {
2748
+ const type = target.type;
2749
+ if (type === "spatialtap" || type === "spatialdragstart") {
2750
+ return target.detail?.globalLocation3D?.z ?? 0;
2751
+ }
2752
+ return void 0;
2753
+ }
2754
+ const val = target[prop];
2755
+ return typeof val === "function" ? val.bind(target) : val;
2756
+ }
2757
+ });
2758
+ }
2759
+ var useEntityEvent = ({ instance, ...handlers }) => {
2760
+ const eventsSetRef = useRef9(/* @__PURE__ */ new Set());
2761
+ useEffect18(() => {
2762
+ const entity = instance.entity;
2763
+ if (!entity) return;
2764
+ Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
2765
+ const handlerFn = handlers[reactKey];
2766
+ if (!handlerFn) return;
2767
+ const wrapped = (ev) => handlerFn(createEventProxy2(ev, instance));
2768
+ entity.addEvent(spatialEvent, wrapped);
2769
+ eventsSetRef.current.add(reactKey);
2770
+ });
2771
+ return () => {
2772
+ };
2773
+ }, [instance.entity, ...Object.values(handlers)]);
2774
+ useEffect18(() => {
2775
+ const entity = instance.entity;
2776
+ if (!entity) return;
2777
+ return () => {
2778
+ for (let x of eventsSetRef.current) {
2779
+ entity.removeEvent(x);
2780
+ }
2781
+ eventsSetRef.current.clear();
2782
+ };
2783
+ }, [instance.entity]);
2784
+ return null;
2785
+ };
2786
+
2496
2787
  // src/reality/hooks/useRealityEvents.tsx
2497
2788
  import { useEffect as useEffect19, useRef as useRef10 } from "react";
2498
- function createEventProxy2(ev, instance) {
2789
+ function createEventProxy3(ev, instance) {
2499
2790
  return new Proxy(ev, {
2500
2791
  get(target, prop) {
2501
2792
  if (prop === "currentTarget") {
@@ -2614,7 +2905,7 @@ var useRealityEvents = ({ instance, ...handlers }) => {
2614
2905
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
2615
2906
  const handlerFn = handlers[reactKey];
2616
2907
  if (!handlerFn) return;
2617
- const wrapped = (ev) => handlerFn(createEventProxy2(ev, instance));
2908
+ const wrapped = (ev) => handlerFn(createEventProxy3(ev, instance));
2618
2909
  instance.addEvent(spatialEvent, wrapped);
2619
2910
  eventsSetRef.current.add(spatialEvent);
2620
2911
  });
@@ -2652,7 +2943,16 @@ var useEntity = ({
2652
2943
  rotation,
2653
2944
  scale,
2654
2945
  enableInput,
2655
- createEntity
2946
+ onSpatialTap,
2947
+ onSpatialDragStart,
2948
+ onSpatialDrag,
2949
+ onSpatialDragEnd,
2950
+ onSpatialRotate,
2951
+ onSpatialRotateEnd,
2952
+ onSpatialMagnify,
2953
+ onSpatialMagnifyEnd,
2954
+ createEntity,
2955
+ recreateKey
2656
2956
  }) => {
2657
2957
  const ctx = useRealityContext();
2658
2958
  const parent = useParentContext();
@@ -2669,6 +2969,11 @@ var useEntity = ({
2669
2969
  ent.destroy();
2670
2970
  return;
2671
2971
  }
2972
+ await ent.updateTransform({ position, rotation, scale });
2973
+ if (controller.signal.aborted) {
2974
+ ent.destroy();
2975
+ return;
2976
+ }
2672
2977
  if (parent) {
2673
2978
  const result = await parent.addEntity(ent);
2674
2979
  if (!result.success) throw new Error("parent.addEntity failed");
@@ -2687,10 +2992,21 @@ var useEntity = ({
2687
2992
  controller.abort();
2688
2993
  instanceRef.current?.destroy();
2689
2994
  };
2690
- }, [ctx, parent]);
2995
+ }, [ctx, parent, recreateKey]);
2691
2996
  useEntityId({ id, entity: instanceRef.current.entity });
2692
2997
  useEntityTransform(instanceRef.current.entity, { position, rotation, scale });
2693
2998
  useEntityRef(ref, instanceRef.current);
2999
+ useEntityEvent({
3000
+ instance: instanceRef.current,
3001
+ onSpatialTap,
3002
+ onSpatialDragStart,
3003
+ onSpatialDrag,
3004
+ onSpatialDragEnd,
3005
+ onSpatialRotate,
3006
+ onSpatialRotateEnd,
3007
+ onSpatialMagnify,
3008
+ onSpatialMagnifyEnd
3009
+ });
2694
3010
  useEffect21(() => {
2695
3011
  const ent = instanceRef.current.entity;
2696
3012
  if (!ent) return;
@@ -2702,20 +3018,21 @@ var useEntity = ({
2702
3018
  };
2703
3019
 
2704
3020
  // src/reality/hooks/useForceUpdate.tsx
2705
- import { useCallback as useCallback7, useState as useState7 } from "react";
3021
+ import { useCallback as useCallback8, useState as useState7 } from "react";
2706
3022
  var useForceUpdate2 = () => {
2707
3023
  const [, setTick] = useState7(0);
2708
- return useCallback7(() => setTick((tick) => tick + 1), []);
3024
+ return useCallback8(() => setTick((tick) => tick + 1), []);
2709
3025
  };
2710
3026
 
2711
3027
  // src/reality/components/BaseEntity.tsx
2712
3028
  import { jsx as jsx12 } from "react/jsx-runtime";
2713
3029
  var BaseEntity = forwardRef10(
2714
- ({ children, createEntity, ...rest }, ref) => {
3030
+ ({ children, createEntity, recreateKey, ...rest }, ref) => {
2715
3031
  const ctx = useRealityContext();
2716
3032
  const entity = useEntity({
2717
3033
  ...rest,
2718
3034
  ref,
3035
+ recreateKey,
2719
3036
  createEntity: (signal) => createEntity(ctx, signal)
2720
3037
  });
2721
3038
  if (!entity) return null;
@@ -2743,35 +3060,102 @@ var Entity = forwardRef11((props, ref) => {
2743
3060
  import { forwardRef as forwardRef13 } from "react";
2744
3061
 
2745
3062
  // src/reality/components/GeometryEntity.tsx
2746
- import { forwardRef as forwardRef12 } from "react";
3063
+ import { forwardRef as forwardRef12, useEffect as useEffect22, useRef as useRef12 } from "react";
2747
3064
  import { jsx as jsx14 } from "react/jsx-runtime";
2748
3065
  var GeometryEntity = forwardRef12(
2749
3066
  ({ id, children, name, materials, geometryOptions, createGeometry, ...rest }, ref) => {
3067
+ const ctx = useRealityContext();
3068
+ const entityRef = useRef12(null);
3069
+ const componentRef = useRef12(null);
3070
+ const mutableRef = useRef12({
3071
+ lastSnapshot: null,
3072
+ rebuildGen: 0
3073
+ });
3074
+ useEffect22(() => {
3075
+ const { lastSnapshot } = mutableRef.current;
3076
+ if (!ctx || !entityRef.current || lastSnapshot === null) return;
3077
+ const geometryChanged = !shallowEqualObject(
3078
+ lastSnapshot.geometryOptions,
3079
+ geometryOptions
3080
+ );
3081
+ const materialsChanged = !shallowEqualArray(
3082
+ lastSnapshot.materials,
3083
+ materials
3084
+ );
3085
+ if (!geometryChanged && !materialsChanged) return;
3086
+ mutableRef.current.lastSnapshot = { geometryOptions, materials };
3087
+ mutableRef.current.rebuildGen += 1;
3088
+ const gen = mutableRef.current.rebuildGen;
3089
+ const rebuild = async () => {
3090
+ const entity = entityRef.current;
3091
+ if (!entity) return;
3092
+ try {
3093
+ const oldComponent = componentRef.current;
3094
+ if (oldComponent) {
3095
+ await entity.removeComponent(oldComponent);
3096
+ await oldComponent.destroy();
3097
+ componentRef.current = null;
3098
+ if (gen !== mutableRef.current.rebuildGen) return;
3099
+ }
3100
+ const geometry = await createGeometry(geometryOptions);
3101
+ if (gen !== mutableRef.current.rebuildGen) {
3102
+ await geometry.destroy();
3103
+ return;
3104
+ }
3105
+ const materialList = await Promise.all(
3106
+ materials?.map((mid) => ctx.resourceRegistry.get(mid)).filter(Boolean) ?? []
3107
+ );
3108
+ if (gen !== mutableRef.current.rebuildGen) {
3109
+ await geometry.destroy();
3110
+ return;
3111
+ }
3112
+ const modelComponent = await ctx.session.createModelComponent({
3113
+ mesh: geometry,
3114
+ materials: materialList
3115
+ });
3116
+ if (gen !== mutableRef.current.rebuildGen) {
3117
+ await modelComponent.destroy();
3118
+ await geometry.destroy();
3119
+ return;
3120
+ }
3121
+ await entity.addComponent(modelComponent);
3122
+ componentRef.current = modelComponent;
3123
+ } catch (error) {
3124
+ if (gen === mutableRef.current.rebuildGen) {
3125
+ console.error("GeometryEntity: rebuild failed", error);
3126
+ }
3127
+ }
3128
+ };
3129
+ rebuild();
3130
+ }, [ctx, geometryOptions, materials, createGeometry]);
2750
3131
  return /* @__PURE__ */ jsx14(
2751
3132
  BaseEntity,
2752
3133
  {
2753
3134
  ...rest,
2754
3135
  id,
2755
3136
  ref,
2756
- createEntity: async (ctx, signal) => {
3137
+ createEntity: async (ctx2, signal) => {
2757
3138
  const manager = new AbortResourceManager(signal);
2758
3139
  try {
2759
3140
  const ent = await manager.addResource(
2760
- () => ctx.session.createEntity({ id, name })
3141
+ () => ctx2.session.createEntity({ id, name })
2761
3142
  );
2762
3143
  const geometry = await manager.addResource(
2763
3144
  () => createGeometry(geometryOptions)
2764
3145
  );
2765
3146
  const materialList = await Promise.all(
2766
- materials?.map((id2) => ctx.resourceRegistry.get(id2)).filter(Boolean) ?? []
3147
+ materials?.map((id2) => ctx2.resourceRegistry.get(id2)).filter(Boolean) ?? []
2767
3148
  );
2768
3149
  const modelComponent = await manager.addResource(
2769
- () => ctx.session.createModelComponent({
3150
+ () => ctx2.session.createModelComponent({
2770
3151
  mesh: geometry,
2771
3152
  materials: materialList
2772
3153
  })
2773
3154
  );
2774
3155
  await ent.addComponent(modelComponent);
3156
+ entityRef.current = ent;
3157
+ componentRef.current = modelComponent;
3158
+ mutableRef.current.lastSnapshot = { geometryOptions, materials };
2775
3159
  return ent;
2776
3160
  } catch (error) {
2777
3161
  await manager.dispose();
@@ -2809,14 +3193,15 @@ var BoxEntity = forwardRef13(
2809
3193
  );
2810
3194
 
2811
3195
  // src/reality/components/UnlitMaterial.tsx
2812
- import { useEffect as useEffect22, useRef as useRef12 } from "react";
3196
+ import { useEffect as useEffect23, useRef as useRef13 } from "react";
2813
3197
  var UnlitMaterial = ({
2814
3198
  children,
2815
3199
  ...options
2816
3200
  }) => {
2817
3201
  const ctx = useRealityContext();
2818
- const materialRef = useRef12();
2819
- useEffect22(() => {
3202
+ const materialRef = useRef13();
3203
+ const isInitializedRef = useRef13(false);
3204
+ useEffect23(() => {
2820
3205
  if (!ctx) return;
2821
3206
  const { session, reality, resourceRegistry } = ctx;
2822
3207
  const init = async () => {
@@ -2825,6 +3210,7 @@ var UnlitMaterial = ({
2825
3210
  try {
2826
3211
  const mat = await materialPromise;
2827
3212
  materialRef.current = mat;
3213
+ isInitializedRef.current = true;
2828
3214
  } catch (error) {
2829
3215
  console.error(" ~ UnlitMaterial ~ error:", error);
2830
3216
  }
@@ -2832,8 +3218,21 @@ var UnlitMaterial = ({
2832
3218
  init();
2833
3219
  return () => {
2834
3220
  resourceRegistry.removeAndDestroy(options.id);
3221
+ materialRef.current = void 0;
3222
+ isInitializedRef.current = false;
2835
3223
  };
2836
3224
  }, [ctx]);
3225
+ useEffect23(() => {
3226
+ if (!isInitializedRef.current || !materialRef.current) return;
3227
+ const updates = {};
3228
+ if (options.color !== void 0) updates.color = options.color;
3229
+ if (options.transparent !== void 0)
3230
+ updates.transparent = options.transparent;
3231
+ if (options.opacity !== void 0) updates.opacity = options.opacity;
3232
+ if (Object.keys(updates).length > 0) {
3233
+ materialRef.current.updateProperties(updates);
3234
+ }
3235
+ }, [options.color, options.transparent, options.opacity]);
2837
3236
  return null;
2838
3237
  };
2839
3238
 
@@ -2932,7 +3331,7 @@ var SceneGraph = ({ children }) => {
2932
3331
  };
2933
3332
 
2934
3333
  // src/reality/components/ModelAsset.tsx
2935
- import { useEffect as useEffect23, useRef as useRef13 } from "react";
3334
+ import { useEffect as useEffect24, useRef as useRef14 } from "react";
2936
3335
  var resolveAssetUrl = (url) => {
2937
3336
  if (url.startsWith("http://") || url.startsWith("https://")) {
2938
3337
  return url;
@@ -2941,8 +3340,8 @@ var resolveAssetUrl = (url) => {
2941
3340
  };
2942
3341
  var ModelAsset = ({ children, ...options }) => {
2943
3342
  const ctx = useRealityContext();
2944
- const materialRef = useRef13();
2945
- useEffect23(() => {
3343
+ const materialRef = useRef14();
3344
+ useEffect24(() => {
2946
3345
  const controller = new AbortController();
2947
3346
  if (!ctx) return;
2948
3347
  const { session, reality, resourceRegistry } = ctx;
@@ -2972,29 +3371,66 @@ var ModelAsset = ({ children, ...options }) => {
2972
3371
  };
2973
3372
 
2974
3373
  // src/reality/components/ModelEntity.tsx
2975
- import { forwardRef as forwardRef18 } from "react";
3374
+ import { forwardRef as forwardRef18, useEffect as useEffect25, useRef as useRef15 } from "react";
2976
3375
  import { jsx as jsx21 } from "react/jsx-runtime";
2977
3376
  var ModelEntity = forwardRef18(
2978
- ({ id, model, children, name, ...rest }, ref) => {
3377
+ ({ id, model, children, name, materials, ...rest }, ref) => {
3378
+ const ctx = useRealityContext();
3379
+ const entityRef = useRef15(null);
3380
+ const lastMaterialsRef = useRef15(void 0);
3381
+ useEffect25(() => {
3382
+ if (!ctx || !entityRef.current) return;
3383
+ const next = materials ?? [];
3384
+ const prev = lastMaterialsRef.current ?? [];
3385
+ if (shallowEqualArray(prev, next)) return;
3386
+ lastMaterialsRef.current = next;
3387
+ const apply = async () => {
3388
+ try {
3389
+ const materialList = (await Promise.all(
3390
+ next.map((mid) => ctx.resourceRegistry.get(mid))
3391
+ )).filter(Boolean);
3392
+ if (entityRef.current) {
3393
+ await entityRef.current.setMaterials(materialList);
3394
+ }
3395
+ } catch (error) {
3396
+ console.error("ModelEntity: failed to set materials", error);
3397
+ }
3398
+ };
3399
+ apply();
3400
+ }, [ctx, materials]);
2979
3401
  return /* @__PURE__ */ jsx21(
2980
3402
  BaseEntity,
2981
3403
  {
2982
3404
  ...rest,
2983
3405
  id,
2984
3406
  ref,
2985
- createEntity: async (ctx, signal) => {
3407
+ recreateKey: model,
3408
+ createEntity: async (ctx2, signal) => {
2986
3409
  try {
2987
- const modelAsset = await ctx.resourceRegistry.get(model);
3410
+ const modelAsset = await ctx2.resourceRegistry.get(model);
2988
3411
  if (!modelAsset)
2989
3412
  throw new Error(`ModelEntity: model not found ${model}`);
2990
3413
  if (signal.aborted) return null;
2991
- return ctx.session.createSpatialModelEntity(
3414
+ const ent = await ctx2.session.createSpatialModelEntity(
2992
3415
  {
2993
3416
  modelAssetId: modelAsset.id,
2994
3417
  name
2995
3418
  },
2996
3419
  { id, name }
2997
3420
  );
3421
+ entityRef.current = ent;
3422
+ if (materials && materials.length > 0) {
3423
+ const materialList = (await Promise.all(
3424
+ materials.map(
3425
+ (mid) => ctx2.resourceRegistry.get(mid)
3426
+ )
3427
+ )).filter(Boolean);
3428
+ if (materialList.length > 0 && !signal.aborted) {
3429
+ await ent.setMaterials(materialList);
3430
+ }
3431
+ }
3432
+ lastMaterialsRef.current = materials ?? [];
3433
+ return ent;
2998
3434
  } catch (error) {
2999
3435
  return null;
3000
3436
  }
@@ -3008,9 +3444,9 @@ var ModelEntity = forwardRef18(
3008
3444
  // src/reality/components/Reality.tsx
3009
3445
  import {
3010
3446
  forwardRef as forwardRef19,
3011
- useCallback as useCallback8,
3012
- useEffect as useEffect24,
3013
- useRef as useRef14,
3447
+ useCallback as useCallback9,
3448
+ useEffect as useEffect26,
3449
+ useRef as useRef16,
3014
3450
  useState as useState8
3015
3451
  } from "react";
3016
3452
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -3034,23 +3470,23 @@ var Reality = forwardRef19(
3034
3470
  onSpatialMagnifyEnd,
3035
3471
  ...props
3036
3472
  } = inProps;
3037
- const ctxRef = useRef14(null);
3038
- const creationId = useRef14(0);
3473
+ const ctxRef = useRef16(null);
3474
+ const creationId = useRef16(0);
3039
3475
  const [isReady, setIsReady] = useState8(false);
3040
- const cleanupReality = useCallback8(() => {
3476
+ const cleanupReality = useCallback9(() => {
3041
3477
  ctxRef.current?.attachmentRegistry.destroy();
3042
3478
  ctxRef.current?.resourceRegistry.destroy();
3043
3479
  ctxRef.current?.reality.destroy();
3044
3480
  ctxRef.current = null;
3045
3481
  setIsReady(false);
3046
3482
  }, []);
3047
- useEffect24(() => {
3483
+ useEffect26(() => {
3048
3484
  return () => {
3049
3485
  creationId.current++;
3050
3486
  cleanupReality();
3051
3487
  };
3052
3488
  }, [cleanupReality]);
3053
- const createReality = useCallback8(async () => {
3489
+ const createReality = useCallback9(async () => {
3054
3490
  const id = ++creationId.current;
3055
3491
  const resourceRegistry = new ResourceRegistry();
3056
3492
  const attachmentRegistry = new AttachmentRegistry();
@@ -3093,7 +3529,7 @@ var Reality = forwardRef19(
3093
3529
  return null;
3094
3530
  }
3095
3531
  }, [cleanupReality]);
3096
- const content = useCallback8(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
3532
+ const content = useCallback9(() => /* @__PURE__ */ jsx22(Fragment3, {}), []);
3097
3533
  useRealityEvents({
3098
3534
  instance: ctxRef.current?.reality ?? null,
3099
3535
  onSpatialTap,
@@ -3122,7 +3558,7 @@ var Reality = forwardRef19(
3122
3558
  );
3123
3559
 
3124
3560
  // src/reality/components/AttachmentAsset.tsx
3125
- import { useEffect as useEffect25, useState as useState9 } from "react";
3561
+ import { useEffect as useEffect27, useState as useState9 } from "react";
3126
3562
  import { createPortal as createPortal3 } from "react-dom";
3127
3563
  import { jsx as jsx23 } from "react/jsx-runtime";
3128
3564
  var AttachmentAsset = ({
@@ -3131,7 +3567,7 @@ var AttachmentAsset = ({
3131
3567
  }) => {
3132
3568
  const ctx = useRealityContext();
3133
3569
  const [containers, setContainers] = useState9([]);
3134
- useEffect25(() => {
3570
+ useEffect27(() => {
3135
3571
  if (!ctx) return;
3136
3572
  return ctx.attachmentRegistry.onContainersChange(name, setContainers);
3137
3573
  }, [ctx, name]);
@@ -3142,7 +3578,7 @@ var AttachmentAsset = ({
3142
3578
  };
3143
3579
 
3144
3580
  // src/reality/components/AttachmentEntity.tsx
3145
- import { useEffect as useEffect26, useRef as useRef15, useState as useState10 } from "react";
3581
+ import { useEffect as useEffect28, useRef as useRef17, useState as useState10 } from "react";
3146
3582
  var instanceCounter = 0;
3147
3583
  var AttachmentEntity = ({
3148
3584
  attachment: attachmentName,
@@ -3151,12 +3587,12 @@ var AttachmentEntity = ({
3151
3587
  }) => {
3152
3588
  const ctx = useRealityContext();
3153
3589
  const parent = useParentContext();
3154
- const attachmentRef = useRef15(null);
3155
- const parentIdRef = useRef15(null);
3156
- const instanceIdRef = useRef15(`att_${++instanceCounter}`);
3157
- const attachmentNameRef = useRef15(attachmentName);
3590
+ const attachmentRef = useRef17(null);
3591
+ const parentIdRef = useRef17(null);
3592
+ const instanceIdRef = useRef17(`att_${++instanceCounter}`);
3593
+ const attachmentNameRef = useRef17(attachmentName);
3158
3594
  const [childWindow, setChildWindow] = useState10(null);
3159
- useEffect26(() => {
3595
+ useEffect28(() => {
3160
3596
  if (!ctx || !parent) return;
3161
3597
  if (attachmentRef.current) return;
3162
3598
  const parentId = parent.id;
@@ -3218,7 +3654,7 @@ var AttachmentEntity = ({
3218
3654
  }
3219
3655
  };
3220
3656
  }, [ctx, parent]);
3221
- useEffect26(() => {
3657
+ useEffect28(() => {
3222
3658
  if (!ctx) return;
3223
3659
  const att = attachmentRef.current;
3224
3660
  const prevName = attachmentNameRef.current;
@@ -3235,7 +3671,7 @@ var AttachmentEntity = ({
3235
3671
  }
3236
3672
  }, [ctx, attachmentName]);
3237
3673
  useSyncHeadStyles(childWindow, { subtree: false });
3238
- useEffect26(() => {
3674
+ useEffect28(() => {
3239
3675
  if (!attachmentRef.current) return;
3240
3676
  attachmentRef.current.update({ position, size });
3241
3677
  }, [position?.[0], position?.[1], position?.[2], size?.width, size?.height]);
@@ -3371,7 +3807,7 @@ async function convertCoordinate(position, { from, to }) {
3371
3807
  }
3372
3808
 
3373
3809
  // src/index.ts
3374
- var version = "1.4.0";
3810
+ var version = "1.6.0";
3375
3811
  if (typeof window !== "undefined") {
3376
3812
  initPolyfill();
3377
3813
  }