@webspatial/react-sdk 1.2.0 → 1.3.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,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.2.0"
5
+ window.__webspatialsdk__['react-sdk-version'] = "1.3.0"
6
6
  window.__webspatialsdk__['XR_ENV'] = "web"
7
7
  })()
8
8
 
@@ -849,7 +849,7 @@ var TransformVisibilityTaskContainer = forwardRef2(
849
849
  );
850
850
 
851
851
  // src/spatialized-container/SpatializedContainer.tsx
852
- import { forwardRef as forwardRef4, useContext as useContext6, useEffect as useEffect10, useMemo as useMemo2 } from "react";
852
+ import { forwardRef as forwardRef4, useContext as useContext7, useEffect as useEffect10, useMemo as useMemo2 } from "react";
853
853
 
854
854
  // src/noRuntime.ts
855
855
  var Spatial = class {
@@ -1205,12 +1205,12 @@ function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObj
1205
1205
 
1206
1206
  // src/spatialized-container/hooks/useSpatializedElement.ts
1207
1207
  import { useEffect as useEffect6, useState as useState3 } from "react";
1208
- function useSpatializedElement(createSpatializedElement3, portalInstanceObject) {
1208
+ function useSpatializedElement(createSpatializedElement2, portalInstanceObject) {
1209
1209
  const [spatializedElement, setSpatializedElement] = useState3();
1210
1210
  useEffect6(() => {
1211
1211
  let isDestroyed = false;
1212
1212
  let spatializedElement2;
1213
- createSpatializedElement3().then(
1213
+ createSpatializedElement2().then(
1214
1214
  (inSpatializedElement) => {
1215
1215
  if (!isDestroyed) {
1216
1216
  spatializedElement2 = inSpatializedElement;
@@ -1228,7 +1228,7 @@ function useSpatializedElement(createSpatializedElement3, portalInstanceObject)
1228
1228
  spatializedElement2 = void 0;
1229
1229
  }
1230
1230
  };
1231
- }, [createSpatializedElement3, portalInstanceObject]);
1231
+ }, [createSpatializedElement2, portalInstanceObject]);
1232
1232
  return spatializedElement;
1233
1233
  }
1234
1234
 
@@ -1262,7 +1262,7 @@ function renderPlaceholderInSubPortal(portalInstanceObject, El) {
1262
1262
  function PortalSpatializedContainer(props) {
1263
1263
  const {
1264
1264
  spatializedContent: Content,
1265
- createSpatializedElement: createSpatializedElement3,
1265
+ createSpatializedElement: createSpatializedElement2,
1266
1266
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1267
1267
  onSpatialTap,
1268
1268
  onSpatialDragStart,
@@ -1296,7 +1296,7 @@ function PortalSpatializedContainer(props) {
1296
1296
  }, []);
1297
1297
  useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObject);
1298
1298
  const spatializedElement = useSpatializedElement(
1299
- createSpatializedElement3,
1299
+ createSpatializedElement2,
1300
1300
  portalInstanceObject
1301
1301
  );
1302
1302
  const PlaceholderEl = renderPlaceholderInSubPortal(
@@ -1349,8 +1349,13 @@ function PortalSpatializedContainer(props) {
1349
1349
  ] });
1350
1350
  }
1351
1351
 
1352
+ // src/reality/context/InsideAttachmentContext.tsx
1353
+ import { createContext as createContext4, useContext as useContext5 } from "react";
1354
+ var InsideAttachmentContext = createContext4(false);
1355
+ var useInsideAttachment = () => useContext5(InsideAttachmentContext);
1356
+
1352
1357
  // src/spatialized-container/hooks/useSpatialEvents.ts
1353
- function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter) {
1358
+ function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter, clientXGetter, clientYGetter, clientZGetter, translationXGetter, translationYGetter, translationZGetter, quaternionGetter, magnificationGetter) {
1354
1359
  return new Proxy(event, {
1355
1360
  get(target, prop) {
1356
1361
  if (prop === "currentTarget") {
@@ -1371,18 +1376,50 @@ function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGett
1371
1376
  if (prop === "offsetZ" && offsetZGetter) {
1372
1377
  return offsetZGetter(target) ?? 0;
1373
1378
  }
1379
+ if (prop === "clientX" && clientXGetter) {
1380
+ return clientXGetter(target) ?? 0;
1381
+ }
1382
+ if (prop === "clientY" && clientYGetter) {
1383
+ return clientYGetter(target) ?? 0;
1384
+ }
1385
+ if (prop === "clientZ" && clientZGetter) {
1386
+ return clientZGetter(target) ?? 0;
1387
+ }
1388
+ if (prop === "translationX" && translationXGetter) {
1389
+ return translationXGetter(target) ?? 0;
1390
+ }
1391
+ if (prop === "translationY" && translationYGetter) {
1392
+ return translationYGetter(target) ?? 0;
1393
+ }
1394
+ if (prop === "translationZ" && translationZGetter) {
1395
+ return translationZGetter(target) ?? 0;
1396
+ }
1397
+ if (prop === "quaternion" && quaternionGetter) {
1398
+ return quaternionGetter(target) ?? { x: 0, y: 0, z: 0, w: 1 };
1399
+ }
1400
+ if (prop === "magnification" && magnificationGetter) {
1401
+ return magnificationGetter(target) ?? 1;
1402
+ }
1374
1403
  return Reflect.get(target, prop);
1375
1404
  }
1376
1405
  });
1377
1406
  }
1378
- function createEventHandler(handler, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter) {
1407
+ function createEventHandler(handler, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter, clientXGetter, clientYGetter, clientZGetter, translationXGetter, translationYGetter, translationZGetter, quaternionGetter, magnificationGetter) {
1379
1408
  return handler ? (event) => {
1380
1409
  const proxyEvent = createEventProxy(
1381
1410
  event,
1382
1411
  currentTargetGetter,
1383
1412
  offsetXGetter,
1384
1413
  offsetYGetter,
1385
- offsetZGetter
1414
+ offsetZGetter,
1415
+ clientXGetter,
1416
+ clientYGetter,
1417
+ clientZGetter,
1418
+ translationXGetter,
1419
+ translationYGetter,
1420
+ translationZGetter,
1421
+ quaternionGetter,
1422
+ magnificationGetter
1386
1423
  );
1387
1424
  handler(proxyEvent);
1388
1425
  } : void 0;
@@ -1391,13 +1428,27 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1391
1428
  const onSpatialTap = createEventHandler(
1392
1429
  spatialEvents.onSpatialTap,
1393
1430
  currentTargetGetter,
1431
+ // offsetX/Y/Z come from local coordinates
1394
1432
  (ev) => ev.detail?.location3D?.x,
1395
1433
  (ev) => ev.detail?.location3D?.y,
1396
- (ev) => ev.detail?.location3D?.z
1434
+ (ev) => ev.detail?.location3D?.z,
1435
+ // clientX/Y/Z come from global scene coordinates
1436
+ (ev) => ev.detail?.globalLocation3D?.x,
1437
+ (ev) => ev.detail?.globalLocation3D?.y,
1438
+ (ev) => ev.detail?.globalLocation3D?.z
1397
1439
  );
1398
1440
  const onSpatialDrag = createEventHandler(
1399
1441
  spatialEvents.onSpatialDrag,
1400
- currentTargetGetter
1442
+ currentTargetGetter,
1443
+ void 0,
1444
+ void 0,
1445
+ void 0,
1446
+ void 0,
1447
+ void 0,
1448
+ void 0,
1449
+ (ev) => ev.detail?.translation3D?.x,
1450
+ (ev) => ev.detail?.translation3D?.y,
1451
+ (ev) => ev.detail?.translation3D?.z
1401
1452
  );
1402
1453
  const onSpatialDragEnd = createEventHandler(
1403
1454
  spatialEvents.onSpatialDragEnd,
@@ -1405,7 +1456,17 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1405
1456
  );
1406
1457
  const onSpatialRotate = createEventHandler(
1407
1458
  spatialEvents.onSpatialRotate,
1408
- currentTargetGetter
1459
+ currentTargetGetter,
1460
+ void 0,
1461
+ void 0,
1462
+ void 0,
1463
+ void 0,
1464
+ void 0,
1465
+ void 0,
1466
+ void 0,
1467
+ void 0,
1468
+ void 0,
1469
+ (ev) => ev.detail?.quaternion
1409
1470
  );
1410
1471
  const onSpatialRotateEnd = createEventHandler(
1411
1472
  spatialEvents.onSpatialRotateEnd,
@@ -1413,7 +1474,18 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1413
1474
  );
1414
1475
  const onSpatialMagnify = createEventHandler(
1415
1476
  spatialEvents.onSpatialMagnify,
1416
- currentTargetGetter
1477
+ currentTargetGetter,
1478
+ void 0,
1479
+ void 0,
1480
+ void 0,
1481
+ void 0,
1482
+ void 0,
1483
+ void 0,
1484
+ void 0,
1485
+ void 0,
1486
+ void 0,
1487
+ void 0,
1488
+ (ev) => ev.detail?.magnification
1417
1489
  );
1418
1490
  const onSpatialMagnifyEnd = createEventHandler(
1419
1491
  spatialEvents.onSpatialMagnifyEnd,
@@ -1424,7 +1496,10 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1424
1496
  currentTargetGetter,
1425
1497
  (ev) => ev.detail?.startLocation3D?.x,
1426
1498
  (ev) => ev.detail?.startLocation3D?.y,
1427
- (ev) => ev.detail?.startLocation3D?.z
1499
+ (ev) => ev.detail?.startLocation3D?.z,
1500
+ (ev) => ev.detail?.globalLocation3D?.x,
1501
+ (ev) => ev.detail?.globalLocation3D?.y,
1502
+ (ev) => ev.detail?.globalLocation3D?.z
1428
1503
  );
1429
1504
  return {
1430
1505
  onSpatialTap,
@@ -1453,9 +1528,9 @@ function useSpatialEventsWhenSpatializedContainerExist(spatialEvents, spatialId,
1453
1528
  }
1454
1529
 
1455
1530
  // src/ssr/SSRContext.tsx
1456
- import { createContext as createContext4, useState as useState4, useEffect as useEffect8 } from "react";
1531
+ import { createContext as createContext5, useState as useState4, useEffect as useEffect8 } from "react";
1457
1532
  import { jsx as jsx4 } from "react/jsx-runtime";
1458
- var SSRContext = createContext4(false);
1533
+ var SSRContext = createContext5(false);
1459
1534
  var SSRProvider = ({
1460
1535
  isSSR: initialIsSSR = true,
1461
1536
  children
@@ -1473,9 +1548,9 @@ var SSRProvider = ({
1473
1548
  import { forwardRef as forwardRef3 } from "react";
1474
1549
 
1475
1550
  // src/ssr/useSSRPhase.tsx
1476
- import { useContext as useContext5, useState as useState5, useEffect as useEffect9 } from "react";
1551
+ import { useContext as useContext6, useState as useState5, useEffect as useEffect9 } from "react";
1477
1552
  function useSSRPhase() {
1478
- const isSSRContext = useContext5(SSRContext);
1553
+ const isSSRContext = useContext6(SSRContext);
1479
1554
  const isServer = typeof window === "undefined";
1480
1555
  const [hydrated, setHydrated] = useState5(false);
1481
1556
  useEffect9(() => setHydrated(true), []);
@@ -1511,33 +1586,48 @@ function withSSRSupported(Component) {
1511
1586
 
1512
1587
  // src/spatialized-container/SpatializedContainer.tsx
1513
1588
  import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1589
+ function DegradedContainer({
1590
+ innerRef,
1591
+ ...inprops
1592
+ }) {
1593
+ const {
1594
+ component: Component,
1595
+ children,
1596
+ ["enable-xr"]: _enableXR,
1597
+ onSpatialTap: _onSpatialTap,
1598
+ onSpatialDragStart: _onSpatialDragStart,
1599
+ onSpatialDrag: _onSpatialDrag,
1600
+ onSpatialDragEnd: _onSpatialDragEnd,
1601
+ onSpatialRotate: _onSpatialRotate,
1602
+ onSpatialRotateEnd: _onSpatialRotateEnd,
1603
+ onSpatialMagnify: _onSpatialMagnify,
1604
+ onSpatialMagnifyEnd: _onSpatialMagnifyEnd,
1605
+ spatializedContent: _content,
1606
+ createSpatializedElement: _create,
1607
+ getExtraSpatializedElementProperties: _getExtra,
1608
+ extraRefProps: _extraRef,
1609
+ sizingMode: _sizingMode,
1610
+ ...restProps
1611
+ } = inprops;
1612
+ return /* @__PURE__ */ jsx6(Component, { ref: innerRef, ...restProps, children });
1613
+ }
1514
1614
  function SpatializedContainerBase(inprops, ref) {
1515
1615
  const isWebSpatialEnv = getSession() !== null;
1516
- if (!isWebSpatialEnv) {
1517
- const {
1518
- component: Component,
1519
- spatializedContent,
1520
- createSpatializedElement: createSpatializedElement3,
1521
- getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1522
- onSpatialTap: onSpatialTap2,
1523
- onSpatialDragStart: onSpatialDragStart2,
1524
- onSpatialDrag: onSpatialDrag2,
1525
- onSpatialDragEnd: onSpatialDragEnd2,
1526
- onSpatialRotate: onSpatialRotate2,
1527
- onSpatialRotateEnd: onSpatialRotateEnd2,
1528
- onSpatialMagnify: onSpatialMagnify2,
1529
- onSpatialMagnifyEnd: onSpatialMagnifyEnd2,
1530
- extraRefProps: extraRefProps2,
1531
- ...restProps
1532
- } = inprops;
1533
- return /* @__PURE__ */ jsx6(Component, { ref, ...restProps });
1616
+ const insideAttachment = useInsideAttachment();
1617
+ if (!isWebSpatialEnv || insideAttachment) {
1618
+ if (insideAttachment) {
1619
+ console.warn(
1620
+ `[WebSpatial] ${inprops.component || "Spatial element"} cannot be used inside AttachmentAsset. Rendering as plain HTML.`
1621
+ );
1622
+ }
1623
+ return /* @__PURE__ */ jsx6(DegradedContainer, { ...inprops, innerRef: ref });
1534
1624
  }
1535
- const layer = useContext6(SpatialLayerContext) + 1;
1536
- const rootSpatializedContainerObject = useContext6(
1625
+ const layer = useContext7(SpatialLayerContext) + 1;
1626
+ const rootSpatializedContainerObject = useContext7(
1537
1627
  SpatializedContainerContext
1538
1628
  );
1539
1629
  const inSpatializedContainer = !!rootSpatializedContainerObject;
1540
- const portalInstanceObject = useContext6(PortalInstanceContext);
1630
+ const portalInstanceObject = useContext7(PortalInstanceContext);
1541
1631
  const inPortalInstanceEnv = !!portalInstanceObject;
1542
1632
  const isInStandardInstance = !inPortalInstanceEnv;
1543
1633
  const spatialId = useMemo2(() => {
@@ -1596,7 +1686,7 @@ function SpatializedContainerBase(inprops, ref) {
1596
1686
  }, [spatialContainerRefProxy.current]);
1597
1687
  const {
1598
1688
  spatializedContent,
1599
- createSpatializedElement: createSpatializedElement3,
1689
+ createSpatializedElement: createSpatializedElement2,
1600
1690
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1601
1691
  ...restProps
1602
1692
  } = props;
@@ -1646,7 +1736,7 @@ function SpatializedContainerBase(inprops, ref) {
1646
1736
  );
1647
1737
  const {
1648
1738
  spatializedContent,
1649
- createSpatializedElement: createSpatializedElement3,
1739
+ createSpatializedElement: createSpatializedElement2,
1650
1740
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1651
1741
  ...restProps
1652
1742
  } = props;
@@ -1691,28 +1781,47 @@ var SpatializedContainer = withSSRSupported(
1691
1781
  );
1692
1782
 
1693
1783
  // src/spatialized-container/Spatialized2DElementContainer.tsx
1784
+ import { createPortal as createPortal2 } from "react-dom";
1694
1785
  import {
1695
1786
  forwardRef as forwardRef5,
1696
- useContext as useContext7,
1697
- useEffect as useEffect11
1787
+ useContext as useContext8,
1788
+ useEffect as useEffect12
1698
1789
  } from "react";
1699
- import { createPortal as createPortal2 } from "react-dom";
1700
- import { jsx as jsx7 } from "react/jsx-runtime";
1701
- function asyncLoadStyleToChildWindow(childWindow, n) {
1790
+
1791
+ // src/utils/windowStyleSync.ts
1792
+ function asyncLoadStyleToChildWindow(childWindow, link, isCurrent) {
1702
1793
  return new Promise((resolve) => {
1703
- n.href += "?uniqueURL=" + Math.random();
1704
- n.onerror = function(error) {
1705
- console.error("Failed to load style link", n.href);
1706
- resolve(false);
1794
+ const { href } = link;
1795
+ const sep = href.includes("?") ? "&" : "?";
1796
+ link.href = `${href}${sep}uniqueURL=${Math.random()}`;
1797
+ let finished = false;
1798
+ const finish = (ok) => {
1799
+ if (finished) return;
1800
+ finished = true;
1801
+ resolve(ok);
1802
+ };
1803
+ link.onerror = () => {
1804
+ finish(false);
1707
1805
  };
1708
- n.onload = function() {
1709
- resolve(true);
1806
+ link.onload = () => {
1807
+ if (!isCurrent()) {
1808
+ link.parentNode?.removeChild(link);
1809
+ finish(false);
1810
+ return;
1811
+ }
1812
+ finish(true);
1710
1813
  };
1711
1814
  setTimeout(() => {
1712
- childWindow.document.head.appendChild(n);
1815
+ if (!isCurrent()) {
1816
+ finish(false);
1817
+ return;
1818
+ }
1819
+ childWindow.document.head.appendChild(link);
1713
1820
  }, 50);
1714
1821
  });
1715
1822
  }
1823
+ var WEBSPATIAL_SYNC_ATTR = "data-webspatial-sync";
1824
+ var WEBSPATIAL_SYNC_KEY_ATTR = "data-webspatial-sync-key";
1716
1825
  function setOpenWindowStyle(openedWindow) {
1717
1826
  openedWindow.document.documentElement.style.cssText += document.documentElement.style.cssText;
1718
1827
  openedWindow.document.documentElement.style.backgroundColor = "transparent";
@@ -1724,23 +1833,118 @@ function setOpenWindowStyle(openedWindow) {
1724
1833
  openedWindow.document.body.style.minWidth = "fit-content";
1725
1834
  openedWindow.document.body.style.background = "transparent";
1726
1835
  }
1836
+ var controllers = /* @__PURE__ */ new WeakMap();
1837
+ function getController(childWindow) {
1838
+ const prev = controllers.get(childWindow);
1839
+ if (prev) return prev;
1840
+ const next = { version: 0 };
1841
+ controllers.set(childWindow, next);
1842
+ return next;
1843
+ }
1727
1844
  async function syncParentHeadToChild(childWindow) {
1845
+ const controller = getController(childWindow);
1846
+ const version2 = ++controller.version;
1728
1847
  const styleLoadedPromises = [];
1729
- for (let i = 0; i < document.head.children.length; i++) {
1730
- let n = document.head.children[i].cloneNode(true);
1731
- if (n.nodeName == "LINK" && n.rel == "stylesheet" && n.href) {
1732
- const promise = asyncLoadStyleToChildWindow(
1733
- childWindow,
1734
- n
1735
- );
1736
- styleLoadedPromises.push(promise);
1737
- } else {
1738
- childWindow.document.head.appendChild(n);
1739
- }
1848
+ const { head } = childWindow.document;
1849
+ const isCurrent = () => controller.version === version2;
1850
+ const parentStyles = Array.from(document.head.querySelectorAll("style"));
1851
+ const parentStylesheets = Array.from(
1852
+ document.head.querySelectorAll('link[rel="stylesheet"][href]')
1853
+ );
1854
+ const desiredStylesheetKeys = /* @__PURE__ */ new Set();
1855
+ for (const link of parentStylesheets) {
1856
+ if (link.href) desiredStylesheetKeys.add(link.href);
1857
+ }
1858
+ const existingSyncedLinks = Array.from(
1859
+ head.querySelectorAll(
1860
+ `link[rel="stylesheet"][${WEBSPATIAL_SYNC_ATTR}="1"]`
1861
+ )
1862
+ );
1863
+ for (const link of existingSyncedLinks) {
1864
+ const key = link.getAttribute(WEBSPATIAL_SYNC_KEY_ATTR) ?? link.href;
1865
+ if (!desiredStylesheetKeys.has(key)) link.parentNode?.removeChild(link);
1866
+ }
1867
+ const prevSyncedStyles = head.querySelectorAll(
1868
+ `style[${WEBSPATIAL_SYNC_ATTR}="1"]`
1869
+ );
1870
+ prevSyncedStyles.forEach((n) => n.parentNode?.removeChild(n));
1871
+ for (const styleEl of parentStyles) {
1872
+ const node = styleEl.cloneNode(true);
1873
+ node.setAttribute(WEBSPATIAL_SYNC_ATTR, "1");
1874
+ head.appendChild(node);
1875
+ }
1876
+ const currentKeys = /* @__PURE__ */ new Set();
1877
+ const currentSyncedLinks = Array.from(
1878
+ head.querySelectorAll(
1879
+ `link[rel="stylesheet"][${WEBSPATIAL_SYNC_ATTR}="1"]`
1880
+ )
1881
+ );
1882
+ for (const link of currentSyncedLinks) {
1883
+ currentKeys.add(link.getAttribute(WEBSPATIAL_SYNC_KEY_ATTR) ?? link.href);
1884
+ }
1885
+ for (const link of parentStylesheets) {
1886
+ const key = link.href;
1887
+ if (!key || currentKeys.has(key)) continue;
1888
+ const node = link.cloneNode(true);
1889
+ node.setAttribute(WEBSPATIAL_SYNC_ATTR, "1");
1890
+ node.setAttribute(WEBSPATIAL_SYNC_KEY_ATTR, key);
1891
+ styleLoadedPromises.push(
1892
+ asyncLoadStyleToChildWindow(childWindow, node, isCurrent)
1893
+ );
1740
1894
  }
1741
1895
  childWindow.document.documentElement.className = document.documentElement.className;
1742
1896
  return Promise.all(styleLoadedPromises);
1743
1897
  }
1898
+
1899
+ // src/utils/useSyncHeadStyles.ts
1900
+ import { useEffect as useEffect11 } from "react";
1901
+ function defaultShouldSync(mutations) {
1902
+ if (!Array.isArray(mutations) || mutations.length === 0) return false;
1903
+ for (const mutation of mutations) {
1904
+ const nodes = [
1905
+ ...Array.from(mutation.addedNodes),
1906
+ ...Array.from(mutation.removedNodes)
1907
+ ];
1908
+ for (const node of nodes) {
1909
+ if (!(node instanceof Element)) continue;
1910
+ const tag = node.tagName;
1911
+ if (tag === "STYLE") return true;
1912
+ if (tag === "LINK") {
1913
+ const { rel } = node;
1914
+ if (rel && rel.toLowerCase() === "stylesheet") return true;
1915
+ }
1916
+ }
1917
+ }
1918
+ return false;
1919
+ }
1920
+ function useSyncHeadStyles(childWindow, options) {
1921
+ const delayMs = 100;
1922
+ const subtree = options?.subtree ?? false;
1923
+ const immediate = options?.immediate ?? true;
1924
+ useEffect11(() => {
1925
+ if (!childWindow) return;
1926
+ let timer;
1927
+ const scheduleSync = () => {
1928
+ if (timer) window.clearTimeout(timer);
1929
+ timer = window.setTimeout(() => {
1930
+ syncParentHeadToChild(childWindow);
1931
+ }, delayMs);
1932
+ };
1933
+ if (immediate) scheduleSync();
1934
+ const observer = new MutationObserver((mutations) => {
1935
+ if (!defaultShouldSync(mutations)) return;
1936
+ scheduleSync();
1937
+ });
1938
+ observer.observe(document.head, { childList: true, subtree });
1939
+ return () => {
1940
+ if (timer) window.clearTimeout(timer);
1941
+ observer.disconnect();
1942
+ };
1943
+ }, [childWindow, delayMs, subtree, immediate]);
1944
+ }
1945
+
1946
+ // src/spatialized-container/Spatialized2DElementContainer.tsx
1947
+ import { jsx as jsx7 } from "react/jsx-runtime";
1744
1948
  function getJSXPortalInstance(inProps, portalInstanceObject) {
1745
1949
  const { component: El, style: inStyle = {}, ...props } = inProps;
1746
1950
  const extraStyle = {
@@ -1766,19 +1970,8 @@ function getJSXPortalInstance(inProps, portalInstanceObject) {
1766
1970
  };
1767
1971
  return /* @__PURE__ */ jsx7(El, { style, ...props });
1768
1972
  }
1769
- function useSyncHeaderStyle(windowProxy) {
1770
- useEffect11(() => {
1771
- const headObserver = new MutationObserver((_) => {
1772
- syncParentHeadToChild(windowProxy);
1773
- });
1774
- headObserver.observe(document.head, { childList: true, subtree: true });
1775
- return () => {
1776
- headObserver.disconnect();
1777
- };
1778
- }, []);
1779
- }
1780
1973
  function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
1781
- useEffect11(() => {
1974
+ useEffect12(() => {
1782
1975
  windowProxy.document.title = name;
1783
1976
  spatializedElement.updateProperties({
1784
1977
  name
@@ -1788,11 +1981,13 @@ function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
1788
1981
  function SpatializedContent(props) {
1789
1982
  const { spatializedElement, ...restProps } = props;
1790
1983
  const spatialized2DElement = spatializedElement;
1791
- const windowProxy = spatialized2DElement.windowProxy;
1792
- useSyncHeaderStyle(windowProxy);
1984
+ const { windowProxy } = spatialized2DElement;
1985
+ useSyncHeadStyles(windowProxy, {
1986
+ subtree: false
1987
+ });
1793
1988
  const name = restProps["data-name"] || "";
1794
1989
  useSyncDocumentTitle(windowProxy, spatialized2DElement, name);
1795
- const portalInstanceObject = useContext7(
1990
+ const portalInstanceObject = useContext8(
1796
1991
  PortalInstanceContext
1797
1992
  );
1798
1993
  const JSXPortalInstance = getJSXPortalInstance(
@@ -1817,14 +2012,14 @@ function getExtraSpatializedElementProperties(computedStyle) {
1817
2012
  }
1818
2013
  async function createSpatializedElement() {
1819
2014
  const spatializedElement = await getSession().createSpatialized2DElement();
1820
- const windowProxy = spatializedElement.windowProxy;
2015
+ const { windowProxy } = spatializedElement;
1821
2016
  setOpenWindowStyle(windowProxy);
1822
2017
  await syncParentHeadToChild(windowProxy);
1823
2018
  const viewport = windowProxy.document.querySelector('meta[name="viewport"]');
1824
2019
  if (viewport) {
1825
2020
  viewport?.setAttribute(
1826
2021
  "content",
1827
- ` initial-scale=1.0, maximum-scale=1.0, user-scalable=no`
2022
+ " initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
1828
2023
  );
1829
2024
  } else {
1830
2025
  const meta = windowProxy.document.createElement("meta");
@@ -1854,9 +2049,10 @@ var Spatialized2DElementContainer = forwardRef5(
1854
2049
  import {
1855
2050
  forwardRef as forwardRef6,
1856
2051
  useCallback as useCallback6,
1857
- useContext as useContext8,
1858
- useEffect as useEffect12,
1859
- useMemo as useMemo3
2052
+ useContext as useContext9,
2053
+ useEffect as useEffect13,
2054
+ useMemo as useMemo3,
2055
+ useRef as useRef4
1860
2056
  } from "react";
1861
2057
  import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
1862
2058
  function getAbsoluteURL(url) {
@@ -1893,16 +2089,16 @@ function createLoadSuccessEvent(targetGetter) {
1893
2089
  function SpatializedContent2(props) {
1894
2090
  const { src, spatializedElement, onLoad, onError } = props;
1895
2091
  const spatializedStatic3DElement = spatializedElement;
1896
- const portalInstanceObject = useContext8(
2092
+ const portalInstanceObject = useContext9(
1897
2093
  PortalInstanceContext
1898
2094
  );
1899
2095
  const currentSrc = useMemo3(() => getAbsoluteURL(src), [src]);
1900
- useEffect12(() => {
2096
+ useEffect13(() => {
1901
2097
  if (src) {
1902
2098
  spatializedStatic3DElement.updateProperties({ modelURL: currentSrc });
1903
2099
  }
1904
2100
  }, [currentSrc]);
1905
- useEffect12(() => {
2101
+ useEffect13(() => {
1906
2102
  if (onLoad) {
1907
2103
  spatializedStatic3DElement.onLoadCallback = () => {
1908
2104
  onLoad(
@@ -1915,7 +2111,7 @@ function SpatializedContent2(props) {
1915
2111
  spatializedStatic3DElement.onLoadCallback = void 0;
1916
2112
  }
1917
2113
  }, [onLoad]);
1918
- useEffect12(() => {
2114
+ useEffect13(() => {
1919
2115
  if (onError) {
1920
2116
  spatializedStatic3DElement.onLoadFailureCallback = () => {
1921
2117
  onError(
@@ -1930,10 +2126,13 @@ function SpatializedContent2(props) {
1930
2126
  }, [onError]);
1931
2127
  return /* @__PURE__ */ jsx8(Fragment2, {});
1932
2128
  }
1933
- async function createSpatializedElement2() {
1934
- return getSession().createSpatializedStatic3DElement();
1935
- }
1936
2129
  function SpatializedStatic3DElementContainerBase(props, ref) {
2130
+ const promiseRef = useRef4(null);
2131
+ const createSpatializedElement2 = useCallback6(() => {
2132
+ const url = getAbsoluteURL(props.src);
2133
+ promiseRef.current = getSession().createSpatializedStatic3DElement(url);
2134
+ return promiseRef.current;
2135
+ }, []);
1937
2136
  const extraRefProps = useCallback6(
1938
2137
  (domProxy) => {
1939
2138
  let modelTransform = new DOMMatrixReadOnly();
@@ -1942,14 +2141,10 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1942
2141
  return getAbsoluteURL(props.src);
1943
2142
  },
1944
2143
  get ready() {
1945
- const spatializedElement = domProxy.__spatializedElement;
1946
- const promise = spatializedElement.ready.then((success) => {
1947
- if (success) {
1948
- return createLoadSuccessEvent(() => domProxy);
1949
- }
2144
+ return promiseRef.current.then((spatializedElement) => spatializedElement.ready).then((success) => {
2145
+ if (success) return createLoadSuccessEvent(() => domProxy);
1950
2146
  throw createLoadFailureEvent(() => domProxy);
1951
2147
  });
1952
- return promise;
1953
2148
  },
1954
2149
  get entityTransform() {
1955
2150
  return modelTransform;
@@ -1957,7 +2152,7 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1957
2152
  set entityTransform(value) {
1958
2153
  modelTransform = value;
1959
2154
  const spatializedElement = domProxy.__spatializedElement;
1960
- spatializedElement.updateModelTransform(modelTransform);
2155
+ spatializedElement?.updateModelTransform(modelTransform);
1961
2156
  }
1962
2157
  };
1963
2158
  },
@@ -2028,10 +2223,10 @@ function initScene(name, callback) {
2028
2223
  import { forwardRef as forwardRef9 } from "react";
2029
2224
 
2030
2225
  // src/spatialized-container-monitor/useMonitorDomChange.tsx
2031
- import { useRef as useRef4, useEffect as useEffect13, useMemo as useMemo4 } from "react";
2226
+ import { useRef as useRef5, useEffect as useEffect14, useMemo as useMemo4 } from "react";
2032
2227
  function useMonitorDomChange(inRef) {
2033
- const ref = useRef4(null);
2034
- useEffect13(() => {
2228
+ const ref = useRef5(null);
2229
+ useEffect14(() => {
2035
2230
  const observer = new MutationObserver((mutationsList) => {
2036
2231
  notifyDOMUpdate(mutationsList);
2037
2232
  });
@@ -2067,9 +2262,9 @@ function useMonitorDomChange(inRef) {
2067
2262
  }
2068
2263
 
2069
2264
  // src/spatialized-container-monitor/useMonitorDocumentHeaderChange.tsx
2070
- import { useEffect as useEffect14 } from "react";
2265
+ import { useEffect as useEffect15 } from "react";
2071
2266
  function useMonitorDocumentHeaderChange() {
2072
- useEffect14(() => {
2267
+ useEffect15(() => {
2073
2268
  const observer = new MutationObserver((mutationsList) => {
2074
2269
  notifyUpdateStandInstanceLayout();
2075
2270
  });
@@ -2129,17 +2324,66 @@ import { forwardRef as forwardRef11 } from "react";
2129
2324
  import { forwardRef as forwardRef10 } from "react";
2130
2325
 
2131
2326
  // src/reality/context/RealityContext.tsx
2132
- import { createContext as createContext5, useContext as useContext9 } from "react";
2133
- var RealityContext = createContext5(null);
2134
- var useRealityContext = () => useContext9(RealityContext);
2327
+ import { createContext as createContext6, useContext as useContext10 } from "react";
2328
+ var RealityContext = createContext6(null);
2329
+ var useRealityContext = () => useContext10(RealityContext);
2135
2330
 
2136
2331
  // src/reality/context/ParentContext.tsx
2137
- import { createContext as createContext6, useContext as useContext10 } from "react";
2138
- var ParentContext = createContext6(null);
2139
- var useParentContext = () => useContext10(ParentContext);
2332
+ import { createContext as createContext7, useContext as useContext11 } from "react";
2333
+ var ParentContext = createContext7(null);
2334
+ var useParentContext = () => useContext11(ParentContext);
2335
+
2336
+ // src/reality/context/AttachmentContext.tsx
2337
+ import { createContext as createContext8, useContext as useContext12 } from "react";
2338
+ var AttachmentRegistry = class {
2339
+ // name → (instanceId → container)
2340
+ containers = /* @__PURE__ */ new Map();
2341
+ listeners = /* @__PURE__ */ new Map();
2342
+ addContainer(name, instanceId, container) {
2343
+ if (!this.containers.has(name)) {
2344
+ this.containers.set(name, /* @__PURE__ */ new Map());
2345
+ }
2346
+ this.containers.get(name).set(instanceId, container);
2347
+ this.notifyListeners(name);
2348
+ }
2349
+ removeContainer(name, instanceId) {
2350
+ this.containers.get(name)?.delete(instanceId);
2351
+ if (this.containers.get(name)?.size === 0) {
2352
+ this.containers.delete(name);
2353
+ }
2354
+ this.notifyListeners(name);
2355
+ }
2356
+ getContainers(name) {
2357
+ const map = this.containers.get(name);
2358
+ return map ? Array.from(map.values()) : [];
2359
+ }
2360
+ onContainersChange(name, cb) {
2361
+ const current = this.getContainers(name);
2362
+ if (current.length > 0) {
2363
+ cb(current);
2364
+ }
2365
+ const prev = this.listeners.get(name);
2366
+ if (prev) prev([]);
2367
+ this.listeners.set(name, cb);
2368
+ return () => {
2369
+ if (this.listeners.get(name) === cb) {
2370
+ this.listeners.delete(name);
2371
+ }
2372
+ };
2373
+ }
2374
+ notifyListeners(name) {
2375
+ const cs = this.getContainers(name);
2376
+ this.listeners.get(name)?.(cs);
2377
+ }
2378
+ destroy() {
2379
+ this.containers.clear();
2380
+ this.listeners.clear();
2381
+ }
2382
+ };
2383
+ var AttachmentContext = createContext8(null);
2140
2384
 
2141
2385
  // src/reality/hooks/useEntityTransform.tsx
2142
- import { useEffect as useEffect15, useRef as useRef5 } from "react";
2386
+ import { useEffect as useEffect16, useRef as useRef6 } from "react";
2143
2387
 
2144
2388
  // src/reality/utils/ResourceRegistry.ts
2145
2389
  var ResourceRegistry = class {
@@ -2220,8 +2464,8 @@ var AbortResourceManager = class {
2220
2464
 
2221
2465
  // src/reality/hooks/useEntityTransform.tsx
2222
2466
  function useEntityTransform(entity, { position, rotation, scale }) {
2223
- const last = useRef5({});
2224
- useEffect15(() => {
2467
+ const last = useRef6({});
2468
+ useEffect16(() => {
2225
2469
  if (!entity) return;
2226
2470
  const shouldUpdate = !shallowEqualVec3(last.current.position, position) || !shallowEqualRotation(last.current.rotation, rotation) || !shallowEqualVec3(last.current.scale, scale);
2227
2471
  if (!shouldUpdate) return;
@@ -2240,7 +2484,7 @@ function useEntityTransform(entity, { position, rotation, scale }) {
2240
2484
  }
2241
2485
 
2242
2486
  // src/reality/hooks/useEntityEvent.tsx
2243
- import { useEffect as useEffect17, useRef as useRef7 } from "react";
2487
+ import { useEffect as useEffect18, useRef as useRef8 } from "react";
2244
2488
 
2245
2489
  // src/reality/type.ts
2246
2490
  var eventMap = {
@@ -2377,14 +2621,75 @@ function createEventProxy2(ev, instance) {
2377
2621
  }
2378
2622
  return void 0;
2379
2623
  }
2624
+ if (prop === "translationX") {
2625
+ const type = target.type;
2626
+ if (type === "spatialdrag") {
2627
+ return target.detail?.translation3D?.x ?? 0;
2628
+ }
2629
+ return void 0;
2630
+ }
2631
+ if (prop === "translationY") {
2632
+ const type = target.type;
2633
+ if (type === "spatialdrag") {
2634
+ return target.detail?.translation3D?.y ?? 0;
2635
+ }
2636
+ return void 0;
2637
+ }
2638
+ if (prop === "translationZ") {
2639
+ const type = target.type;
2640
+ if (type === "spatialdrag") {
2641
+ return target.detail?.translation3D?.z ?? 0;
2642
+ }
2643
+ return void 0;
2644
+ }
2645
+ if (prop === "quaternion") {
2646
+ const type = target.type;
2647
+ if (type === "spatialrotate") {
2648
+ return target.detail?.quaternion ?? {
2649
+ x: 0,
2650
+ y: 0,
2651
+ z: 0,
2652
+ w: 1
2653
+ };
2654
+ }
2655
+ return void 0;
2656
+ }
2657
+ if (prop === "magnification") {
2658
+ const type = target.type;
2659
+ if (type === "spatialmagnify") {
2660
+ return target.detail?.magnification ?? 1;
2661
+ }
2662
+ return void 0;
2663
+ }
2664
+ if (prop === "clientX") {
2665
+ const type = target.type;
2666
+ if (type === "spatialtap" || type === "spatialdragstart") {
2667
+ return target.detail?.globalLocation3D?.x ?? 0;
2668
+ }
2669
+ return void 0;
2670
+ }
2671
+ if (prop === "clientY") {
2672
+ const type = target.type;
2673
+ if (type === "spatialtap" || type === "spatialdragstart") {
2674
+ return target.detail?.globalLocation3D?.y ?? 0;
2675
+ }
2676
+ return void 0;
2677
+ }
2678
+ if (prop === "clientZ") {
2679
+ const type = target.type;
2680
+ if (type === "spatialtap" || type === "spatialdragstart") {
2681
+ return target.detail?.globalLocation3D?.z ?? 0;
2682
+ }
2683
+ return void 0;
2684
+ }
2380
2685
  const val = target[prop];
2381
2686
  return typeof val === "function" ? val.bind(target) : val;
2382
2687
  }
2383
2688
  });
2384
2689
  }
2385
2690
  var useEntityEvent = ({ instance, ...handlers }) => {
2386
- const eventsSetRef = useRef7(/* @__PURE__ */ new Set());
2387
- useEffect17(() => {
2691
+ const eventsSetRef = useRef8(/* @__PURE__ */ new Set());
2692
+ useEffect18(() => {
2388
2693
  const entity = instance.entity;
2389
2694
  if (!entity) return;
2390
2695
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
@@ -2397,7 +2702,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2397
2702
  return () => {
2398
2703
  };
2399
2704
  }, [instance.entity, ...Object.values(handlers)]);
2400
- useEffect17(() => {
2705
+ useEffect18(() => {
2401
2706
  const entity = instance.entity;
2402
2707
  if (!entity) return;
2403
2708
  return () => {
@@ -2411,10 +2716,10 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2411
2716
  };
2412
2717
 
2413
2718
  // src/reality/hooks/useEntityId.tsx
2414
- import { useEffect as useEffect18 } from "react";
2719
+ import { useEffect as useEffect19 } from "react";
2415
2720
  var useEntityId = ({ id, entity }) => {
2416
2721
  const ctx = useRealityContext();
2417
- useEffect18(() => {
2722
+ useEffect19(() => {
2418
2723
  if (!id || !entity || !ctx) return;
2419
2724
  ctx.resourceRegistry.add(id, Promise.resolve(entity));
2420
2725
  return () => {
@@ -2425,7 +2730,7 @@ var useEntityId = ({ id, entity }) => {
2425
2730
  };
2426
2731
 
2427
2732
  // src/reality/hooks/useEntity.tsx
2428
- import { useEffect as useEffect19, useRef as useRef8 } from "react";
2733
+ import { useEffect as useEffect20, useRef as useRef9 } from "react";
2429
2734
  var useEntity = ({
2430
2735
  ref,
2431
2736
  id,
@@ -2447,9 +2752,9 @@ var useEntity = ({
2447
2752
  }) => {
2448
2753
  const ctx = useRealityContext();
2449
2754
  const parent = useParentContext();
2450
- const instanceRef = useRef8(new EntityRef(null, ctx));
2755
+ const instanceRef = useRef9(new EntityRef(null, ctx));
2451
2756
  const forceUpdate = useForceUpdate2();
2452
- useEffect19(() => {
2757
+ useEffect20(() => {
2453
2758
  if (!ctx) return;
2454
2759
  const controller = new AbortController();
2455
2760
  const init = async () => {
@@ -2606,11 +2911,11 @@ var BoxEntity = forwardRef13(
2606
2911
  );
2607
2912
 
2608
2913
  // src/reality/components/UnlitMaterial.tsx
2609
- import { useEffect as useEffect20, useRef as useRef9 } from "react";
2914
+ import { useEffect as useEffect21, useRef as useRef10 } from "react";
2610
2915
  var UnlitMaterial = ({ children, ...options }) => {
2611
2916
  const ctx = useRealityContext();
2612
- const materialRef = useRef9();
2613
- useEffect20(() => {
2917
+ const materialRef = useRef10();
2918
+ useEffect21(() => {
2614
2919
  if (!ctx) return;
2615
2920
  const { session, reality, resourceRegistry } = ctx;
2616
2921
  const init = async () => {
@@ -2726,7 +3031,7 @@ var SceneGraph = ({ children }) => {
2726
3031
  };
2727
3032
 
2728
3033
  // src/reality/components/ModelAsset.tsx
2729
- import { useEffect as useEffect21, useRef as useRef10 } from "react";
3034
+ import { useEffect as useEffect22, useRef as useRef11 } from "react";
2730
3035
  var resolveAssetUrl = (url) => {
2731
3036
  if (url.startsWith("http://") || url.startsWith("https://")) {
2732
3037
  return url;
@@ -2735,8 +3040,8 @@ var resolveAssetUrl = (url) => {
2735
3040
  };
2736
3041
  var ModelAsset = ({ children, ...options }) => {
2737
3042
  const ctx = useRealityContext();
2738
- const materialRef = useRef10();
2739
- useEffect21(() => {
3043
+ const materialRef = useRef11();
3044
+ useEffect22(() => {
2740
3045
  const controller = new AbortController();
2741
3046
  if (!ctx) return;
2742
3047
  const { session, reality, resourceRegistry } = ctx;
@@ -2803,13 +3108,20 @@ var ModelEntity = forwardRef18(
2803
3108
  import {
2804
3109
  forwardRef as forwardRef19,
2805
3110
  useCallback as useCallback8,
2806
- useEffect as useEffect22,
2807
- useRef as useRef11,
3111
+ useEffect as useEffect23,
3112
+ useRef as useRef12,
2808
3113
  useState as useState8
2809
3114
  } from "react";
2810
3115
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
2811
3116
  var Reality = forwardRef19(
2812
3117
  function RealityBase({ children, ...inProps }, ref) {
3118
+ const insideAttachment = useInsideAttachment();
3119
+ if (insideAttachment) {
3120
+ console.warn(
3121
+ "[WebSpatial] Reality cannot be used inside AttachmentAsset."
3122
+ );
3123
+ return null;
3124
+ }
2813
3125
  const {
2814
3126
  onSpatialTap,
2815
3127
  onSpatialDragStart,
@@ -2821,16 +3133,17 @@ var Reality = forwardRef19(
2821
3133
  onSpatialMagnifyEnd,
2822
3134
  ...props
2823
3135
  } = inProps;
2824
- const ctxRef = useRef11(null);
2825
- const creationId = useRef11(0);
3136
+ const ctxRef = useRef12(null);
3137
+ const creationId = useRef12(0);
2826
3138
  const [isReady, setIsReady] = useState8(false);
2827
3139
  const cleanupReality = useCallback8(() => {
3140
+ ctxRef.current?.attachmentRegistry.destroy();
2828
3141
  ctxRef.current?.resourceRegistry.destroy();
2829
3142
  ctxRef.current?.reality.destroy();
2830
3143
  ctxRef.current = null;
2831
3144
  setIsReady(false);
2832
3145
  }, []);
2833
- useEffect22(() => {
3146
+ useEffect23(() => {
2834
3147
  return () => {
2835
3148
  creationId.current++;
2836
3149
  cleanupReality();
@@ -2839,15 +3152,18 @@ var Reality = forwardRef19(
2839
3152
  const createReality = useCallback8(async () => {
2840
3153
  const id = ++creationId.current;
2841
3154
  const resourceRegistry = new ResourceRegistry();
3155
+ const attachmentRegistry = new AttachmentRegistry();
2842
3156
  const session = await getSession();
2843
3157
  if (!session) {
2844
3158
  resourceRegistry.destroy();
3159
+ attachmentRegistry.destroy();
2845
3160
  return null;
2846
3161
  }
2847
3162
  const reality = await session.createSpatializedDynamic3DElement();
2848
3163
  const isCancelled = () => id !== creationId.current;
2849
3164
  if (isCancelled()) {
2850
3165
  resourceRegistry.destroy();
3166
+ attachmentRegistry.destroy();
2851
3167
  reality.destroy();
2852
3168
  return null;
2853
3169
  }
@@ -2855,16 +3171,23 @@ var Reality = forwardRef19(
2855
3171
  const result = await session.getSpatialScene().addSpatializedElement(reality);
2856
3172
  if (!result.success || isCancelled()) {
2857
3173
  resourceRegistry.destroy();
3174
+ attachmentRegistry.destroy();
2858
3175
  reality.destroy();
2859
3176
  return null;
2860
3177
  }
2861
3178
  cleanupReality();
2862
- ctxRef.current = { session, reality, resourceRegistry };
3179
+ ctxRef.current = {
3180
+ session,
3181
+ reality,
3182
+ resourceRegistry,
3183
+ attachmentRegistry
3184
+ };
2863
3185
  setIsReady(true);
2864
3186
  return reality;
2865
3187
  } catch (err) {
2866
3188
  console.error("[createReality] failed", err);
2867
3189
  resourceRegistry.destroy();
3190
+ attachmentRegistry.destroy();
2868
3191
  reality.destroy();
2869
3192
  return null;
2870
3193
  }
@@ -2886,13 +3209,132 @@ var Reality = forwardRef19(
2886
3209
  }
2887
3210
  );
2888
3211
 
3212
+ // src/reality/components/AttachmentAsset.tsx
3213
+ import { useEffect as useEffect24, useState as useState9 } from "react";
3214
+ import { createPortal as createPortal3 } from "react-dom";
3215
+ import { jsx as jsx23 } from "react/jsx-runtime";
3216
+ var AttachmentAsset = ({
3217
+ name,
3218
+ children
3219
+ }) => {
3220
+ const ctx = useRealityContext();
3221
+ const [containers, setContainers] = useState9([]);
3222
+ useEffect24(() => {
3223
+ if (!ctx) return;
3224
+ return ctx.attachmentRegistry.onContainersChange(name, setContainers);
3225
+ }, [ctx, name]);
3226
+ if (!containers.length) return null;
3227
+ return /* @__PURE__ */ jsx23(InsideAttachmentContext.Provider, { value: true, children: containers.map((c, idx) => createPortal3(children, c, `${name}-${idx}`)) });
3228
+ };
3229
+
3230
+ // src/reality/components/AttachmentEntity.tsx
3231
+ import { useEffect as useEffect25, useRef as useRef13, useState as useState10 } from "react";
3232
+ var instanceCounter = 0;
3233
+ var AttachmentEntity = ({
3234
+ attachment: attachmentName,
3235
+ position,
3236
+ size
3237
+ }) => {
3238
+ const ctx = useRealityContext();
3239
+ const parent = useParentContext();
3240
+ const attachmentRef = useRef13(null);
3241
+ const parentIdRef = useRef13(null);
3242
+ const instanceIdRef = useRef13(`att_${++instanceCounter}`);
3243
+ const attachmentNameRef = useRef13(attachmentName);
3244
+ const [childWindow, setChildWindow] = useState10(null);
3245
+ useEffect25(() => {
3246
+ if (!ctx || !parent) return;
3247
+ const parentId = parent.id;
3248
+ parentIdRef.current = parentId;
3249
+ let cancelled = false;
3250
+ const init = async () => {
3251
+ try {
3252
+ const att = await ctx.session.createAttachmentEntity({
3253
+ parentEntityId: parentId,
3254
+ position: position ?? [0, 0, 0],
3255
+ size
3256
+ });
3257
+ if (cancelled) {
3258
+ att.destroy();
3259
+ return;
3260
+ }
3261
+ const windowProxy = att.getWindowProxy();
3262
+ setOpenWindowStyle(windowProxy);
3263
+ windowProxy.document.body.style.display = "block";
3264
+ windowProxy.document.body.style.minWidth = "100%";
3265
+ windowProxy.document.body.style.maxWidth = "100%";
3266
+ windowProxy.document.body.style.minHeight = "100%";
3267
+ await syncParentHeadToChild(windowProxy);
3268
+ const viewport = windowProxy.document.querySelector(
3269
+ 'meta[name="viewport"]'
3270
+ );
3271
+ if (!viewport) {
3272
+ const meta = windowProxy.document.createElement("meta");
3273
+ meta.name = "viewport";
3274
+ meta.content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
3275
+ windowProxy.document.head.appendChild(meta);
3276
+ }
3277
+ const base = windowProxy.document.createElement("base");
3278
+ base.href = document.baseURI;
3279
+ windowProxy.document.head.appendChild(base);
3280
+ attachmentRef.current = att;
3281
+ setChildWindow(windowProxy);
3282
+ ctx.attachmentRegistry.addContainer(
3283
+ attachmentNameRef.current,
3284
+ instanceIdRef.current,
3285
+ att.getContainer()
3286
+ );
3287
+ } catch (error) {
3288
+ console.error("[AttachmentEntity] init error:", error);
3289
+ }
3290
+ };
3291
+ init();
3292
+ return () => {
3293
+ cancelled = true;
3294
+ const att = attachmentRef.current;
3295
+ if (att) {
3296
+ ctx.attachmentRegistry.removeContainer(
3297
+ attachmentNameRef.current,
3298
+ instanceIdRef.current
3299
+ );
3300
+ att.destroy();
3301
+ attachmentRef.current = null;
3302
+ setChildWindow(null);
3303
+ }
3304
+ };
3305
+ }, [ctx, parent]);
3306
+ useEffect25(() => {
3307
+ if (!ctx) return;
3308
+ const att = attachmentRef.current;
3309
+ const prevName = attachmentNameRef.current;
3310
+ if (att && prevName !== attachmentName) {
3311
+ ctx.attachmentRegistry.removeContainer(prevName, instanceIdRef.current);
3312
+ ctx.attachmentRegistry.addContainer(
3313
+ attachmentName,
3314
+ instanceIdRef.current,
3315
+ att.getContainer()
3316
+ );
3317
+ attachmentNameRef.current = attachmentName;
3318
+ } else {
3319
+ attachmentNameRef.current = attachmentName;
3320
+ }
3321
+ }, [ctx, attachmentName]);
3322
+ useSyncHeadStyles(childWindow, { subtree: false });
3323
+ useEffect25(() => {
3324
+ if (!attachmentRef.current) return;
3325
+ attachmentRef.current.update({ position, size });
3326
+ }, [position?.[0], position?.[1], position?.[2], size?.width, size?.height]);
3327
+ return null;
3328
+ };
3329
+
2889
3330
  // src/Model.tsx
2890
3331
  import { forwardRef as forwardRef20 } from "react";
2891
- import { jsx as jsx23 } from "react/jsx-runtime";
3332
+ import { jsx as jsx24 } from "react/jsx-runtime";
2892
3333
  var spatial2 = new Spatial();
2893
3334
  function ModelBase(props, ref) {
3335
+ const insideAttachment = useInsideAttachment();
2894
3336
  const { "enable-xr": enableXR, ...restProps } = props;
2895
- if (!enableXR || !spatial2.runInSpatialWeb()) {
3337
+ if (!enableXR || !spatial2.runInSpatialWeb() || insideAttachment) {
2896
3338
  const {
2897
3339
  onSpatialTap,
2898
3340
  onSpatialDragStart,
@@ -2904,19 +3346,21 @@ function ModelBase(props, ref) {
2904
3346
  onSpatialMagnifyEnd,
2905
3347
  ...modelProps
2906
3348
  } = restProps;
2907
- return /* @__PURE__ */ jsx23("model", { ref, ...modelProps });
3349
+ return /* @__PURE__ */ jsx24("model", { ref, ...modelProps });
2908
3350
  }
2909
- return /* @__PURE__ */ jsx23(SpatializedStatic3DElementContainer, { ref, ...restProps });
3351
+ return /* @__PURE__ */ jsx24(SpatializedStatic3DElementContainer, { ref, ...restProps });
2910
3352
  }
2911
3353
  var Model = withSSRSupported(forwardRef20(ModelBase));
2912
3354
  Model.displayName = "Model";
2913
3355
 
2914
3356
  // src/index.ts
2915
- var version = "1.2.0";
3357
+ var version = "1.3.0";
2916
3358
  if (typeof window !== "undefined") {
2917
3359
  initPolyfill();
2918
3360
  }
2919
3361
  export {
3362
+ AttachmentAsset,
3363
+ AttachmentEntity,
2920
3364
  BoxEntity,
2921
3365
  ConeEntity,
2922
3366
  CylinderEntity,