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