@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.
@@ -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'] = "avp"
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/utils/getSession.ts
855
855
  import { isSSREnv, Spatial } from "@webspatial/core-sdk";
@@ -1164,12 +1164,12 @@ function useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObj
1164
1164
 
1165
1165
  // src/spatialized-container/hooks/useSpatializedElement.ts
1166
1166
  import { useEffect as useEffect6, useState as useState3 } from "react";
1167
- function useSpatializedElement(createSpatializedElement3, portalInstanceObject) {
1167
+ function useSpatializedElement(createSpatializedElement2, portalInstanceObject) {
1168
1168
  const [spatializedElement, setSpatializedElement] = useState3();
1169
1169
  useEffect6(() => {
1170
1170
  let isDestroyed = false;
1171
1171
  let spatializedElement2;
1172
- createSpatializedElement3().then(
1172
+ createSpatializedElement2().then(
1173
1173
  (inSpatializedElement) => {
1174
1174
  if (!isDestroyed) {
1175
1175
  spatializedElement2 = inSpatializedElement;
@@ -1187,7 +1187,7 @@ function useSpatializedElement(createSpatializedElement3, portalInstanceObject)
1187
1187
  spatializedElement2 = void 0;
1188
1188
  }
1189
1189
  };
1190
- }, [createSpatializedElement3, portalInstanceObject]);
1190
+ }, [createSpatializedElement2, portalInstanceObject]);
1191
1191
  return spatializedElement;
1192
1192
  }
1193
1193
 
@@ -1221,7 +1221,7 @@ function renderPlaceholderInSubPortal(portalInstanceObject, El) {
1221
1221
  function PortalSpatializedContainer(props) {
1222
1222
  const {
1223
1223
  spatializedContent: Content,
1224
- createSpatializedElement: createSpatializedElement3,
1224
+ createSpatializedElement: createSpatializedElement2,
1225
1225
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1226
1226
  onSpatialTap,
1227
1227
  onSpatialDragStart,
@@ -1255,7 +1255,7 @@ function PortalSpatializedContainer(props) {
1255
1255
  }, []);
1256
1256
  useSync2DFrame(spatialId, portalInstanceObject, spatializedContainerObject);
1257
1257
  const spatializedElement = useSpatializedElement(
1258
- createSpatializedElement3,
1258
+ createSpatializedElement2,
1259
1259
  portalInstanceObject
1260
1260
  );
1261
1261
  const PlaceholderEl = renderPlaceholderInSubPortal(
@@ -1308,8 +1308,13 @@ function PortalSpatializedContainer(props) {
1308
1308
  ] });
1309
1309
  }
1310
1310
 
1311
+ // src/reality/context/InsideAttachmentContext.tsx
1312
+ import { createContext as createContext4, useContext as useContext5 } from "react";
1313
+ var InsideAttachmentContext = createContext4(false);
1314
+ var useInsideAttachment = () => useContext5(InsideAttachmentContext);
1315
+
1311
1316
  // src/spatialized-container/hooks/useSpatialEvents.ts
1312
- function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter) {
1317
+ function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter, clientXGetter, clientYGetter, clientZGetter, translationXGetter, translationYGetter, translationZGetter, quaternionGetter, magnificationGetter) {
1313
1318
  return new Proxy(event, {
1314
1319
  get(target, prop) {
1315
1320
  if (prop === "currentTarget") {
@@ -1330,18 +1335,50 @@ function createEventProxy(event, currentTargetGetter, offsetXGetter, offsetYGett
1330
1335
  if (prop === "offsetZ" && offsetZGetter) {
1331
1336
  return offsetZGetter(target) ?? 0;
1332
1337
  }
1338
+ if (prop === "clientX" && clientXGetter) {
1339
+ return clientXGetter(target) ?? 0;
1340
+ }
1341
+ if (prop === "clientY" && clientYGetter) {
1342
+ return clientYGetter(target) ?? 0;
1343
+ }
1344
+ if (prop === "clientZ" && clientZGetter) {
1345
+ return clientZGetter(target) ?? 0;
1346
+ }
1347
+ if (prop === "translationX" && translationXGetter) {
1348
+ return translationXGetter(target) ?? 0;
1349
+ }
1350
+ if (prop === "translationY" && translationYGetter) {
1351
+ return translationYGetter(target) ?? 0;
1352
+ }
1353
+ if (prop === "translationZ" && translationZGetter) {
1354
+ return translationZGetter(target) ?? 0;
1355
+ }
1356
+ if (prop === "quaternion" && quaternionGetter) {
1357
+ return quaternionGetter(target) ?? { x: 0, y: 0, z: 0, w: 1 };
1358
+ }
1359
+ if (prop === "magnification" && magnificationGetter) {
1360
+ return magnificationGetter(target) ?? 1;
1361
+ }
1333
1362
  return Reflect.get(target, prop);
1334
1363
  }
1335
1364
  });
1336
1365
  }
1337
- function createEventHandler(handler, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter) {
1366
+ function createEventHandler(handler, currentTargetGetter, offsetXGetter, offsetYGetter, offsetZGetter, clientXGetter, clientYGetter, clientZGetter, translationXGetter, translationYGetter, translationZGetter, quaternionGetter, magnificationGetter) {
1338
1367
  return handler ? (event) => {
1339
1368
  const proxyEvent = createEventProxy(
1340
1369
  event,
1341
1370
  currentTargetGetter,
1342
1371
  offsetXGetter,
1343
1372
  offsetYGetter,
1344
- offsetZGetter
1373
+ offsetZGetter,
1374
+ clientXGetter,
1375
+ clientYGetter,
1376
+ clientZGetter,
1377
+ translationXGetter,
1378
+ translationYGetter,
1379
+ translationZGetter,
1380
+ quaternionGetter,
1381
+ magnificationGetter
1345
1382
  );
1346
1383
  handler(proxyEvent);
1347
1384
  } : void 0;
@@ -1350,13 +1387,27 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1350
1387
  const onSpatialTap = createEventHandler(
1351
1388
  spatialEvents.onSpatialTap,
1352
1389
  currentTargetGetter,
1390
+ // offsetX/Y/Z come from local coordinates
1353
1391
  (ev) => ev.detail?.location3D?.x,
1354
1392
  (ev) => ev.detail?.location3D?.y,
1355
- (ev) => ev.detail?.location3D?.z
1393
+ (ev) => ev.detail?.location3D?.z,
1394
+ // clientX/Y/Z come from global scene coordinates
1395
+ (ev) => ev.detail?.globalLocation3D?.x,
1396
+ (ev) => ev.detail?.globalLocation3D?.y,
1397
+ (ev) => ev.detail?.globalLocation3D?.z
1356
1398
  );
1357
1399
  const onSpatialDrag = createEventHandler(
1358
1400
  spatialEvents.onSpatialDrag,
1359
- currentTargetGetter
1401
+ currentTargetGetter,
1402
+ void 0,
1403
+ void 0,
1404
+ void 0,
1405
+ void 0,
1406
+ void 0,
1407
+ void 0,
1408
+ (ev) => ev.detail?.translation3D?.x,
1409
+ (ev) => ev.detail?.translation3D?.y,
1410
+ (ev) => ev.detail?.translation3D?.z
1360
1411
  );
1361
1412
  const onSpatialDragEnd = createEventHandler(
1362
1413
  spatialEvents.onSpatialDragEnd,
@@ -1364,7 +1415,17 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1364
1415
  );
1365
1416
  const onSpatialRotate = createEventHandler(
1366
1417
  spatialEvents.onSpatialRotate,
1367
- currentTargetGetter
1418
+ currentTargetGetter,
1419
+ void 0,
1420
+ void 0,
1421
+ void 0,
1422
+ void 0,
1423
+ void 0,
1424
+ void 0,
1425
+ void 0,
1426
+ void 0,
1427
+ void 0,
1428
+ (ev) => ev.detail?.quaternion
1368
1429
  );
1369
1430
  const onSpatialRotateEnd = createEventHandler(
1370
1431
  spatialEvents.onSpatialRotateEnd,
@@ -1372,7 +1433,18 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1372
1433
  );
1373
1434
  const onSpatialMagnify = createEventHandler(
1374
1435
  spatialEvents.onSpatialMagnify,
1375
- currentTargetGetter
1436
+ currentTargetGetter,
1437
+ void 0,
1438
+ void 0,
1439
+ void 0,
1440
+ void 0,
1441
+ void 0,
1442
+ void 0,
1443
+ void 0,
1444
+ void 0,
1445
+ void 0,
1446
+ void 0,
1447
+ (ev) => ev.detail?.magnification
1376
1448
  );
1377
1449
  const onSpatialMagnifyEnd = createEventHandler(
1378
1450
  spatialEvents.onSpatialMagnifyEnd,
@@ -1383,7 +1455,10 @@ function useSpatialEventsBase(spatialEvents, currentTargetGetter) {
1383
1455
  currentTargetGetter,
1384
1456
  (ev) => ev.detail?.startLocation3D?.x,
1385
1457
  (ev) => ev.detail?.startLocation3D?.y,
1386
- (ev) => ev.detail?.startLocation3D?.z
1458
+ (ev) => ev.detail?.startLocation3D?.z,
1459
+ (ev) => ev.detail?.globalLocation3D?.x,
1460
+ (ev) => ev.detail?.globalLocation3D?.y,
1461
+ (ev) => ev.detail?.globalLocation3D?.z
1387
1462
  );
1388
1463
  return {
1389
1464
  onSpatialTap,
@@ -1412,9 +1487,9 @@ function useSpatialEventsWhenSpatializedContainerExist(spatialEvents, spatialId,
1412
1487
  }
1413
1488
 
1414
1489
  // src/ssr/SSRContext.tsx
1415
- import { createContext as createContext4, useState as useState4, useEffect as useEffect8 } from "react";
1490
+ import { createContext as createContext5, useState as useState4, useEffect as useEffect8 } from "react";
1416
1491
  import { jsx as jsx4 } from "react/jsx-runtime";
1417
- var SSRContext = createContext4(false);
1492
+ var SSRContext = createContext5(false);
1418
1493
  var SSRProvider = ({
1419
1494
  isSSR: initialIsSSR = true,
1420
1495
  children
@@ -1432,9 +1507,9 @@ var SSRProvider = ({
1432
1507
  import { forwardRef as forwardRef3 } from "react";
1433
1508
 
1434
1509
  // src/ssr/useSSRPhase.tsx
1435
- import { useContext as useContext5, useState as useState5, useEffect as useEffect9 } from "react";
1510
+ import { useContext as useContext6, useState as useState5, useEffect as useEffect9 } from "react";
1436
1511
  function useSSRPhase() {
1437
- const isSSRContext = useContext5(SSRContext);
1512
+ const isSSRContext = useContext6(SSRContext);
1438
1513
  const isServer = typeof window === "undefined";
1439
1514
  const [hydrated, setHydrated] = useState5(false);
1440
1515
  useEffect9(() => setHydrated(true), []);
@@ -1470,33 +1545,48 @@ function withSSRSupported(Component) {
1470
1545
 
1471
1546
  // src/spatialized-container/SpatializedContainer.tsx
1472
1547
  import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1548
+ function DegradedContainer({
1549
+ innerRef,
1550
+ ...inprops
1551
+ }) {
1552
+ const {
1553
+ component: Component,
1554
+ children,
1555
+ ["enable-xr"]: _enableXR,
1556
+ onSpatialTap: _onSpatialTap,
1557
+ onSpatialDragStart: _onSpatialDragStart,
1558
+ onSpatialDrag: _onSpatialDrag,
1559
+ onSpatialDragEnd: _onSpatialDragEnd,
1560
+ onSpatialRotate: _onSpatialRotate,
1561
+ onSpatialRotateEnd: _onSpatialRotateEnd,
1562
+ onSpatialMagnify: _onSpatialMagnify,
1563
+ onSpatialMagnifyEnd: _onSpatialMagnifyEnd,
1564
+ spatializedContent: _content,
1565
+ createSpatializedElement: _create,
1566
+ getExtraSpatializedElementProperties: _getExtra,
1567
+ extraRefProps: _extraRef,
1568
+ sizingMode: _sizingMode,
1569
+ ...restProps
1570
+ } = inprops;
1571
+ return /* @__PURE__ */ jsx6(Component, { ref: innerRef, ...restProps, children });
1572
+ }
1473
1573
  function SpatializedContainerBase(inprops, ref) {
1474
1574
  const isWebSpatialEnv = getSession() !== null;
1475
- if (!isWebSpatialEnv) {
1476
- const {
1477
- component: Component,
1478
- spatializedContent,
1479
- createSpatializedElement: createSpatializedElement3,
1480
- getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1481
- onSpatialTap: onSpatialTap2,
1482
- onSpatialDragStart: onSpatialDragStart2,
1483
- onSpatialDrag: onSpatialDrag2,
1484
- onSpatialDragEnd: onSpatialDragEnd2,
1485
- onSpatialRotate: onSpatialRotate2,
1486
- onSpatialRotateEnd: onSpatialRotateEnd2,
1487
- onSpatialMagnify: onSpatialMagnify2,
1488
- onSpatialMagnifyEnd: onSpatialMagnifyEnd2,
1489
- extraRefProps: extraRefProps2,
1490
- ...restProps
1491
- } = inprops;
1492
- return /* @__PURE__ */ jsx6(Component, { ref, ...restProps });
1575
+ const insideAttachment = useInsideAttachment();
1576
+ if (!isWebSpatialEnv || insideAttachment) {
1577
+ if (insideAttachment) {
1578
+ console.warn(
1579
+ `[WebSpatial] ${inprops.component || "Spatial element"} cannot be used inside AttachmentAsset. Rendering as plain HTML.`
1580
+ );
1581
+ }
1582
+ return /* @__PURE__ */ jsx6(DegradedContainer, { ...inprops, innerRef: ref });
1493
1583
  }
1494
- const layer = useContext6(SpatialLayerContext) + 1;
1495
- const rootSpatializedContainerObject = useContext6(
1584
+ const layer = useContext7(SpatialLayerContext) + 1;
1585
+ const rootSpatializedContainerObject = useContext7(
1496
1586
  SpatializedContainerContext
1497
1587
  );
1498
1588
  const inSpatializedContainer = !!rootSpatializedContainerObject;
1499
- const portalInstanceObject = useContext6(PortalInstanceContext);
1589
+ const portalInstanceObject = useContext7(PortalInstanceContext);
1500
1590
  const inPortalInstanceEnv = !!portalInstanceObject;
1501
1591
  const isInStandardInstance = !inPortalInstanceEnv;
1502
1592
  const spatialId = useMemo2(() => {
@@ -1555,7 +1645,7 @@ function SpatializedContainerBase(inprops, ref) {
1555
1645
  }, [spatialContainerRefProxy.current]);
1556
1646
  const {
1557
1647
  spatializedContent,
1558
- createSpatializedElement: createSpatializedElement3,
1648
+ createSpatializedElement: createSpatializedElement2,
1559
1649
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1560
1650
  ...restProps
1561
1651
  } = props;
@@ -1605,7 +1695,7 @@ function SpatializedContainerBase(inprops, ref) {
1605
1695
  );
1606
1696
  const {
1607
1697
  spatializedContent,
1608
- createSpatializedElement: createSpatializedElement3,
1698
+ createSpatializedElement: createSpatializedElement2,
1609
1699
  getExtraSpatializedElementProperties: getExtraSpatializedElementProperties2,
1610
1700
  ...restProps
1611
1701
  } = props;
@@ -1650,28 +1740,47 @@ var SpatializedContainer = withSSRSupported(
1650
1740
  );
1651
1741
 
1652
1742
  // src/spatialized-container/Spatialized2DElementContainer.tsx
1743
+ import { createPortal as createPortal2 } from "react-dom";
1653
1744
  import {
1654
1745
  forwardRef as forwardRef5,
1655
- useContext as useContext7,
1656
- useEffect as useEffect11
1746
+ useContext as useContext8,
1747
+ useEffect as useEffect12
1657
1748
  } from "react";
1658
- import { createPortal as createPortal2 } from "react-dom";
1659
- import { jsx as jsx7 } from "react/jsx-runtime";
1660
- function asyncLoadStyleToChildWindow(childWindow, n) {
1749
+
1750
+ // src/utils/windowStyleSync.ts
1751
+ function asyncLoadStyleToChildWindow(childWindow, link, isCurrent) {
1661
1752
  return new Promise((resolve) => {
1662
- n.href += "?uniqueURL=" + Math.random();
1663
- n.onerror = function(error) {
1664
- console.error("Failed to load style link", n.href);
1665
- resolve(false);
1753
+ const { href } = link;
1754
+ const sep = href.includes("?") ? "&" : "?";
1755
+ link.href = `${href}${sep}uniqueURL=${Math.random()}`;
1756
+ let finished = false;
1757
+ const finish = (ok) => {
1758
+ if (finished) return;
1759
+ finished = true;
1760
+ resolve(ok);
1761
+ };
1762
+ link.onerror = () => {
1763
+ finish(false);
1666
1764
  };
1667
- n.onload = function() {
1668
- resolve(true);
1765
+ link.onload = () => {
1766
+ if (!isCurrent()) {
1767
+ link.parentNode?.removeChild(link);
1768
+ finish(false);
1769
+ return;
1770
+ }
1771
+ finish(true);
1669
1772
  };
1670
1773
  setTimeout(() => {
1671
- childWindow.document.head.appendChild(n);
1774
+ if (!isCurrent()) {
1775
+ finish(false);
1776
+ return;
1777
+ }
1778
+ childWindow.document.head.appendChild(link);
1672
1779
  }, 50);
1673
1780
  });
1674
1781
  }
1782
+ var WEBSPATIAL_SYNC_ATTR = "data-webspatial-sync";
1783
+ var WEBSPATIAL_SYNC_KEY_ATTR = "data-webspatial-sync-key";
1675
1784
  function setOpenWindowStyle(openedWindow) {
1676
1785
  openedWindow.document.documentElement.style.cssText += document.documentElement.style.cssText;
1677
1786
  openedWindow.document.documentElement.style.backgroundColor = "transparent";
@@ -1683,23 +1792,118 @@ function setOpenWindowStyle(openedWindow) {
1683
1792
  openedWindow.document.body.style.minWidth = "fit-content";
1684
1793
  openedWindow.document.body.style.background = "transparent";
1685
1794
  }
1795
+ var controllers = /* @__PURE__ */ new WeakMap();
1796
+ function getController(childWindow) {
1797
+ const prev = controllers.get(childWindow);
1798
+ if (prev) return prev;
1799
+ const next = { version: 0 };
1800
+ controllers.set(childWindow, next);
1801
+ return next;
1802
+ }
1686
1803
  async function syncParentHeadToChild(childWindow) {
1804
+ const controller = getController(childWindow);
1805
+ const version2 = ++controller.version;
1687
1806
  const styleLoadedPromises = [];
1688
- for (let i = 0; i < document.head.children.length; i++) {
1689
- let n = document.head.children[i].cloneNode(true);
1690
- if (n.nodeName == "LINK" && n.rel == "stylesheet" && n.href) {
1691
- const promise = asyncLoadStyleToChildWindow(
1692
- childWindow,
1693
- n
1694
- );
1695
- styleLoadedPromises.push(promise);
1696
- } else {
1697
- childWindow.document.head.appendChild(n);
1698
- }
1807
+ const { head } = childWindow.document;
1808
+ const isCurrent = () => controller.version === version2;
1809
+ const parentStyles = Array.from(document.head.querySelectorAll("style"));
1810
+ const parentStylesheets = Array.from(
1811
+ document.head.querySelectorAll('link[rel="stylesheet"][href]')
1812
+ );
1813
+ const desiredStylesheetKeys = /* @__PURE__ */ new Set();
1814
+ for (const link of parentStylesheets) {
1815
+ if (link.href) desiredStylesheetKeys.add(link.href);
1816
+ }
1817
+ const existingSyncedLinks = Array.from(
1818
+ head.querySelectorAll(
1819
+ `link[rel="stylesheet"][${WEBSPATIAL_SYNC_ATTR}="1"]`
1820
+ )
1821
+ );
1822
+ for (const link of existingSyncedLinks) {
1823
+ const key = link.getAttribute(WEBSPATIAL_SYNC_KEY_ATTR) ?? link.href;
1824
+ if (!desiredStylesheetKeys.has(key)) link.parentNode?.removeChild(link);
1825
+ }
1826
+ const prevSyncedStyles = head.querySelectorAll(
1827
+ `style[${WEBSPATIAL_SYNC_ATTR}="1"]`
1828
+ );
1829
+ prevSyncedStyles.forEach((n) => n.parentNode?.removeChild(n));
1830
+ for (const styleEl of parentStyles) {
1831
+ const node = styleEl.cloneNode(true);
1832
+ node.setAttribute(WEBSPATIAL_SYNC_ATTR, "1");
1833
+ head.appendChild(node);
1834
+ }
1835
+ const currentKeys = /* @__PURE__ */ new Set();
1836
+ const currentSyncedLinks = Array.from(
1837
+ head.querySelectorAll(
1838
+ `link[rel="stylesheet"][${WEBSPATIAL_SYNC_ATTR}="1"]`
1839
+ )
1840
+ );
1841
+ for (const link of currentSyncedLinks) {
1842
+ currentKeys.add(link.getAttribute(WEBSPATIAL_SYNC_KEY_ATTR) ?? link.href);
1843
+ }
1844
+ for (const link of parentStylesheets) {
1845
+ const key = link.href;
1846
+ if (!key || currentKeys.has(key)) continue;
1847
+ const node = link.cloneNode(true);
1848
+ node.setAttribute(WEBSPATIAL_SYNC_ATTR, "1");
1849
+ node.setAttribute(WEBSPATIAL_SYNC_KEY_ATTR, key);
1850
+ styleLoadedPromises.push(
1851
+ asyncLoadStyleToChildWindow(childWindow, node, isCurrent)
1852
+ );
1699
1853
  }
1700
1854
  childWindow.document.documentElement.className = document.documentElement.className;
1701
1855
  return Promise.all(styleLoadedPromises);
1702
1856
  }
1857
+
1858
+ // src/utils/useSyncHeadStyles.ts
1859
+ import { useEffect as useEffect11 } from "react";
1860
+ function defaultShouldSync(mutations) {
1861
+ if (!Array.isArray(mutations) || mutations.length === 0) return false;
1862
+ for (const mutation of mutations) {
1863
+ const nodes = [
1864
+ ...Array.from(mutation.addedNodes),
1865
+ ...Array.from(mutation.removedNodes)
1866
+ ];
1867
+ for (const node of nodes) {
1868
+ if (!(node instanceof Element)) continue;
1869
+ const tag = node.tagName;
1870
+ if (tag === "STYLE") return true;
1871
+ if (tag === "LINK") {
1872
+ const { rel } = node;
1873
+ if (rel && rel.toLowerCase() === "stylesheet") return true;
1874
+ }
1875
+ }
1876
+ }
1877
+ return false;
1878
+ }
1879
+ function useSyncHeadStyles(childWindow, options) {
1880
+ const delayMs = 100;
1881
+ const subtree = options?.subtree ?? false;
1882
+ const immediate = options?.immediate ?? true;
1883
+ useEffect11(() => {
1884
+ if (!childWindow) return;
1885
+ let timer;
1886
+ const scheduleSync = () => {
1887
+ if (timer) window.clearTimeout(timer);
1888
+ timer = window.setTimeout(() => {
1889
+ syncParentHeadToChild(childWindow);
1890
+ }, delayMs);
1891
+ };
1892
+ if (immediate) scheduleSync();
1893
+ const observer = new MutationObserver((mutations) => {
1894
+ if (!defaultShouldSync(mutations)) return;
1895
+ scheduleSync();
1896
+ });
1897
+ observer.observe(document.head, { childList: true, subtree });
1898
+ return () => {
1899
+ if (timer) window.clearTimeout(timer);
1900
+ observer.disconnect();
1901
+ };
1902
+ }, [childWindow, delayMs, subtree, immediate]);
1903
+ }
1904
+
1905
+ // src/spatialized-container/Spatialized2DElementContainer.tsx
1906
+ import { jsx as jsx7 } from "react/jsx-runtime";
1703
1907
  function getJSXPortalInstance(inProps, portalInstanceObject) {
1704
1908
  const { component: El, style: inStyle = {}, ...props } = inProps;
1705
1909
  const extraStyle = {
@@ -1725,19 +1929,8 @@ function getJSXPortalInstance(inProps, portalInstanceObject) {
1725
1929
  };
1726
1930
  return /* @__PURE__ */ jsx7(El, { style, ...props });
1727
1931
  }
1728
- function useSyncHeaderStyle(windowProxy) {
1729
- useEffect11(() => {
1730
- const headObserver = new MutationObserver((_) => {
1731
- syncParentHeadToChild(windowProxy);
1732
- });
1733
- headObserver.observe(document.head, { childList: true, subtree: true });
1734
- return () => {
1735
- headObserver.disconnect();
1736
- };
1737
- }, []);
1738
- }
1739
1932
  function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
1740
- useEffect11(() => {
1933
+ useEffect12(() => {
1741
1934
  windowProxy.document.title = name;
1742
1935
  spatializedElement.updateProperties({
1743
1936
  name
@@ -1747,11 +1940,13 @@ function useSyncDocumentTitle(windowProxy, spatializedElement, name) {
1747
1940
  function SpatializedContent(props) {
1748
1941
  const { spatializedElement, ...restProps } = props;
1749
1942
  const spatialized2DElement = spatializedElement;
1750
- const windowProxy = spatialized2DElement.windowProxy;
1751
- useSyncHeaderStyle(windowProxy);
1943
+ const { windowProxy } = spatialized2DElement;
1944
+ useSyncHeadStyles(windowProxy, {
1945
+ subtree: false
1946
+ });
1752
1947
  const name = restProps["data-name"] || "";
1753
1948
  useSyncDocumentTitle(windowProxy, spatialized2DElement, name);
1754
- const portalInstanceObject = useContext7(
1949
+ const portalInstanceObject = useContext8(
1755
1950
  PortalInstanceContext
1756
1951
  );
1757
1952
  const JSXPortalInstance = getJSXPortalInstance(
@@ -1776,14 +1971,14 @@ function getExtraSpatializedElementProperties(computedStyle) {
1776
1971
  }
1777
1972
  async function createSpatializedElement() {
1778
1973
  const spatializedElement = await getSession().createSpatialized2DElement();
1779
- const windowProxy = spatializedElement.windowProxy;
1974
+ const { windowProxy } = spatializedElement;
1780
1975
  setOpenWindowStyle(windowProxy);
1781
1976
  await syncParentHeadToChild(windowProxy);
1782
1977
  const viewport = windowProxy.document.querySelector('meta[name="viewport"]');
1783
1978
  if (viewport) {
1784
1979
  viewport?.setAttribute(
1785
1980
  "content",
1786
- ` initial-scale=1.0, maximum-scale=1.0, user-scalable=no`
1981
+ " initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
1787
1982
  );
1788
1983
  } else {
1789
1984
  const meta = windowProxy.document.createElement("meta");
@@ -1813,9 +2008,10 @@ var Spatialized2DElementContainer = forwardRef5(
1813
2008
  import {
1814
2009
  forwardRef as forwardRef6,
1815
2010
  useCallback as useCallback6,
1816
- useContext as useContext8,
1817
- useEffect as useEffect12,
1818
- useMemo as useMemo3
2011
+ useContext as useContext9,
2012
+ useEffect as useEffect13,
2013
+ useMemo as useMemo3,
2014
+ useRef as useRef4
1819
2015
  } from "react";
1820
2016
  import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
1821
2017
  function getAbsoluteURL(url) {
@@ -1852,16 +2048,16 @@ function createLoadSuccessEvent(targetGetter) {
1852
2048
  function SpatializedContent2(props) {
1853
2049
  const { src, spatializedElement, onLoad, onError } = props;
1854
2050
  const spatializedStatic3DElement = spatializedElement;
1855
- const portalInstanceObject = useContext8(
2051
+ const portalInstanceObject = useContext9(
1856
2052
  PortalInstanceContext
1857
2053
  );
1858
2054
  const currentSrc = useMemo3(() => getAbsoluteURL(src), [src]);
1859
- useEffect12(() => {
2055
+ useEffect13(() => {
1860
2056
  if (src) {
1861
2057
  spatializedStatic3DElement.updateProperties({ modelURL: currentSrc });
1862
2058
  }
1863
2059
  }, [currentSrc]);
1864
- useEffect12(() => {
2060
+ useEffect13(() => {
1865
2061
  if (onLoad) {
1866
2062
  spatializedStatic3DElement.onLoadCallback = () => {
1867
2063
  onLoad(
@@ -1874,7 +2070,7 @@ function SpatializedContent2(props) {
1874
2070
  spatializedStatic3DElement.onLoadCallback = void 0;
1875
2071
  }
1876
2072
  }, [onLoad]);
1877
- useEffect12(() => {
2073
+ useEffect13(() => {
1878
2074
  if (onError) {
1879
2075
  spatializedStatic3DElement.onLoadFailureCallback = () => {
1880
2076
  onError(
@@ -1889,10 +2085,13 @@ function SpatializedContent2(props) {
1889
2085
  }, [onError]);
1890
2086
  return /* @__PURE__ */ jsx8(Fragment2, {});
1891
2087
  }
1892
- async function createSpatializedElement2() {
1893
- return getSession().createSpatializedStatic3DElement();
1894
- }
1895
2088
  function SpatializedStatic3DElementContainerBase(props, ref) {
2089
+ const promiseRef = useRef4(null);
2090
+ const createSpatializedElement2 = useCallback6(() => {
2091
+ const url = getAbsoluteURL(props.src);
2092
+ promiseRef.current = getSession().createSpatializedStatic3DElement(url);
2093
+ return promiseRef.current;
2094
+ }, []);
1896
2095
  const extraRefProps = useCallback6(
1897
2096
  (domProxy) => {
1898
2097
  let modelTransform = new DOMMatrixReadOnly();
@@ -1901,14 +2100,10 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1901
2100
  return getAbsoluteURL(props.src);
1902
2101
  },
1903
2102
  get ready() {
1904
- const spatializedElement = domProxy.__spatializedElement;
1905
- const promise = spatializedElement.ready.then((success) => {
1906
- if (success) {
1907
- return createLoadSuccessEvent(() => domProxy);
1908
- }
2103
+ return promiseRef.current.then((spatializedElement) => spatializedElement.ready).then((success) => {
2104
+ if (success) return createLoadSuccessEvent(() => domProxy);
1909
2105
  throw createLoadFailureEvent(() => domProxy);
1910
2106
  });
1911
- return promise;
1912
2107
  },
1913
2108
  get entityTransform() {
1914
2109
  return modelTransform;
@@ -1916,7 +2111,7 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1916
2111
  set entityTransform(value) {
1917
2112
  modelTransform = value;
1918
2113
  const spatializedElement = domProxy.__spatializedElement;
1919
- spatializedElement.updateModelTransform(modelTransform);
2114
+ spatializedElement?.updateModelTransform(modelTransform);
1920
2115
  }
1921
2116
  };
1922
2117
  },
@@ -1987,10 +2182,10 @@ function initScene(name, callback, options) {
1987
2182
  import { forwardRef as forwardRef9 } from "react";
1988
2183
 
1989
2184
  // src/spatialized-container-monitor/useMonitorDomChange.tsx
1990
- import { useRef as useRef4, useEffect as useEffect13, useMemo as useMemo4 } from "react";
2185
+ import { useRef as useRef5, useEffect as useEffect14, useMemo as useMemo4 } from "react";
1991
2186
  function useMonitorDomChange(inRef) {
1992
- const ref = useRef4(null);
1993
- useEffect13(() => {
2187
+ const ref = useRef5(null);
2188
+ useEffect14(() => {
1994
2189
  const observer = new MutationObserver((mutationsList) => {
1995
2190
  notifyDOMUpdate(mutationsList);
1996
2191
  });
@@ -2026,9 +2221,9 @@ function useMonitorDomChange(inRef) {
2026
2221
  }
2027
2222
 
2028
2223
  // src/spatialized-container-monitor/useMonitorDocumentHeaderChange.tsx
2029
- import { useEffect as useEffect14 } from "react";
2224
+ import { useEffect as useEffect15 } from "react";
2030
2225
  function useMonitorDocumentHeaderChange() {
2031
- useEffect14(() => {
2226
+ useEffect15(() => {
2032
2227
  const observer = new MutationObserver((mutationsList) => {
2033
2228
  notifyUpdateStandInstanceLayout();
2034
2229
  });
@@ -2088,17 +2283,66 @@ import { forwardRef as forwardRef11 } from "react";
2088
2283
  import { forwardRef as forwardRef10 } from "react";
2089
2284
 
2090
2285
  // src/reality/context/RealityContext.tsx
2091
- import { createContext as createContext5, useContext as useContext9 } from "react";
2092
- var RealityContext = createContext5(null);
2093
- var useRealityContext = () => useContext9(RealityContext);
2286
+ import { createContext as createContext6, useContext as useContext10 } from "react";
2287
+ var RealityContext = createContext6(null);
2288
+ var useRealityContext = () => useContext10(RealityContext);
2094
2289
 
2095
2290
  // src/reality/context/ParentContext.tsx
2096
- import { createContext as createContext6, useContext as useContext10 } from "react";
2097
- var ParentContext = createContext6(null);
2098
- var useParentContext = () => useContext10(ParentContext);
2291
+ import { createContext as createContext7, useContext as useContext11 } from "react";
2292
+ var ParentContext = createContext7(null);
2293
+ var useParentContext = () => useContext11(ParentContext);
2294
+
2295
+ // src/reality/context/AttachmentContext.tsx
2296
+ import { createContext as createContext8, useContext as useContext12 } from "react";
2297
+ var AttachmentRegistry = class {
2298
+ // name → (instanceId → container)
2299
+ containers = /* @__PURE__ */ new Map();
2300
+ listeners = /* @__PURE__ */ new Map();
2301
+ addContainer(name, instanceId, container) {
2302
+ if (!this.containers.has(name)) {
2303
+ this.containers.set(name, /* @__PURE__ */ new Map());
2304
+ }
2305
+ this.containers.get(name).set(instanceId, container);
2306
+ this.notifyListeners(name);
2307
+ }
2308
+ removeContainer(name, instanceId) {
2309
+ this.containers.get(name)?.delete(instanceId);
2310
+ if (this.containers.get(name)?.size === 0) {
2311
+ this.containers.delete(name);
2312
+ }
2313
+ this.notifyListeners(name);
2314
+ }
2315
+ getContainers(name) {
2316
+ const map = this.containers.get(name);
2317
+ return map ? Array.from(map.values()) : [];
2318
+ }
2319
+ onContainersChange(name, cb) {
2320
+ const current = this.getContainers(name);
2321
+ if (current.length > 0) {
2322
+ cb(current);
2323
+ }
2324
+ const prev = this.listeners.get(name);
2325
+ if (prev) prev([]);
2326
+ this.listeners.set(name, cb);
2327
+ return () => {
2328
+ if (this.listeners.get(name) === cb) {
2329
+ this.listeners.delete(name);
2330
+ }
2331
+ };
2332
+ }
2333
+ notifyListeners(name) {
2334
+ const cs = this.getContainers(name);
2335
+ this.listeners.get(name)?.(cs);
2336
+ }
2337
+ destroy() {
2338
+ this.containers.clear();
2339
+ this.listeners.clear();
2340
+ }
2341
+ };
2342
+ var AttachmentContext = createContext8(null);
2099
2343
 
2100
2344
  // src/reality/hooks/useEntityTransform.tsx
2101
- import { useEffect as useEffect15, useRef as useRef5 } from "react";
2345
+ import { useEffect as useEffect16, useRef as useRef6 } from "react";
2102
2346
 
2103
2347
  // src/reality/utils/ResourceRegistry.ts
2104
2348
  var ResourceRegistry = class {
@@ -2179,8 +2423,8 @@ var AbortResourceManager = class {
2179
2423
 
2180
2424
  // src/reality/hooks/useEntityTransform.tsx
2181
2425
  function useEntityTransform(entity, { position, rotation, scale }) {
2182
- const last = useRef5({});
2183
- useEffect15(() => {
2426
+ const last = useRef6({});
2427
+ useEffect16(() => {
2184
2428
  if (!entity) return;
2185
2429
  const shouldUpdate = !shallowEqualVec3(last.current.position, position) || !shallowEqualRotation(last.current.rotation, rotation) || !shallowEqualVec3(last.current.scale, scale);
2186
2430
  if (!shouldUpdate) return;
@@ -2199,7 +2443,7 @@ function useEntityTransform(entity, { position, rotation, scale }) {
2199
2443
  }
2200
2444
 
2201
2445
  // src/reality/hooks/useEntityEvent.tsx
2202
- import { useEffect as useEffect17, useRef as useRef7 } from "react";
2446
+ import { useEffect as useEffect18, useRef as useRef8 } from "react";
2203
2447
 
2204
2448
  // src/reality/type.ts
2205
2449
  var eventMap = {
@@ -2336,14 +2580,75 @@ function createEventProxy2(ev, instance) {
2336
2580
  }
2337
2581
  return void 0;
2338
2582
  }
2583
+ if (prop === "translationX") {
2584
+ const type = target.type;
2585
+ if (type === "spatialdrag") {
2586
+ return target.detail?.translation3D?.x ?? 0;
2587
+ }
2588
+ return void 0;
2589
+ }
2590
+ if (prop === "translationY") {
2591
+ const type = target.type;
2592
+ if (type === "spatialdrag") {
2593
+ return target.detail?.translation3D?.y ?? 0;
2594
+ }
2595
+ return void 0;
2596
+ }
2597
+ if (prop === "translationZ") {
2598
+ const type = target.type;
2599
+ if (type === "spatialdrag") {
2600
+ return target.detail?.translation3D?.z ?? 0;
2601
+ }
2602
+ return void 0;
2603
+ }
2604
+ if (prop === "quaternion") {
2605
+ const type = target.type;
2606
+ if (type === "spatialrotate") {
2607
+ return target.detail?.quaternion ?? {
2608
+ x: 0,
2609
+ y: 0,
2610
+ z: 0,
2611
+ w: 1
2612
+ };
2613
+ }
2614
+ return void 0;
2615
+ }
2616
+ if (prop === "magnification") {
2617
+ const type = target.type;
2618
+ if (type === "spatialmagnify") {
2619
+ return target.detail?.magnification ?? 1;
2620
+ }
2621
+ return void 0;
2622
+ }
2623
+ if (prop === "clientX") {
2624
+ const type = target.type;
2625
+ if (type === "spatialtap" || type === "spatialdragstart") {
2626
+ return target.detail?.globalLocation3D?.x ?? 0;
2627
+ }
2628
+ return void 0;
2629
+ }
2630
+ if (prop === "clientY") {
2631
+ const type = target.type;
2632
+ if (type === "spatialtap" || type === "spatialdragstart") {
2633
+ return target.detail?.globalLocation3D?.y ?? 0;
2634
+ }
2635
+ return void 0;
2636
+ }
2637
+ if (prop === "clientZ") {
2638
+ const type = target.type;
2639
+ if (type === "spatialtap" || type === "spatialdragstart") {
2640
+ return target.detail?.globalLocation3D?.z ?? 0;
2641
+ }
2642
+ return void 0;
2643
+ }
2339
2644
  const val = target[prop];
2340
2645
  return typeof val === "function" ? val.bind(target) : val;
2341
2646
  }
2342
2647
  });
2343
2648
  }
2344
2649
  var useEntityEvent = ({ instance, ...handlers }) => {
2345
- const eventsSetRef = useRef7(/* @__PURE__ */ new Set());
2346
- useEffect17(() => {
2650
+ const eventsSetRef = useRef8(/* @__PURE__ */ new Set());
2651
+ useEffect18(() => {
2347
2652
  const entity = instance.entity;
2348
2653
  if (!entity) return;
2349
2654
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
@@ -2356,7 +2661,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2356
2661
  return () => {
2357
2662
  };
2358
2663
  }, [instance.entity, ...Object.values(handlers)]);
2359
- useEffect17(() => {
2664
+ useEffect18(() => {
2360
2665
  const entity = instance.entity;
2361
2666
  if (!entity) return;
2362
2667
  return () => {
@@ -2370,10 +2675,10 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2370
2675
  };
2371
2676
 
2372
2677
  // src/reality/hooks/useEntityId.tsx
2373
- import { useEffect as useEffect18 } from "react";
2678
+ import { useEffect as useEffect19 } from "react";
2374
2679
  var useEntityId = ({ id, entity }) => {
2375
2680
  const ctx = useRealityContext();
2376
- useEffect18(() => {
2681
+ useEffect19(() => {
2377
2682
  if (!id || !entity || !ctx) return;
2378
2683
  ctx.resourceRegistry.add(id, Promise.resolve(entity));
2379
2684
  return () => {
@@ -2384,7 +2689,7 @@ var useEntityId = ({ id, entity }) => {
2384
2689
  };
2385
2690
 
2386
2691
  // src/reality/hooks/useEntity.tsx
2387
- import { useEffect as useEffect19, useRef as useRef8 } from "react";
2692
+ import { useEffect as useEffect20, useRef as useRef9 } from "react";
2388
2693
  var useEntity = ({
2389
2694
  ref,
2390
2695
  id,
@@ -2406,9 +2711,9 @@ var useEntity = ({
2406
2711
  }) => {
2407
2712
  const ctx = useRealityContext();
2408
2713
  const parent = useParentContext();
2409
- const instanceRef = useRef8(new EntityRef(null, ctx));
2714
+ const instanceRef = useRef9(new EntityRef(null, ctx));
2410
2715
  const forceUpdate = useForceUpdate2();
2411
- useEffect19(() => {
2716
+ useEffect20(() => {
2412
2717
  if (!ctx) return;
2413
2718
  const controller = new AbortController();
2414
2719
  const init = async () => {
@@ -2565,11 +2870,11 @@ var BoxEntity = forwardRef13(
2565
2870
  );
2566
2871
 
2567
2872
  // src/reality/components/UnlitMaterial.tsx
2568
- import { useEffect as useEffect20, useRef as useRef9 } from "react";
2873
+ import { useEffect as useEffect21, useRef as useRef10 } from "react";
2569
2874
  var UnlitMaterial = ({ children, ...options }) => {
2570
2875
  const ctx = useRealityContext();
2571
- const materialRef = useRef9();
2572
- useEffect20(() => {
2876
+ const materialRef = useRef10();
2877
+ useEffect21(() => {
2573
2878
  if (!ctx) return;
2574
2879
  const { session, reality, resourceRegistry } = ctx;
2575
2880
  const init = async () => {
@@ -2685,7 +2990,7 @@ var SceneGraph = ({ children }) => {
2685
2990
  };
2686
2991
 
2687
2992
  // src/reality/components/ModelAsset.tsx
2688
- import { useEffect as useEffect21, useRef as useRef10 } from "react";
2993
+ import { useEffect as useEffect22, useRef as useRef11 } from "react";
2689
2994
  var resolveAssetUrl = (url) => {
2690
2995
  if (url.startsWith("http://") || url.startsWith("https://")) {
2691
2996
  return url;
@@ -2694,8 +2999,8 @@ var resolveAssetUrl = (url) => {
2694
2999
  };
2695
3000
  var ModelAsset = ({ children, ...options }) => {
2696
3001
  const ctx = useRealityContext();
2697
- const materialRef = useRef10();
2698
- useEffect21(() => {
3002
+ const materialRef = useRef11();
3003
+ useEffect22(() => {
2699
3004
  const controller = new AbortController();
2700
3005
  if (!ctx) return;
2701
3006
  const { session, reality, resourceRegistry } = ctx;
@@ -2762,13 +3067,20 @@ var ModelEntity = forwardRef18(
2762
3067
  import {
2763
3068
  forwardRef as forwardRef19,
2764
3069
  useCallback as useCallback8,
2765
- useEffect as useEffect22,
2766
- useRef as useRef11,
3070
+ useEffect as useEffect23,
3071
+ useRef as useRef12,
2767
3072
  useState as useState8
2768
3073
  } from "react";
2769
3074
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
2770
3075
  var Reality = forwardRef19(
2771
3076
  function RealityBase({ children, ...inProps }, ref) {
3077
+ const insideAttachment = useInsideAttachment();
3078
+ if (insideAttachment) {
3079
+ console.warn(
3080
+ "[WebSpatial] Reality cannot be used inside AttachmentAsset."
3081
+ );
3082
+ return null;
3083
+ }
2772
3084
  const {
2773
3085
  onSpatialTap,
2774
3086
  onSpatialDragStart,
@@ -2780,16 +3092,17 @@ var Reality = forwardRef19(
2780
3092
  onSpatialMagnifyEnd,
2781
3093
  ...props
2782
3094
  } = inProps;
2783
- const ctxRef = useRef11(null);
2784
- const creationId = useRef11(0);
3095
+ const ctxRef = useRef12(null);
3096
+ const creationId = useRef12(0);
2785
3097
  const [isReady, setIsReady] = useState8(false);
2786
3098
  const cleanupReality = useCallback8(() => {
3099
+ ctxRef.current?.attachmentRegistry.destroy();
2787
3100
  ctxRef.current?.resourceRegistry.destroy();
2788
3101
  ctxRef.current?.reality.destroy();
2789
3102
  ctxRef.current = null;
2790
3103
  setIsReady(false);
2791
3104
  }, []);
2792
- useEffect22(() => {
3105
+ useEffect23(() => {
2793
3106
  return () => {
2794
3107
  creationId.current++;
2795
3108
  cleanupReality();
@@ -2798,15 +3111,18 @@ var Reality = forwardRef19(
2798
3111
  const createReality = useCallback8(async () => {
2799
3112
  const id = ++creationId.current;
2800
3113
  const resourceRegistry = new ResourceRegistry();
3114
+ const attachmentRegistry = new AttachmentRegistry();
2801
3115
  const session = await getSession();
2802
3116
  if (!session) {
2803
3117
  resourceRegistry.destroy();
3118
+ attachmentRegistry.destroy();
2804
3119
  return null;
2805
3120
  }
2806
3121
  const reality = await session.createSpatializedDynamic3DElement();
2807
3122
  const isCancelled = () => id !== creationId.current;
2808
3123
  if (isCancelled()) {
2809
3124
  resourceRegistry.destroy();
3125
+ attachmentRegistry.destroy();
2810
3126
  reality.destroy();
2811
3127
  return null;
2812
3128
  }
@@ -2814,16 +3130,23 @@ var Reality = forwardRef19(
2814
3130
  const result = await session.getSpatialScene().addSpatializedElement(reality);
2815
3131
  if (!result.success || isCancelled()) {
2816
3132
  resourceRegistry.destroy();
3133
+ attachmentRegistry.destroy();
2817
3134
  reality.destroy();
2818
3135
  return null;
2819
3136
  }
2820
3137
  cleanupReality();
2821
- ctxRef.current = { session, reality, resourceRegistry };
3138
+ ctxRef.current = {
3139
+ session,
3140
+ reality,
3141
+ resourceRegistry,
3142
+ attachmentRegistry
3143
+ };
2822
3144
  setIsReady(true);
2823
3145
  return reality;
2824
3146
  } catch (err) {
2825
3147
  console.error("[createReality] failed", err);
2826
3148
  resourceRegistry.destroy();
3149
+ attachmentRegistry.destroy();
2827
3150
  reality.destroy();
2828
3151
  return null;
2829
3152
  }
@@ -2845,14 +3168,133 @@ var Reality = forwardRef19(
2845
3168
  }
2846
3169
  );
2847
3170
 
3171
+ // src/reality/components/AttachmentAsset.tsx
3172
+ import { useEffect as useEffect24, useState as useState9 } from "react";
3173
+ import { createPortal as createPortal3 } from "react-dom";
3174
+ import { jsx as jsx23 } from "react/jsx-runtime";
3175
+ var AttachmentAsset = ({
3176
+ name,
3177
+ children
3178
+ }) => {
3179
+ const ctx = useRealityContext();
3180
+ const [containers, setContainers] = useState9([]);
3181
+ useEffect24(() => {
3182
+ if (!ctx) return;
3183
+ return ctx.attachmentRegistry.onContainersChange(name, setContainers);
3184
+ }, [ctx, name]);
3185
+ if (!containers.length) return null;
3186
+ return /* @__PURE__ */ jsx23(InsideAttachmentContext.Provider, { value: true, children: containers.map((c, idx) => createPortal3(children, c, `${name}-${idx}`)) });
3187
+ };
3188
+
3189
+ // src/reality/components/AttachmentEntity.tsx
3190
+ import { useEffect as useEffect25, useRef as useRef13, useState as useState10 } from "react";
3191
+ var instanceCounter = 0;
3192
+ var AttachmentEntity = ({
3193
+ attachment: attachmentName,
3194
+ position,
3195
+ size
3196
+ }) => {
3197
+ const ctx = useRealityContext();
3198
+ const parent = useParentContext();
3199
+ const attachmentRef = useRef13(null);
3200
+ const parentIdRef = useRef13(null);
3201
+ const instanceIdRef = useRef13(`att_${++instanceCounter}`);
3202
+ const attachmentNameRef = useRef13(attachmentName);
3203
+ const [childWindow, setChildWindow] = useState10(null);
3204
+ useEffect25(() => {
3205
+ if (!ctx || !parent) return;
3206
+ const parentId = parent.id;
3207
+ parentIdRef.current = parentId;
3208
+ let cancelled = false;
3209
+ const init = async () => {
3210
+ try {
3211
+ const att = await ctx.session.createAttachmentEntity({
3212
+ parentEntityId: parentId,
3213
+ position: position ?? [0, 0, 0],
3214
+ size
3215
+ });
3216
+ if (cancelled) {
3217
+ att.destroy();
3218
+ return;
3219
+ }
3220
+ const windowProxy = att.getWindowProxy();
3221
+ setOpenWindowStyle(windowProxy);
3222
+ windowProxy.document.body.style.display = "block";
3223
+ windowProxy.document.body.style.minWidth = "100%";
3224
+ windowProxy.document.body.style.maxWidth = "100%";
3225
+ windowProxy.document.body.style.minHeight = "100%";
3226
+ await syncParentHeadToChild(windowProxy);
3227
+ const viewport = windowProxy.document.querySelector(
3228
+ 'meta[name="viewport"]'
3229
+ );
3230
+ if (!viewport) {
3231
+ const meta = windowProxy.document.createElement("meta");
3232
+ meta.name = "viewport";
3233
+ meta.content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
3234
+ windowProxy.document.head.appendChild(meta);
3235
+ }
3236
+ const base = windowProxy.document.createElement("base");
3237
+ base.href = document.baseURI;
3238
+ windowProxy.document.head.appendChild(base);
3239
+ attachmentRef.current = att;
3240
+ setChildWindow(windowProxy);
3241
+ ctx.attachmentRegistry.addContainer(
3242
+ attachmentNameRef.current,
3243
+ instanceIdRef.current,
3244
+ att.getContainer()
3245
+ );
3246
+ } catch (error) {
3247
+ console.error("[AttachmentEntity] init error:", error);
3248
+ }
3249
+ };
3250
+ init();
3251
+ return () => {
3252
+ cancelled = true;
3253
+ const att = attachmentRef.current;
3254
+ if (att) {
3255
+ ctx.attachmentRegistry.removeContainer(
3256
+ attachmentNameRef.current,
3257
+ instanceIdRef.current
3258
+ );
3259
+ att.destroy();
3260
+ attachmentRef.current = null;
3261
+ setChildWindow(null);
3262
+ }
3263
+ };
3264
+ }, [ctx, parent]);
3265
+ useEffect25(() => {
3266
+ if (!ctx) return;
3267
+ const att = attachmentRef.current;
3268
+ const prevName = attachmentNameRef.current;
3269
+ if (att && prevName !== attachmentName) {
3270
+ ctx.attachmentRegistry.removeContainer(prevName, instanceIdRef.current);
3271
+ ctx.attachmentRegistry.addContainer(
3272
+ attachmentName,
3273
+ instanceIdRef.current,
3274
+ att.getContainer()
3275
+ );
3276
+ attachmentNameRef.current = attachmentName;
3277
+ } else {
3278
+ attachmentNameRef.current = attachmentName;
3279
+ }
3280
+ }, [ctx, attachmentName]);
3281
+ useSyncHeadStyles(childWindow, { subtree: false });
3282
+ useEffect25(() => {
3283
+ if (!attachmentRef.current) return;
3284
+ attachmentRef.current.update({ position, size });
3285
+ }, [position?.[0], position?.[1], position?.[2], size?.width, size?.height]);
3286
+ return null;
3287
+ };
3288
+
2848
3289
  // src/Model.tsx
2849
3290
  import { forwardRef as forwardRef20 } from "react";
2850
3291
  import { Spatial as Spatial2 } from "@webspatial/core-sdk";
2851
- import { jsx as jsx23 } from "react/jsx-runtime";
3292
+ import { jsx as jsx24 } from "react/jsx-runtime";
2852
3293
  var spatial2 = new Spatial2();
2853
3294
  function ModelBase(props, ref) {
3295
+ const insideAttachment = useInsideAttachment();
2854
3296
  const { "enable-xr": enableXR, ...restProps } = props;
2855
- if (!enableXR || !spatial2.runInSpatialWeb()) {
3297
+ if (!enableXR || !spatial2.runInSpatialWeb() || insideAttachment) {
2856
3298
  const {
2857
3299
  onSpatialTap,
2858
3300
  onSpatialDragStart,
@@ -2864,19 +3306,21 @@ function ModelBase(props, ref) {
2864
3306
  onSpatialMagnifyEnd,
2865
3307
  ...modelProps
2866
3308
  } = restProps;
2867
- return /* @__PURE__ */ jsx23("model", { ref, ...modelProps });
3309
+ return /* @__PURE__ */ jsx24("model", { ref, ...modelProps });
2868
3310
  }
2869
- return /* @__PURE__ */ jsx23(SpatializedStatic3DElementContainer, { ref, ...restProps });
3311
+ return /* @__PURE__ */ jsx24(SpatializedStatic3DElementContainer, { ref, ...restProps });
2870
3312
  }
2871
3313
  var Model = withSSRSupported(forwardRef20(ModelBase));
2872
3314
  Model.displayName = "Model";
2873
3315
 
2874
3316
  // src/index.ts
2875
- var version = "1.2.0";
3317
+ var version = "1.3.0";
2876
3318
  if (typeof window !== "undefined") {
2877
3319
  initPolyfill();
2878
3320
  }
2879
3321
  export {
3322
+ AttachmentAsset,
3323
+ AttachmentEntity,
2880
3324
  BoxEntity,
2881
3325
  ConeEntity,
2882
3326
  CylinderEntity,