@webspatial/react-sdk 1.2.1 → 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.1"
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";
@@ -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: createSpatializedElement2,
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(() => {
@@ -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);
1666
1761
  };
1667
- n.onload = function() {
1668
- resolve(true);
1762
+ link.onerror = () => {
1763
+ finish(false);
1764
+ };
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,11 +2008,9 @@ var Spatialized2DElementContainer = forwardRef5(
1813
2008
  import {
1814
2009
  forwardRef as forwardRef6,
1815
2010
  useCallback as useCallback6,
1816
- useContext as useContext8,
1817
- useEffect as useEffect12,
2011
+ useContext as useContext9,
2012
+ useEffect as useEffect13,
1818
2013
  useMemo as useMemo3,
1819
- useState as useState6,
1820
- useImperativeHandle,
1821
2014
  useRef as useRef4
1822
2015
  } from "react";
1823
2016
  import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime";
@@ -1855,16 +2048,16 @@ function createLoadSuccessEvent(targetGetter) {
1855
2048
  function SpatializedContent2(props) {
1856
2049
  const { src, spatializedElement, onLoad, onError } = props;
1857
2050
  const spatializedStatic3DElement = spatializedElement;
1858
- const portalInstanceObject = useContext8(
2051
+ const portalInstanceObject = useContext9(
1859
2052
  PortalInstanceContext
1860
2053
  );
1861
2054
  const currentSrc = useMemo3(() => getAbsoluteURL(src), [src]);
1862
- useEffect12(() => {
2055
+ useEffect13(() => {
1863
2056
  if (src) {
1864
2057
  spatializedStatic3DElement.updateProperties({ modelURL: currentSrc });
1865
2058
  }
1866
2059
  }, [currentSrc]);
1867
- useEffect12(() => {
2060
+ useEffect13(() => {
1868
2061
  if (onLoad) {
1869
2062
  spatializedStatic3DElement.onLoadCallback = () => {
1870
2063
  onLoad(
@@ -1877,7 +2070,7 @@ function SpatializedContent2(props) {
1877
2070
  spatializedStatic3DElement.onLoadCallback = void 0;
1878
2071
  }
1879
2072
  }, [onLoad]);
1880
- useEffect12(() => {
2073
+ useEffect13(() => {
1881
2074
  if (onError) {
1882
2075
  spatializedStatic3DElement.onLoadFailureCallback = () => {
1883
2076
  onError(
@@ -1893,14 +2086,12 @@ function SpatializedContent2(props) {
1893
2086
  return /* @__PURE__ */ jsx8(Fragment2, {});
1894
2087
  }
1895
2088
  function SpatializedStatic3DElementContainerBase(props, ref) {
1896
- const containerRef = useRef4(null);
1897
- const [elementCreated, setElementCreated] = useState6(false);
1898
- useImperativeHandle(ref, () => elementCreated ? containerRef.current : null, [elementCreated]);
1899
- const createSpatializedElement2 = useCallback6(async () => {
1900
- const element = await getSession().createSpatializedStatic3DElement();
1901
- setElementCreated(true);
1902
- return element;
1903
- }, [setElementCreated]);
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
+ }, []);
1904
2095
  const extraRefProps = useCallback6(
1905
2096
  (domProxy) => {
1906
2097
  let modelTransform = new DOMMatrixReadOnly();
@@ -1909,14 +2100,10 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1909
2100
  return getAbsoluteURL(props.src);
1910
2101
  },
1911
2102
  get ready() {
1912
- const spatializedElement = domProxy.__spatializedElement;
1913
- const promise = spatializedElement.ready.then((success) => {
1914
- if (success) {
1915
- return createLoadSuccessEvent(() => domProxy);
1916
- }
2103
+ return promiseRef.current.then((spatializedElement) => spatializedElement.ready).then((success) => {
2104
+ if (success) return createLoadSuccessEvent(() => domProxy);
1917
2105
  throw createLoadFailureEvent(() => domProxy);
1918
2106
  });
1919
- return promise;
1920
2107
  },
1921
2108
  get entityTransform() {
1922
2109
  return modelTransform;
@@ -1924,7 +2111,7 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1924
2111
  set entityTransform(value) {
1925
2112
  modelTransform = value;
1926
2113
  const spatializedElement = domProxy.__spatializedElement;
1927
- spatializedElement.updateModelTransform(modelTransform);
2114
+ spatializedElement?.updateModelTransform(modelTransform);
1928
2115
  }
1929
2116
  };
1930
2117
  },
@@ -1933,7 +2120,7 @@ function SpatializedStatic3DElementContainerBase(props, ref) {
1933
2120
  return /* @__PURE__ */ jsx8(
1934
2121
  SpatializedContainer,
1935
2122
  {
1936
- ref: containerRef,
2123
+ ref,
1937
2124
  component: "div",
1938
2125
  createSpatializedElement: createSpatializedElement2,
1939
2126
  spatializedContent: SpatializedContent2,
@@ -1995,10 +2182,10 @@ function initScene(name, callback, options) {
1995
2182
  import { forwardRef as forwardRef9 } from "react";
1996
2183
 
1997
2184
  // src/spatialized-container-monitor/useMonitorDomChange.tsx
1998
- import { useRef as useRef5, useEffect as useEffect13, useMemo as useMemo4 } from "react";
2185
+ import { useRef as useRef5, useEffect as useEffect14, useMemo as useMemo4 } from "react";
1999
2186
  function useMonitorDomChange(inRef) {
2000
2187
  const ref = useRef5(null);
2001
- useEffect13(() => {
2188
+ useEffect14(() => {
2002
2189
  const observer = new MutationObserver((mutationsList) => {
2003
2190
  notifyDOMUpdate(mutationsList);
2004
2191
  });
@@ -2034,9 +2221,9 @@ function useMonitorDomChange(inRef) {
2034
2221
  }
2035
2222
 
2036
2223
  // src/spatialized-container-monitor/useMonitorDocumentHeaderChange.tsx
2037
- import { useEffect as useEffect14 } from "react";
2224
+ import { useEffect as useEffect15 } from "react";
2038
2225
  function useMonitorDocumentHeaderChange() {
2039
- useEffect14(() => {
2226
+ useEffect15(() => {
2040
2227
  const observer = new MutationObserver((mutationsList) => {
2041
2228
  notifyUpdateStandInstanceLayout();
2042
2229
  });
@@ -2096,17 +2283,66 @@ import { forwardRef as forwardRef11 } from "react";
2096
2283
  import { forwardRef as forwardRef10 } from "react";
2097
2284
 
2098
2285
  // src/reality/context/RealityContext.tsx
2099
- import { createContext as createContext5, useContext as useContext9 } from "react";
2100
- var RealityContext = createContext5(null);
2101
- var useRealityContext = () => useContext9(RealityContext);
2286
+ import { createContext as createContext6, useContext as useContext10 } from "react";
2287
+ var RealityContext = createContext6(null);
2288
+ var useRealityContext = () => useContext10(RealityContext);
2102
2289
 
2103
2290
  // src/reality/context/ParentContext.tsx
2104
- import { createContext as createContext6, useContext as useContext10 } from "react";
2105
- var ParentContext = createContext6(null);
2106
- 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);
2107
2343
 
2108
2344
  // src/reality/hooks/useEntityTransform.tsx
2109
- import { useEffect as useEffect15, useRef as useRef6 } from "react";
2345
+ import { useEffect as useEffect16, useRef as useRef6 } from "react";
2110
2346
 
2111
2347
  // src/reality/utils/ResourceRegistry.ts
2112
2348
  var ResourceRegistry = class {
@@ -2188,7 +2424,7 @@ var AbortResourceManager = class {
2188
2424
  // src/reality/hooks/useEntityTransform.tsx
2189
2425
  function useEntityTransform(entity, { position, rotation, scale }) {
2190
2426
  const last = useRef6({});
2191
- useEffect15(() => {
2427
+ useEffect16(() => {
2192
2428
  if (!entity) return;
2193
2429
  const shouldUpdate = !shallowEqualVec3(last.current.position, position) || !shallowEqualRotation(last.current.rotation, rotation) || !shallowEqualVec3(last.current.scale, scale);
2194
2430
  if (!shouldUpdate) return;
@@ -2207,7 +2443,7 @@ function useEntityTransform(entity, { position, rotation, scale }) {
2207
2443
  }
2208
2444
 
2209
2445
  // src/reality/hooks/useEntityEvent.tsx
2210
- import { useEffect as useEffect17, useRef as useRef8 } from "react";
2446
+ import { useEffect as useEffect18, useRef as useRef8 } from "react";
2211
2447
 
2212
2448
  // src/reality/type.ts
2213
2449
  var eventMap = {
@@ -2228,9 +2464,9 @@ var eventMap = {
2228
2464
  };
2229
2465
 
2230
2466
  // src/reality/hooks/useEntityRef.tsx
2231
- import { useImperativeHandle as useImperativeHandle2 } from "react";
2467
+ import { useImperativeHandle } from "react";
2232
2468
  var useEntityRef = (ref, instance) => {
2233
- useImperativeHandle2(ref, () => instance);
2469
+ useImperativeHandle(ref, () => instance);
2234
2470
  };
2235
2471
  var EntityRef = class {
2236
2472
  _entity;
@@ -2344,6 +2580,67 @@ function createEventProxy2(ev, instance) {
2344
2580
  }
2345
2581
  return void 0;
2346
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
+ }
2347
2644
  const val = target[prop];
2348
2645
  return typeof val === "function" ? val.bind(target) : val;
2349
2646
  }
@@ -2351,7 +2648,7 @@ function createEventProxy2(ev, instance) {
2351
2648
  }
2352
2649
  var useEntityEvent = ({ instance, ...handlers }) => {
2353
2650
  const eventsSetRef = useRef8(/* @__PURE__ */ new Set());
2354
- useEffect17(() => {
2651
+ useEffect18(() => {
2355
2652
  const entity = instance.entity;
2356
2653
  if (!entity) return;
2357
2654
  Object.entries(eventMap).forEach(([reactKey, spatialEvent]) => {
@@ -2364,7 +2661,7 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2364
2661
  return () => {
2365
2662
  };
2366
2663
  }, [instance.entity, ...Object.values(handlers)]);
2367
- useEffect17(() => {
2664
+ useEffect18(() => {
2368
2665
  const entity = instance.entity;
2369
2666
  if (!entity) return;
2370
2667
  return () => {
@@ -2378,10 +2675,10 @@ var useEntityEvent = ({ instance, ...handlers }) => {
2378
2675
  };
2379
2676
 
2380
2677
  // src/reality/hooks/useEntityId.tsx
2381
- import { useEffect as useEffect18 } from "react";
2678
+ import { useEffect as useEffect19 } from "react";
2382
2679
  var useEntityId = ({ id, entity }) => {
2383
2680
  const ctx = useRealityContext();
2384
- useEffect18(() => {
2681
+ useEffect19(() => {
2385
2682
  if (!id || !entity || !ctx) return;
2386
2683
  ctx.resourceRegistry.add(id, Promise.resolve(entity));
2387
2684
  return () => {
@@ -2392,7 +2689,7 @@ var useEntityId = ({ id, entity }) => {
2392
2689
  };
2393
2690
 
2394
2691
  // src/reality/hooks/useEntity.tsx
2395
- import { useEffect as useEffect19, useRef as useRef9 } from "react";
2692
+ import { useEffect as useEffect20, useRef as useRef9 } from "react";
2396
2693
  var useEntity = ({
2397
2694
  ref,
2398
2695
  id,
@@ -2416,7 +2713,7 @@ var useEntity = ({
2416
2713
  const parent = useParentContext();
2417
2714
  const instanceRef = useRef9(new EntityRef(null, ctx));
2418
2715
  const forceUpdate = useForceUpdate2();
2419
- useEffect19(() => {
2716
+ useEffect20(() => {
2420
2717
  if (!ctx) return;
2421
2718
  const controller = new AbortController();
2422
2719
  const init = async () => {
@@ -2466,9 +2763,9 @@ var useEntity = ({
2466
2763
  };
2467
2764
 
2468
2765
  // src/reality/hooks/useForceUpdate.tsx
2469
- import { useCallback as useCallback7, useState as useState8 } from "react";
2766
+ import { useCallback as useCallback7, useState as useState7 } from "react";
2470
2767
  var useForceUpdate2 = () => {
2471
- const [, setTick] = useState8(0);
2768
+ const [, setTick] = useState7(0);
2472
2769
  return useCallback7(() => setTick((tick) => tick + 1), []);
2473
2770
  };
2474
2771
 
@@ -2573,11 +2870,11 @@ var BoxEntity = forwardRef13(
2573
2870
  );
2574
2871
 
2575
2872
  // src/reality/components/UnlitMaterial.tsx
2576
- import { useEffect as useEffect20, useRef as useRef10 } from "react";
2873
+ import { useEffect as useEffect21, useRef as useRef10 } from "react";
2577
2874
  var UnlitMaterial = ({ children, ...options }) => {
2578
2875
  const ctx = useRealityContext();
2579
2876
  const materialRef = useRef10();
2580
- useEffect20(() => {
2877
+ useEffect21(() => {
2581
2878
  if (!ctx) return;
2582
2879
  const { session, reality, resourceRegistry } = ctx;
2583
2880
  const init = async () => {
@@ -2693,7 +2990,7 @@ var SceneGraph = ({ children }) => {
2693
2990
  };
2694
2991
 
2695
2992
  // src/reality/components/ModelAsset.tsx
2696
- import { useEffect as useEffect21, useRef as useRef11 } from "react";
2993
+ import { useEffect as useEffect22, useRef as useRef11 } from "react";
2697
2994
  var resolveAssetUrl = (url) => {
2698
2995
  if (url.startsWith("http://") || url.startsWith("https://")) {
2699
2996
  return url;
@@ -2703,7 +3000,7 @@ var resolveAssetUrl = (url) => {
2703
3000
  var ModelAsset = ({ children, ...options }) => {
2704
3001
  const ctx = useRealityContext();
2705
3002
  const materialRef = useRef11();
2706
- useEffect21(() => {
3003
+ useEffect22(() => {
2707
3004
  const controller = new AbortController();
2708
3005
  if (!ctx) return;
2709
3006
  const { session, reality, resourceRegistry } = ctx;
@@ -2770,13 +3067,20 @@ var ModelEntity = forwardRef18(
2770
3067
  import {
2771
3068
  forwardRef as forwardRef19,
2772
3069
  useCallback as useCallback8,
2773
- useEffect as useEffect22,
3070
+ useEffect as useEffect23,
2774
3071
  useRef as useRef12,
2775
- useState as useState9
3072
+ useState as useState8
2776
3073
  } from "react";
2777
3074
  import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
2778
3075
  var Reality = forwardRef19(
2779
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
+ }
2780
3084
  const {
2781
3085
  onSpatialTap,
2782
3086
  onSpatialDragStart,
@@ -2790,14 +3094,15 @@ var Reality = forwardRef19(
2790
3094
  } = inProps;
2791
3095
  const ctxRef = useRef12(null);
2792
3096
  const creationId = useRef12(0);
2793
- const [isReady, setIsReady] = useState9(false);
3097
+ const [isReady, setIsReady] = useState8(false);
2794
3098
  const cleanupReality = useCallback8(() => {
3099
+ ctxRef.current?.attachmentRegistry.destroy();
2795
3100
  ctxRef.current?.resourceRegistry.destroy();
2796
3101
  ctxRef.current?.reality.destroy();
2797
3102
  ctxRef.current = null;
2798
3103
  setIsReady(false);
2799
3104
  }, []);
2800
- useEffect22(() => {
3105
+ useEffect23(() => {
2801
3106
  return () => {
2802
3107
  creationId.current++;
2803
3108
  cleanupReality();
@@ -2806,15 +3111,18 @@ var Reality = forwardRef19(
2806
3111
  const createReality = useCallback8(async () => {
2807
3112
  const id = ++creationId.current;
2808
3113
  const resourceRegistry = new ResourceRegistry();
3114
+ const attachmentRegistry = new AttachmentRegistry();
2809
3115
  const session = await getSession();
2810
3116
  if (!session) {
2811
3117
  resourceRegistry.destroy();
3118
+ attachmentRegistry.destroy();
2812
3119
  return null;
2813
3120
  }
2814
3121
  const reality = await session.createSpatializedDynamic3DElement();
2815
3122
  const isCancelled = () => id !== creationId.current;
2816
3123
  if (isCancelled()) {
2817
3124
  resourceRegistry.destroy();
3125
+ attachmentRegistry.destroy();
2818
3126
  reality.destroy();
2819
3127
  return null;
2820
3128
  }
@@ -2822,16 +3130,23 @@ var Reality = forwardRef19(
2822
3130
  const result = await session.getSpatialScene().addSpatializedElement(reality);
2823
3131
  if (!result.success || isCancelled()) {
2824
3132
  resourceRegistry.destroy();
3133
+ attachmentRegistry.destroy();
2825
3134
  reality.destroy();
2826
3135
  return null;
2827
3136
  }
2828
3137
  cleanupReality();
2829
- ctxRef.current = { session, reality, resourceRegistry };
3138
+ ctxRef.current = {
3139
+ session,
3140
+ reality,
3141
+ resourceRegistry,
3142
+ attachmentRegistry
3143
+ };
2830
3144
  setIsReady(true);
2831
3145
  return reality;
2832
3146
  } catch (err) {
2833
3147
  console.error("[createReality] failed", err);
2834
3148
  resourceRegistry.destroy();
3149
+ attachmentRegistry.destroy();
2835
3150
  reality.destroy();
2836
3151
  return null;
2837
3152
  }
@@ -2853,14 +3168,133 @@ var Reality = forwardRef19(
2853
3168
  }
2854
3169
  );
2855
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
+
2856
3289
  // src/Model.tsx
2857
3290
  import { forwardRef as forwardRef20 } from "react";
2858
3291
  import { Spatial as Spatial2 } from "@webspatial/core-sdk";
2859
- import { jsx as jsx23 } from "react/jsx-runtime";
3292
+ import { jsx as jsx24 } from "react/jsx-runtime";
2860
3293
  var spatial2 = new Spatial2();
2861
3294
  function ModelBase(props, ref) {
3295
+ const insideAttachment = useInsideAttachment();
2862
3296
  const { "enable-xr": enableXR, ...restProps } = props;
2863
- if (!enableXR || !spatial2.runInSpatialWeb()) {
3297
+ if (!enableXR || !spatial2.runInSpatialWeb() || insideAttachment) {
2864
3298
  const {
2865
3299
  onSpatialTap,
2866
3300
  onSpatialDragStart,
@@ -2872,19 +3306,21 @@ function ModelBase(props, ref) {
2872
3306
  onSpatialMagnifyEnd,
2873
3307
  ...modelProps
2874
3308
  } = restProps;
2875
- return /* @__PURE__ */ jsx23("model", { ref, ...modelProps });
3309
+ return /* @__PURE__ */ jsx24("model", { ref, ...modelProps });
2876
3310
  }
2877
- return /* @__PURE__ */ jsx23(SpatializedStatic3DElementContainer, { ref, ...restProps });
3311
+ return /* @__PURE__ */ jsx24(SpatializedStatic3DElementContainer, { ref, ...restProps });
2878
3312
  }
2879
3313
  var Model = withSSRSupported(forwardRef20(ModelBase));
2880
3314
  Model.displayName = "Model";
2881
3315
 
2882
3316
  // src/index.ts
2883
- var version = "1.2.1";
3317
+ var version = "1.3.0";
2884
3318
  if (typeof window !== "undefined") {
2885
3319
  initPolyfill();
2886
3320
  }
2887
3321
  export {
3322
+ AttachmentAsset,
3323
+ AttachmentEntity,
2888
3324
  BoxEntity,
2889
3325
  ConeEntity,
2890
3326
  CylinderEntity,