@vizij/render 0.0.7 → 0.1.1

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/index.js CHANGED
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  loadGLTFFromBlobWithBundle: () => loadGLTFFromBlobWithBundle,
46
46
  loadGLTFWithBundle: () => loadGLTFWithBundle,
47
47
  loadGltfFromBlob: () => loadGltfFromBlob,
48
+ parseGlbJsonChunk: () => parseGlbJsonChunk,
48
49
  useDefaultVizijStore: () => useDefaultVizijStore,
49
50
  useFeatures: () => useFeatures,
50
51
  useVizijStore: () => useVizijStore,
@@ -55,12 +56,12 @@ __export(index_exports, {
55
56
  module.exports = __toCommonJS(index_exports);
56
57
 
57
58
  // src/vizij.tsx
58
- var import_react10 = require("react");
59
+ var import_react11 = require("react");
59
60
  var import_react_error_boundary = require("react-error-boundary");
60
- var import_three = require("three");
61
- var import_fiber = require("@react-three/fiber");
61
+ var import_three2 = require("three");
62
+ var import_fiber2 = require("@react-three/fiber");
62
63
  var import_drei3 = require("@react-three/drei");
63
- var import_shallow7 = require("zustand/react/shallow");
64
+ var import_shallow8 = require("zustand/react/shallow");
64
65
 
65
66
  // src/renderables/renderable.tsx
66
67
  var import_react8 = require("react");
@@ -174,7 +175,7 @@ function InnerRenderedGroup({
174
175
  namespace,
175
176
  chain
176
177
  }) {
177
- const ref = (0, import_react4.useRef)();
178
+ const ref = (0, import_react4.useRef)(null);
178
179
  const group = useVizijStore((0, import_shallow2.useShallow)((state) => state.world[id]));
179
180
  const refIsNull = !group.refs[namespace]?.current;
180
181
  const animatables = useVizijStore((0, import_shallow2.useShallow)((state) => state.animatables));
@@ -295,9 +296,11 @@ function InnerRenderedEllipse({
295
296
  namespace,
296
297
  chain
297
298
  }) {
298
- const ellipseRef = (0, import_react5.useRef)();
299
- const materialRef = (0, import_react5.useRef)();
300
- const lineRef = (0, import_react5.useRef)();
299
+ const ellipseRef = (0, import_react5.useRef)(null);
300
+ const materialRef = (0, import_react5.useRef)(
301
+ null
302
+ );
303
+ const lineRef = (0, import_react5.useRef)(null);
301
304
  const strokeOffsetRef = (0, import_react5.useRef)(0);
302
305
  const strokeWidthRef = (0, import_react5.useRef)(0);
303
306
  const onElementClick = useVizijStore(
@@ -580,9 +583,11 @@ function InnerRenderedRectangle({
580
583
  namespace,
581
584
  chain
582
585
  }) {
583
- const rectangleRef = (0, import_react6.useRef)();
584
- const materialRef = (0, import_react6.useRef)();
585
- const lineRef = (0, import_react6.useRef)();
586
+ const rectangleRef = (0, import_react6.useRef)(null);
587
+ const materialRef = (0, import_react6.useRef)(
588
+ null
589
+ );
590
+ const lineRef = (0, import_react6.useRef)(null);
586
591
  const strokeOffsetRef = (0, import_react6.useRef)(0);
587
592
  const strokeWidthRef = (0, import_react6.useRef)(0);
588
593
  const onElementClick = useVizijStore(
@@ -867,8 +872,8 @@ function InnerRenderedShape({
867
872
  namespace,
868
873
  chain
869
874
  }) {
870
- const refGroup = (0, import_react7.useRef)();
871
- const ref = (0, import_react7.useRef)();
875
+ const refGroup = (0, import_react7.useRef)(null);
876
+ const ref = (0, import_react7.useRef)(null);
872
877
  const shape = useVizijStore((0, import_shallow5.useShallow)((state) => state.world[id]));
873
878
  const refs = useVizijStore(
874
879
  (0, import_shallow5.useShallow)((state) => state.world[id].refs)
@@ -909,7 +914,7 @@ function InnerRenderedShape({
909
914
  }),
910
915
  [shape, animatableValues, selectionData]
911
916
  );
912
- const material = (0, import_react7.useRef)();
917
+ const material = (0, import_react7.useRef)(null);
913
918
  const morphTargetSettings = (0, import_react7.useMemo)(() => {
914
919
  if (shape.morphTargets) {
915
920
  const dictionary = shape.morphTargets.reduce(
@@ -1430,6 +1435,35 @@ function createAnimatable(value) {
1430
1435
  }
1431
1436
  createAnimatable({ type: "euler", name: "Rotation" });
1432
1437
 
1438
+ // src/functions/exportable-bodies.ts
1439
+ function asGroupEntries(world) {
1440
+ return Object.values(world).filter((entry) => entry.type === "group");
1441
+ }
1442
+ function selectExportableGroupEntries(world, filterIds) {
1443
+ const groupEntries = asGroupEntries(world);
1444
+ const filterSet = Array.isArray(filterIds) && filterIds.length > 0 ? new Set(filterIds) : null;
1445
+ if (filterSet) {
1446
+ return groupEntries.filter((entry) => filterSet.has(entry.id));
1447
+ }
1448
+ const rootBoundsGroups = groupEntries.filter(
1449
+ (entry) => Boolean(entry.rootBounds)
1450
+ );
1451
+ if (rootBoundsGroups.length > 0) {
1452
+ return rootBoundsGroups;
1453
+ }
1454
+ const explicitRootGroups = groupEntries.filter(
1455
+ (entry) => entry.root === true
1456
+ );
1457
+ if (explicitRootGroups.length > 0) {
1458
+ return explicitRootGroups;
1459
+ }
1460
+ const topLevelGroups = groupEntries.filter((entry) => !entry.parent);
1461
+ if (topLevelGroups.length > 0) {
1462
+ return topLevelGroups;
1463
+ }
1464
+ return groupEntries;
1465
+ }
1466
+
1433
1467
  // src/store.ts
1434
1468
  THREE3.Object3D.DEFAULT_UP.set(0, 0, 1);
1435
1469
  (0, import_immer.enableMapSet)();
@@ -1505,23 +1539,17 @@ var VizijSlice = (set, get) => ({
1505
1539
  },
1506
1540
  getExportableBodies: (filterIds) => {
1507
1541
  const worldData = get().world;
1508
- if (!filterIds) {
1509
- const bodies = Object.values(worldData).filter((entry) => entry.type === "group" && entry.rootBounds).map((entry) => {
1510
- const firstNs = Object.keys(entry.refs)[0];
1511
- const refGroup = entry.refs[firstNs].current;
1512
- return refGroup;
1513
- });
1514
- return bodies;
1515
- } else {
1516
- const bodies = Object.values(worldData).filter(
1517
- (entry) => entry.type === "group" && entry.rootBounds && filterIds.includes(entry.id)
1518
- ).map((entry) => {
1519
- const firstNs = Object.keys(entry.refs)[0];
1520
- const refGroup = entry.refs[firstNs].current;
1521
- return refGroup;
1522
- });
1523
- return bodies;
1524
- }
1542
+ const candidateGroups = selectExportableGroupEntries(
1543
+ worldData,
1544
+ filterIds
1545
+ );
1546
+ return candidateGroups.flatMap((entry) => {
1547
+ const refs = Object.values(
1548
+ entry.refs ?? {}
1549
+ );
1550
+ const resolved = refs.find((ref) => ref?.current)?.current ?? null;
1551
+ return resolved ? [resolved] : [];
1552
+ });
1525
1553
  },
1526
1554
  setGeometry: (id, geometry) => {
1527
1555
  set(
@@ -1555,6 +1583,19 @@ var VizijSlice = (set, get) => ({
1555
1583
  })
1556
1584
  );
1557
1585
  },
1586
+ setValues: (writes = []) => {
1587
+ if (writes.length === 0) {
1588
+ return;
1589
+ }
1590
+ set(
1591
+ (0, import_immer.produce)((state) => {
1592
+ writes.forEach(({ id, namespace, value }) => {
1593
+ const lookupId = (0, import_utils6.getLookup)(namespace, id);
1594
+ state.values.set(lookupId, value);
1595
+ });
1596
+ })
1597
+ );
1598
+ },
1558
1599
  setWorldElementName: (id, value) => {
1559
1600
  set(
1560
1601
  (0, import_immer.produce)((state) => {
@@ -1749,59 +1790,163 @@ var createVizijStore = (initial) => (0, import_zustand2.create)()(
1749
1790
  }))
1750
1791
  );
1751
1792
 
1752
- // src/vizij.tsx
1793
+ // src/effects/selection-glow-effect.tsx
1794
+ var import_react10 = require("react");
1795
+ var import_fiber = require("@react-three/fiber");
1796
+ var import_shallow7 = require("zustand/react/shallow");
1797
+ var import_three = require("three");
1753
1798
  var import_jsx_runtime6 = require("react/jsx-runtime");
1754
- import_three.Object3D.DEFAULT_UP.set(0, 0, 1);
1799
+ function SelectionGlowEffect({
1800
+ enabled = false,
1801
+ color = "#ff1010ff",
1802
+ opacity = 0.9,
1803
+ thresholdAngle = 2
1804
+ }) {
1805
+ const selections = useVizijStore(
1806
+ (0, import_shallow7.useShallow)(
1807
+ (state) => enabled ? state.elementSelection ?? [] : []
1808
+ )
1809
+ );
1810
+ if (!enabled || selections.length === 0) {
1811
+ return null;
1812
+ }
1813
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react10.Fragment, { children: selections.map((selection) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1814
+ SelectionOutline,
1815
+ {
1816
+ selection,
1817
+ color: selection.color ?? color,
1818
+ opacity,
1819
+ thresholdAngle
1820
+ },
1821
+ `${selection.namespace}:${selection.id}`
1822
+ )) });
1823
+ }
1824
+ function SelectionOutline({
1825
+ selection,
1826
+ color,
1827
+ opacity,
1828
+ thresholdAngle
1829
+ }) {
1830
+ const target = useVizijStore(
1831
+ (0, import_shallow7.useShallow)((state) => {
1832
+ const entry = state.world[selection.id];
1833
+ const ref = entry?.refs?.[selection.namespace];
1834
+ const geometry = entry?.geometry ?? null;
1835
+ return { ref, geometry };
1836
+ })
1837
+ );
1838
+ const sourceRef = target.ref;
1839
+ const edgesGeometry = (0, import_react10.useMemo)(() => {
1840
+ if (!target.geometry) return null;
1841
+ const edges = new import_three.EdgesGeometry(target.geometry, thresholdAngle);
1842
+ return edges;
1843
+ }, [target.geometry, thresholdAngle]);
1844
+ (0, import_react10.useEffect)(() => () => edgesGeometry?.dispose(), [edgesGeometry]);
1845
+ const material = (0, import_react10.useMemo)(() => {
1846
+ const mat = new import_three.LineBasicMaterial({
1847
+ color: new import_three.Color(color),
1848
+ transparent: true,
1849
+ opacity,
1850
+ blending: import_three.AdditiveBlending,
1851
+ depthTest: false,
1852
+ depthWrite: false,
1853
+ toneMapped: false
1854
+ });
1855
+ return mat;
1856
+ }, [color, opacity]);
1857
+ (0, import_react10.useEffect)(() => () => material.dispose(), [material]);
1858
+ const lineRef = (0, import_react10.useRef)(null);
1859
+ (0, import_fiber.useFrame)(() => {
1860
+ const source = sourceRef?.current;
1861
+ const line = lineRef.current;
1862
+ if (!source || !line) return;
1863
+ copyWorldTransform(source, line);
1864
+ line.visible = source.visible;
1865
+ });
1866
+ if (!sourceRef || !edgesGeometry) {
1867
+ return null;
1868
+ }
1869
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1870
+ "lineSegments",
1871
+ {
1872
+ ref: lineRef,
1873
+ geometry: edgesGeometry,
1874
+ material,
1875
+ frustumCulled: false,
1876
+ renderOrder: 1e3
1877
+ }
1878
+ );
1879
+ }
1880
+ var tempMatrix = new import_three.Matrix4();
1881
+ var tempPosition = new import_three.Vector3();
1882
+ var tempQuaternion = new import_three.Quaternion();
1883
+ var tempScale = new import_three.Vector3();
1884
+ function copyWorldTransform(source, target) {
1885
+ source.updateWorldMatrix(true, false);
1886
+ tempMatrix.copy(source.matrixWorld);
1887
+ tempMatrix.decompose(tempPosition, tempQuaternion, tempScale);
1888
+ target.position.copy(tempPosition);
1889
+ target.quaternion.copy(tempQuaternion);
1890
+ target.scale.copy(tempScale);
1891
+ target.updateMatrix();
1892
+ }
1893
+
1894
+ // src/vizij.tsx
1895
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1896
+ import_three2.Object3D.DEFAULT_UP.set(0, 0, 1);
1755
1897
  function Vizij({
1756
1898
  style,
1757
1899
  className,
1758
1900
  rootId,
1759
1901
  namespace = "default",
1760
1902
  showSafeArea = false,
1903
+ showSelectionGlow = false,
1761
1904
  onPointerMissed
1762
1905
  }) {
1763
- const ctx = (0, import_react10.useContext)(VizijContext);
1906
+ const ctx = (0, import_react11.useContext)(VizijContext);
1764
1907
  if (ctx) {
1765
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1766
- import_fiber.Canvas,
1908
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1909
+ import_fiber2.Canvas,
1767
1910
  {
1768
1911
  shadows: false,
1769
1912
  style,
1770
1913
  className,
1771
1914
  onPointerMissed,
1772
1915
  gl: {
1773
- outputColorSpace: import_three.SRGBColorSpace,
1774
- toneMapping: import_three.NoToneMapping,
1916
+ outputColorSpace: import_three2.SRGBColorSpace,
1917
+ toneMapping: import_three2.NoToneMapping,
1775
1918
  antialias: true
1776
1919
  },
1777
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1920
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1778
1921
  MemoizedInnerVizij,
1779
1922
  {
1780
1923
  rootId,
1781
1924
  namespace,
1782
- showSafeArea
1925
+ showSafeArea,
1926
+ showSelectionGlow
1783
1927
  }
1784
1928
  )
1785
1929
  }
1786
1930
  );
1787
1931
  } else {
1788
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VizijContext.Provider, { value: useDefaultVizijStore, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1789
- import_fiber.Canvas,
1932
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VizijContext.Provider, { value: useDefaultVizijStore, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1933
+ import_fiber2.Canvas,
1790
1934
  {
1791
1935
  style,
1792
1936
  className,
1793
1937
  onPointerMissed,
1794
1938
  gl: {
1795
- outputColorSpace: import_three.SRGBColorSpace,
1796
- toneMapping: import_three.NoToneMapping,
1939
+ outputColorSpace: import_three2.SRGBColorSpace,
1940
+ toneMapping: import_three2.NoToneMapping,
1797
1941
  antialias: true
1798
1942
  },
1799
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1943
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1800
1944
  MemoizedInnerVizij,
1801
1945
  {
1802
1946
  rootId,
1803
1947
  namespace,
1804
- showSafeArea
1948
+ showSafeArea,
1949
+ showSelectionGlow
1805
1950
  }
1806
1951
  )
1807
1952
  }
@@ -1812,15 +1957,16 @@ function InnerVizij({
1812
1957
  rootId,
1813
1958
  namespace = "default",
1814
1959
  container,
1815
- showSafeArea
1960
+ showSafeArea,
1961
+ showSelectionGlow
1816
1962
  }) {
1817
1963
  const sceneParentSizing = container ? {
1818
1964
  width: container.width * container.resolution,
1819
1965
  height: container.height * container.resolution
1820
1966
  } : void 0;
1821
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1822
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ambientLight", { intensity: Math.PI / 2 }),
1823
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1967
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ambientLight", { intensity: Math.PI / 2 }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1824
1970
  import_drei3.OrthographicCamera,
1825
1971
  {
1826
1972
  makeDefault: true,
@@ -1829,7 +1975,7 @@ function InnerVizij({
1829
1975
  far: 101
1830
1976
  }
1831
1977
  ),
1832
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react10.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1978
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react11.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1833
1979
  World,
1834
1980
  {
1835
1981
  rootId,
@@ -1837,27 +1983,28 @@ function InnerVizij({
1837
1983
  parentSizing: sceneParentSizing
1838
1984
  }
1839
1985
  ) }),
1840
- showSafeArea && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SafeAreaRenderer, { rootId })
1986
+ showSelectionGlow && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectionGlowEffect, { enabled: true }),
1987
+ showSafeArea && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SafeAreaRenderer, { rootId })
1841
1988
  ] });
1842
1989
  }
1843
- var MemoizedInnerVizij = (0, import_react10.memo)(InnerVizij);
1990
+ var MemoizedInnerVizij = (0, import_react11.memo)(InnerVizij);
1844
1991
  function InnerWorld({
1845
1992
  rootId,
1846
1993
  namespace = "default",
1847
1994
  parentSizing
1848
1995
  }) {
1849
1996
  const [present, rootBounds] = useVizijStore(
1850
- (0, import_shallow7.useShallow)((state) => {
1997
+ (0, import_shallow8.useShallow)((state) => {
1851
1998
  const group = state.world[rootId];
1852
1999
  const bounds = group?.rootBounds ?? defaultRootBounds;
1853
2000
  return [group !== void 0, bounds];
1854
2001
  })
1855
2002
  );
1856
- const { camera, size } = (0, import_fiber.useThree)((state) => ({
2003
+ const { camera, size } = (0, import_fiber2.useThree)((state) => ({
1857
2004
  camera: state.camera,
1858
2005
  size: state.size
1859
2006
  }));
1860
- (0, import_react10.useEffect)(() => {
2007
+ (0, import_react11.useEffect)(() => {
1861
2008
  const width = rootBounds.size.x;
1862
2009
  const height = rootBounds.size.y;
1863
2010
  if (camera && parentSizing === void 0 && camera.isOrthographicCamera) {
@@ -1885,9 +2032,9 @@ function InnerWorld({
1885
2032
  camera.updateProjectionMatrix();
1886
2033
  }
1887
2034
  }, [rootBounds, camera, parentSizing, size]);
1888
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_error_boundary.ErrorBoundary, { fallback: null, children: [
1889
- present && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Renderable, { id: rootId, namespace, chain: [] }),
1890
- !present && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2035
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_error_boundary.ErrorBoundary, { fallback: null, children: [
2036
+ present && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Renderable, { id: rootId, namespace, chain: [] }),
2037
+ !present && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1891
2038
  import_drei3.Text,
1892
2039
  {
1893
2040
  position: [0, 0, 0],
@@ -1900,7 +2047,7 @@ function InnerWorld({
1900
2047
  )
1901
2048
  ] });
1902
2049
  }
1903
- var World = (0, import_react10.memo)(InnerWorld);
2050
+ var World = (0, import_react11.memo)(InnerWorld);
1904
2051
  function SafeAreaRenderer({ rootId }) {
1905
2052
  const rootBounds = useVizijStore((state) => {
1906
2053
  const group = state.world[rootId];
@@ -1910,7 +2057,7 @@ function SafeAreaRenderer({ rootId }) {
1910
2057
  const right = rootBounds.center.x + rootBounds.size.x / 2;
1911
2058
  const top = rootBounds.center.y + rootBounds.size.y / 2;
1912
2059
  const bottom = rootBounds.center.y - rootBounds.size.y / 2;
1913
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2060
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1914
2061
  import_drei3.Line,
1915
2062
  {
1916
2063
  points: [
@@ -1941,11 +2088,11 @@ var ShapeMaterial = /* @__PURE__ */ ((ShapeMaterial2) => {
1941
2088
  })(ShapeMaterial || {});
1942
2089
 
1943
2090
  // src/hooks/use-vizij-store-subscription.ts
1944
- var import_react11 = require("react");
2091
+ var import_react12 = require("react");
1945
2092
  function useVizijStoreSubscription(selector, listener) {
1946
- const store = (0, import_react11.useContext)(VizijContext);
2093
+ const store = (0, import_react12.useContext)(VizijContext);
1947
2094
  if (!store) throw new Error("Missing VizijProvider in the tree");
1948
- (0, import_react11.useEffect)(() => {
2095
+ (0, import_react12.useEffect)(() => {
1949
2096
  const initialValue = selector(store.getState());
1950
2097
  listener(initialValue);
1951
2098
  return store.subscribe(selector, listener);
@@ -1953,17 +2100,17 @@ function useVizijStoreSubscription(selector, listener) {
1953
2100
  }
1954
2101
 
1955
2102
  // src/hooks/use-vizij-store-setter.ts
1956
- var import_react12 = require("react");
2103
+ var import_react13 = require("react");
1957
2104
  function useVizijStoreSetter() {
1958
- const store = (0, import_react12.useContext)(VizijContext);
2105
+ const store = (0, import_react13.useContext)(VizijContext);
1959
2106
  if (!store) throw new Error("Missing VizijProvider in the tree");
1960
2107
  return store.setState;
1961
2108
  }
1962
2109
 
1963
2110
  // src/hooks/use-vizij-store-getter.ts
1964
- var import_react13 = require("react");
2111
+ var import_react14 = require("react");
1965
2112
  function useVizijStoreGetter() {
1966
- const store = (0, import_react13.useContext)(VizijContext);
2113
+ const store = (0, import_react14.useContext)(VizijContext);
1967
2114
  if (!store) throw new Error("Missing VizijProvider in the tree");
1968
2115
  return store.getState;
1969
2116
  }
@@ -1973,7 +2120,7 @@ var THREE5 = __toESM(require("three"));
1973
2120
  var import_three_stdlib = require("three-stdlib");
1974
2121
 
1975
2122
  // src/functions/gltf-loading/traverse-three.ts
1976
- var import_react15 = require("react");
2123
+ var import_react16 = require("react");
1977
2124
  var THREE4 = __toESM(require("three"));
1978
2125
 
1979
2126
  // src/functions/gltf-loading/map-features.ts
@@ -1999,19 +2146,19 @@ function mapFeatures(features) {
1999
2146
  }
2000
2147
 
2001
2148
  // src/functions/gltf-loading/import-scene.ts
2002
- var import_three4 = require("three");
2149
+ var import_three5 = require("three");
2003
2150
 
2004
2151
  // src/functions/gltf-loading/import-group.ts
2005
- var import_three3 = require("three");
2152
+ var import_three4 = require("three");
2006
2153
 
2007
2154
  // src/functions/util.ts
2008
- var import_react14 = require("react");
2155
+ var import_react15 = require("react");
2009
2156
  function namespaceArrayToRefs(namespaces) {
2010
- return namespaces.reduce((acc, ns) => ({ ...acc, [ns]: (0, import_react14.createRef)() }), {});
2157
+ return namespaces.reduce((acc, ns) => ({ ...acc, [ns]: (0, import_react15.createRef)() }), {});
2011
2158
  }
2012
2159
 
2013
2160
  // src/functions/gltf-loading/import-mesh.ts
2014
- var import_three2 = require("three");
2161
+ var import_three3 = require("three");
2015
2162
 
2016
2163
  // src/functions/gltf-loading/import-geometry.ts
2017
2164
  function sanitizeMorphKey(name, fallbackIndex, used) {
@@ -2051,7 +2198,7 @@ function importGeometry(geometry, mesh) {
2051
2198
  type: "number",
2052
2199
  default: mesh.morphTargetInfluences?.[index] ?? 0,
2053
2200
  constraints: {
2054
- min: 0,
2201
+ min: -1,
2055
2202
  max: 1
2056
2203
  },
2057
2204
  pub: {
@@ -2067,7 +2214,7 @@ function importGeometry(geometry, mesh) {
2067
2214
  }
2068
2215
 
2069
2216
  // src/functions/gltf-loading/import-mesh.ts
2070
- import_three2.Object3D.DEFAULT_UP.set(0, 0, 1);
2217
+ import_three3.Object3D.DEFAULT_UP.set(0, 0, 1);
2071
2218
  function importMesh(mesh, namespaces, colorLookup) {
2072
2219
  let world = {};
2073
2220
  let animatables = {};
@@ -2181,6 +2328,15 @@ function importMesh(mesh, namespaces, colorLookup) {
2181
2328
  world = { ...world, ...newWorldItems };
2182
2329
  animatables = { ...animatables, ...newAnimatables };
2183
2330
  children.push(childId);
2331
+ } else if (shouldImportAsGroupChild(child)) {
2332
+ const [newWorldItems, newAnimatables, childId, newMeshColors] = importGroup(child, namespaces, {
2333
+ ...colorLookup,
2334
+ ...newColorLookup
2335
+ });
2336
+ newColorLookup = { ...newColorLookup, ...newMeshColors };
2337
+ world = { ...world, ...newWorldItems };
2338
+ animatables = { ...animatables, ...newAnimatables };
2339
+ children.push(childId);
2184
2340
  }
2185
2341
  });
2186
2342
  const newShape = {
@@ -2224,9 +2380,27 @@ function getShapeMaterial(mesh, useEmissive) {
2224
2380
  return "standard" /* Standard */;
2225
2381
  }
2226
2382
  }
2383
+ function shouldImportAsGroupChild(child) {
2384
+ if (!child.isObject3D) {
2385
+ return false;
2386
+ }
2387
+ if (child.isMesh) {
2388
+ return false;
2389
+ }
2390
+ if (child.isCamera) {
2391
+ return false;
2392
+ }
2393
+ if (child.isLight) {
2394
+ return false;
2395
+ }
2396
+ if (child.isBone) {
2397
+ return false;
2398
+ }
2399
+ return true;
2400
+ }
2227
2401
 
2228
2402
  // src/functions/gltf-loading/import-group.ts
2229
- import_three3.Object3D.DEFAULT_UP.set(0, 0, 1);
2403
+ import_three4.Object3D.DEFAULT_UP.set(0, 0, 1);
2230
2404
  function importGroup(group, namespaces, colorLookup, rootBounds) {
2231
2405
  let world = {};
2232
2406
  let animatables = {};
@@ -2283,7 +2457,7 @@ function importGroup(group, namespaces, colorLookup, rootBounds) {
2283
2457
  world = { ...world, ...newWorldItems };
2284
2458
  animatables = { ...animatables, ...newAnimatables };
2285
2459
  children.push(childId);
2286
- } else if (child.isGroup || child.isObject3D && child.children.length !== 0) {
2460
+ } else if (shouldImportAsGroupChild2(child)) {
2287
2461
  const [newWorldItems, newAnimatables, childId, newMeshColors] = importGroup(child, namespaces, {
2288
2462
  ...colorLookup,
2289
2463
  ...newColorLookup
@@ -2312,9 +2486,27 @@ function importGroup(group, namespaces, colorLookup, rootBounds) {
2312
2486
  world = { ...world, [newGroup.id]: newGroup };
2313
2487
  return [world, animatables, newGroup.id, newColorLookup];
2314
2488
  }
2489
+ function shouldImportAsGroupChild2(child) {
2490
+ if (!child.isObject3D) {
2491
+ return false;
2492
+ }
2493
+ if (child.isMesh) {
2494
+ return false;
2495
+ }
2496
+ if (child.isCamera) {
2497
+ return false;
2498
+ }
2499
+ if (child.isLight) {
2500
+ return false;
2501
+ }
2502
+ if (child.isBone) {
2503
+ return false;
2504
+ }
2505
+ return true;
2506
+ }
2315
2507
 
2316
2508
  // src/functions/gltf-loading/import-scene.ts
2317
- import_three4.Object3D.DEFAULT_UP.set(0, 0, 1);
2509
+ import_three5.Object3D.DEFAULT_UP.set(0, 0, 1);
2318
2510
  function importScene(scene, namespaces, rootBounds) {
2319
2511
  let world = {};
2320
2512
  let animatables = {};
@@ -2356,7 +2548,7 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2356
2548
  loadedData = {
2357
2549
  ...data,
2358
2550
  refs: namespaces.reduce(
2359
- (acc, ns) => ({ ...acc, [ns]: (0, import_react15.createRef)() }),
2551
+ (acc, ns) => ({ ...acc, [ns]: (0, import_react16.createRef)() }),
2360
2552
  {}
2361
2553
  )
2362
2554
  };
@@ -2371,7 +2563,7 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2371
2563
  ...data,
2372
2564
  geometry: child.geometry,
2373
2565
  refs: namespaces.reduce(
2374
- (acc, ns) => ({ ...acc, [ns]: (0, import_react15.createRef)() }),
2566
+ (acc, ns) => ({ ...acc, [ns]: (0, import_react16.createRef)() }),
2375
2567
  {}
2376
2568
  )
2377
2569
  };
@@ -2385,7 +2577,7 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2385
2577
  loadedData = {
2386
2578
  ...data,
2387
2579
  refs: namespaces.reduce(
2388
- (acc, ns) => ({ ...acc, [ns]: (0, import_react15.createRef)() }),
2580
+ (acc, ns) => ({ ...acc, [ns]: (0, import_react16.createRef)() }),
2389
2581
  {}
2390
2582
  )
2391
2583
  };
@@ -2399,7 +2591,7 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2399
2591
  loadedData = {
2400
2592
  ...data,
2401
2593
  refs: namespaces.reduce(
2402
- (acc, ns) => ({ ...acc, [ns]: (0, import_react15.createRef)() }),
2594
+ (acc, ns) => ({ ...acc, [ns]: (0, import_react16.createRef)() }),
2403
2595
  {}
2404
2596
  )
2405
2597
  };
@@ -2585,9 +2777,10 @@ function deriveRootBounds(group) {
2585
2777
  }
2586
2778
 
2587
2779
  // src/functions/vizij-bundle.ts
2780
+ var import_utils7 = require("@vizij/utils");
2588
2781
  var BUNDLE_KEYS = ["VIZIJ_bundle"];
2589
2782
  function cloneBundle(value) {
2590
- return JSON.parse(JSON.stringify(value));
2783
+ return (0, import_utils7.cloneDeepSafe)(value);
2591
2784
  }
2592
2785
  function readExtensionValue(extensionContainer) {
2593
2786
  for (const key of BUNDLE_KEYS) {
@@ -2621,6 +2814,13 @@ function searchParserJsonForBundle(parserJson) {
2621
2814
  if (!parserJson || typeof parserJson !== "object") {
2622
2815
  return null;
2623
2816
  }
2817
+ const rootExtensions = parserJson && typeof parserJson === "object" ? parserJson.extensions : null;
2818
+ if (rootExtensions && typeof rootExtensions === "object") {
2819
+ const match = readExtensionValue(rootExtensions);
2820
+ if (match) {
2821
+ return cloneBundle(match.value);
2822
+ }
2823
+ }
2624
2824
  const nodes = Array.isArray(parserJson.nodes) ? parserJson.nodes : [];
2625
2825
  for (const node of nodes) {
2626
2826
  const extensions = node && typeof node === "object" ? node.extensions : null;
@@ -2685,6 +2885,7 @@ function applyVizijBundle(object, bundle) {
2685
2885
  }
2686
2886
 
2687
2887
  // src/functions/gltf-loading/extract-animations.ts
2888
+ var import_utils8 = require("@vizij/utils");
2688
2889
  var CHANNEL_PATH_TO_TRACK_PROPERTY = {
2689
2890
  translation: "position",
2690
2891
  rotation: "quaternion",
@@ -2700,7 +2901,7 @@ function clonePlainObject(value) {
2700
2901
  if (!value) {
2701
2902
  return void 0;
2702
2903
  }
2703
- return JSON.parse(JSON.stringify(value));
2904
+ return (0, import_utils8.cloneDeepSafe)(value);
2704
2905
  }
2705
2906
  function inferValueSize(valueType) {
2706
2907
  switch (valueType) {
@@ -3055,23 +3256,94 @@ function extractVizijAnimations(parserJson, clips) {
3055
3256
 
3056
3257
  // src/functions/load-gltf.ts
3057
3258
  THREE5.Object3D.DEFAULT_UP.set(0, 0, 1);
3259
+ var GLB_MAGIC = 1179937895;
3260
+ var GLB_VERSION = 2;
3261
+ var GLB_HEADER_BYTES = 12;
3262
+ var GLB_CHUNK_HEADER_BYTES = 8;
3263
+ var GLB_JSON_CHUNK_TYPE = 1313821514;
3058
3264
  var EmptyModelError = class extends Error {
3059
3265
  constructor(message) {
3060
3266
  super(message);
3061
3267
  this.name = "EmptyModelError";
3062
3268
  }
3063
3269
  };
3270
+ function parseGlbJsonChunk(buffer) {
3271
+ if (buffer.byteLength < GLB_HEADER_BYTES + GLB_CHUNK_HEADER_BYTES) {
3272
+ return void 0;
3273
+ }
3274
+ const view = new DataView(buffer);
3275
+ const magic = view.getUint32(0, true);
3276
+ const version = view.getUint32(4, true);
3277
+ if (magic !== GLB_MAGIC || version !== GLB_VERSION) {
3278
+ return void 0;
3279
+ }
3280
+ const chunkLength = view.getUint32(GLB_HEADER_BYTES, true);
3281
+ const chunkType = view.getUint32(GLB_HEADER_BYTES + 4, true);
3282
+ if (chunkType !== GLB_JSON_CHUNK_TYPE) {
3283
+ return void 0;
3284
+ }
3285
+ const chunkStart = GLB_HEADER_BYTES + GLB_CHUNK_HEADER_BYTES;
3286
+ const chunkEnd = chunkStart + chunkLength;
3287
+ if (chunkEnd > buffer.byteLength) {
3288
+ return void 0;
3289
+ }
3290
+ try {
3291
+ const chunkBytes = new Uint8Array(buffer, chunkStart, chunkLength);
3292
+ const jsonText = new TextDecoder().decode(chunkBytes);
3293
+ return JSON.parse(jsonText);
3294
+ } catch {
3295
+ return void 0;
3296
+ }
3297
+ }
3298
+ async function resolveParserJson(parserJson, fallback) {
3299
+ if (parserJson && typeof parserJson === "object") {
3300
+ return parserJson;
3301
+ }
3302
+ if (fallback.arrayBuffer) {
3303
+ const fromArrayBuffer = parseGlbJsonChunk(fallback.arrayBuffer);
3304
+ if (fromArrayBuffer && typeof fromArrayBuffer === "object") {
3305
+ return fromArrayBuffer;
3306
+ }
3307
+ }
3308
+ if (fallback.blob) {
3309
+ try {
3310
+ const blobBuffer = typeof fallback.blob.arrayBuffer === "function" ? await fallback.blob.arrayBuffer() : await new Response(fallback.blob).arrayBuffer();
3311
+ const fromBlob = parseGlbJsonChunk(blobBuffer);
3312
+ if (fromBlob && typeof fromBlob === "object") {
3313
+ return fromBlob;
3314
+ }
3315
+ } catch {
3316
+ }
3317
+ }
3318
+ if (fallback.url && typeof fetch === "function") {
3319
+ try {
3320
+ const response = await fetch(fallback.url);
3321
+ if (response.ok) {
3322
+ const binary = await response.arrayBuffer();
3323
+ const fromUrl = parseGlbJsonChunk(binary);
3324
+ if (fromUrl && typeof fromUrl === "object") {
3325
+ return fromUrl;
3326
+ }
3327
+ }
3328
+ } catch {
3329
+ }
3330
+ }
3331
+ return parserJson;
3332
+ }
3064
3333
  async function loadGLTF(url, namespaces, aggressiveImport = false, rootBounds) {
3065
3334
  const modelLoader = new import_three_stdlib.GLTFLoader();
3066
3335
  modelLoader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
3067
3336
  const modelData = await modelLoader.loadAsync(url);
3337
+ const parserJson = await resolveParserJson(modelData?.parser?.json, {
3338
+ url
3339
+ });
3068
3340
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
3069
3341
  const asset = parseScene(
3070
3342
  modelData.scene,
3071
3343
  actualizedNamespaces,
3072
3344
  aggressiveImport,
3073
3345
  rootBounds,
3074
- modelData?.parser?.json,
3346
+ parserJson,
3075
3347
  modelData.animations
3076
3348
  );
3077
3349
  return [asset.world, asset.animatables, asset.animations];
@@ -3085,7 +3357,8 @@ async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, root
3085
3357
  objectUrl,
3086
3358
  actualizedNamespaces,
3087
3359
  aggressiveImport,
3088
- rootBounds
3360
+ rootBounds,
3361
+ { blob }
3089
3362
  );
3090
3363
  return [asset.world, asset.animatables, asset.animations];
3091
3364
  } finally {
@@ -3099,14 +3372,18 @@ async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, root
3099
3372
  loader.parse(
3100
3373
  arrayBuffer,
3101
3374
  "",
3102
- (gltf) => {
3375
+ async (gltf) => {
3103
3376
  try {
3377
+ const parserJson = await resolveParserJson(
3378
+ gltf?.parser?.json,
3379
+ { arrayBuffer }
3380
+ );
3104
3381
  const asset = parseScene(
3105
3382
  gltf.scene,
3106
3383
  actualizedNamespaces,
3107
3384
  aggressiveImport,
3108
3385
  rootBounds,
3109
- gltf?.parser?.json,
3386
+ parserJson,
3110
3387
  gltf.animations
3111
3388
  );
3112
3389
  resolve([asset.world, asset.animatables, asset.animations]);
@@ -3133,19 +3410,23 @@ function parseScene(scene, namespaces, aggressiveImport, rootBounds, parserJson,
3133
3410
  );
3134
3411
  const bundle = extractVizijBundle(scene, parserJson);
3135
3412
  const animations = extractVizijAnimations(parserJson, clips);
3136
- return { world, animatables, bundle, animations };
3413
+ return { world, animatables, bundle, animations, scene };
3137
3414
  }
3138
- async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, rootBounds) {
3415
+ async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, rootBounds, parserJsonFallback) {
3139
3416
  const modelLoader = new import_three_stdlib.GLTFLoader();
3140
3417
  modelLoader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
3141
3418
  const modelData = await modelLoader.loadAsync(url);
3419
+ const parserJson = await resolveParserJson(modelData?.parser?.json, {
3420
+ url,
3421
+ ...parserJsonFallback
3422
+ });
3142
3423
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
3143
3424
  return parseScene(
3144
3425
  modelData.scene,
3145
3426
  actualizedNamespaces,
3146
3427
  aggressiveImport,
3147
3428
  rootBounds,
3148
- modelData?.parser?.json,
3429
+ parserJson,
3149
3430
  modelData.animations
3150
3431
  );
3151
3432
  }
@@ -3158,7 +3439,8 @@ async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = f
3158
3439
  objectUrl,
3159
3440
  actualizedNamespaces,
3160
3441
  aggressiveImport,
3161
- rootBounds
3442
+ rootBounds,
3443
+ { blob }
3162
3444
  );
3163
3445
  } finally {
3164
3446
  URL.revokeObjectURL(objectUrl);
@@ -3171,14 +3453,18 @@ async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = f
3171
3453
  loader.parse(
3172
3454
  arrayBuffer,
3173
3455
  "",
3174
- (gltf) => {
3456
+ async (gltf) => {
3175
3457
  try {
3458
+ const parserJson = await resolveParserJson(
3459
+ gltf?.parser?.json,
3460
+ { arrayBuffer }
3461
+ );
3176
3462
  const asset = parseScene(
3177
3463
  gltf.scene,
3178
3464
  actualizedNamespaces,
3179
3465
  aggressiveImport,
3180
3466
  rootBounds,
3181
- gltf?.parser?.json,
3467
+ parserJson,
3182
3468
  gltf.animations
3183
3469
  );
3184
3470
  resolve(asset);
@@ -3230,6 +3516,160 @@ var loadGltfFromBlob = (blob, namespaces) => {
3230
3516
  var import_three_stdlib3 = require("three-stdlib");
3231
3517
  var THREE6 = __toESM(require("three"));
3232
3518
  THREE6.Object3D.DEFAULT_UP.set(0, 0, 1);
3519
+ function normalizeExportError(error) {
3520
+ if (error instanceof Error) {
3521
+ return error;
3522
+ }
3523
+ if (typeof error === "string") {
3524
+ return new Error(error);
3525
+ }
3526
+ return new Error("Failed to export scene.");
3527
+ }
3528
+ function triggerBlobDownload(blob, filename) {
3529
+ const url = URL.createObjectURL(blob);
3530
+ const link = document.createElement("a");
3531
+ link.href = url;
3532
+ link.download = filename;
3533
+ link.style.display = "none";
3534
+ document.body.appendChild(link);
3535
+ link.click();
3536
+ document.body.removeChild(link);
3537
+ setTimeout(() => URL.revokeObjectURL(url), 0);
3538
+ }
3539
+ var GLB_MAGIC2 = 1179937895;
3540
+ var GLB_VERSION2 = 2;
3541
+ var GLB_JSON_CHUNK_TYPE2 = 1313821514;
3542
+ var GLB_HEADER_BYTES2 = 12;
3543
+ var GLB_CHUNK_HEADER_BYTES2 = 8;
3544
+ var GLB_JSON_PADDING_BYTE = 32;
3545
+ function isNearlyEqual(a, b, epsilon = 1e-6) {
3546
+ return Math.abs(a - b) <= epsilon;
3547
+ }
3548
+ function isIdentityTransformNode(node) {
3549
+ const translation = node.translation;
3550
+ if (Array.isArray(translation) && (translation.length !== 3 || !isNearlyEqual(Number(translation[0]), 0) || !isNearlyEqual(Number(translation[1]), 0) || !isNearlyEqual(Number(translation[2]), 0))) {
3551
+ return false;
3552
+ }
3553
+ const rotation = node.rotation;
3554
+ if (Array.isArray(rotation) && (rotation.length !== 4 || !isNearlyEqual(Number(rotation[0]), 0) || !isNearlyEqual(Number(rotation[1]), 0) || !isNearlyEqual(Number(rotation[2]), 0) || !isNearlyEqual(Number(rotation[3]), 1))) {
3555
+ return false;
3556
+ }
3557
+ const scale = node.scale;
3558
+ if (Array.isArray(scale) && (scale.length !== 3 || !isNearlyEqual(Number(scale[0]), 1) || !isNearlyEqual(Number(scale[1]), 1) || !isNearlyEqual(Number(scale[2]), 1))) {
3559
+ return false;
3560
+ }
3561
+ return true;
3562
+ }
3563
+ function isPassThroughWrapperNode(node) {
3564
+ if (!node || typeof node !== "object") {
3565
+ return false;
3566
+ }
3567
+ if (!Array.isArray(node.children) || node.children.length === 0) {
3568
+ return false;
3569
+ }
3570
+ const hasOnlyNumericChildren = node.children.every(
3571
+ (index) => Number.isInteger(index)
3572
+ );
3573
+ if (!hasOnlyNumericChildren) {
3574
+ return false;
3575
+ }
3576
+ if (node.mesh !== void 0 || node.camera !== void 0 || node.skin !== void 0) {
3577
+ return false;
3578
+ }
3579
+ if (node.extensions && typeof node.extensions === "object" && Object.keys(node.extensions).length > 0) {
3580
+ return false;
3581
+ }
3582
+ return isIdentityTransformNode(node);
3583
+ }
3584
+ function normalizeExportedSceneJson(json, fallbackSceneName) {
3585
+ if (!json || typeof json !== "object") {
3586
+ return false;
3587
+ }
3588
+ if (!Array.isArray(json.scenes) || !Array.isArray(json.nodes)) {
3589
+ return false;
3590
+ }
3591
+ const sceneIndexRaw = json.scene;
3592
+ const sceneIndex = typeof sceneIndexRaw === "number" && Number.isInteger(sceneIndexRaw) ? sceneIndexRaw : 0;
3593
+ const sceneDef = json.scenes[sceneIndex];
3594
+ if (!sceneDef || typeof sceneDef !== "object") {
3595
+ return false;
3596
+ }
3597
+ let changed = false;
3598
+ let wrapperNodeName;
3599
+ if (Array.isArray(sceneDef.nodes) && sceneDef.nodes.length === 1) {
3600
+ const wrapperIndex = sceneDef.nodes[0];
3601
+ if (Number.isInteger(wrapperIndex)) {
3602
+ const wrapperNode = json.nodes[wrapperIndex];
3603
+ if (isPassThroughWrapperNode(wrapperNode)) {
3604
+ sceneDef.nodes = [...wrapperNode.children];
3605
+ wrapperNodeName = typeof wrapperNode.name === "string" ? wrapperNode.name : void 0;
3606
+ changed = true;
3607
+ }
3608
+ }
3609
+ }
3610
+ const currentSceneName = typeof sceneDef.name === "string" ? sceneDef.name.trim() : "";
3611
+ if (currentSceneName === "AuxScene") {
3612
+ const nextSceneName = (wrapperNodeName?.trim() || fallbackSceneName?.trim() || "Scene").trim();
3613
+ if (nextSceneName.length > 0 && nextSceneName !== currentSceneName) {
3614
+ sceneDef.name = nextSceneName;
3615
+ changed = true;
3616
+ }
3617
+ }
3618
+ return changed;
3619
+ }
3620
+ function sanitizeExportedGlb(buffer, fallbackSceneName) {
3621
+ if (buffer.byteLength < GLB_HEADER_BYTES2 + GLB_CHUNK_HEADER_BYTES2) {
3622
+ return buffer;
3623
+ }
3624
+ const originalBytes = new Uint8Array(buffer);
3625
+ const view = new DataView(buffer);
3626
+ const magic = view.getUint32(0, true);
3627
+ const version = view.getUint32(4, true);
3628
+ if (magic !== GLB_MAGIC2 || version !== GLB_VERSION2) {
3629
+ return buffer;
3630
+ }
3631
+ const jsonChunkLength = view.getUint32(GLB_HEADER_BYTES2, true);
3632
+ const jsonChunkType = view.getUint32(GLB_HEADER_BYTES2 + 4, true);
3633
+ if (jsonChunkType !== GLB_JSON_CHUNK_TYPE2) {
3634
+ return buffer;
3635
+ }
3636
+ const jsonChunkStart = GLB_HEADER_BYTES2 + GLB_CHUNK_HEADER_BYTES2;
3637
+ const jsonChunkEnd = jsonChunkStart + jsonChunkLength;
3638
+ if (jsonChunkEnd > originalBytes.length) {
3639
+ return buffer;
3640
+ }
3641
+ let jsonPayload;
3642
+ try {
3643
+ const jsonText = new TextDecoder().decode(
3644
+ originalBytes.slice(jsonChunkStart, jsonChunkEnd)
3645
+ );
3646
+ jsonPayload = JSON.parse(jsonText);
3647
+ } catch {
3648
+ return buffer;
3649
+ }
3650
+ const changed = normalizeExportedSceneJson(jsonPayload, fallbackSceneName);
3651
+ if (!changed) {
3652
+ return buffer;
3653
+ }
3654
+ const encodedJson = new TextEncoder().encode(JSON.stringify(jsonPayload));
3655
+ const paddedJsonLength = encodedJson.length + 3 & ~3;
3656
+ const paddedJson = new Uint8Array(paddedJsonLength);
3657
+ paddedJson.fill(GLB_JSON_PADDING_BYTE);
3658
+ paddedJson.set(encodedJson);
3659
+ const remainingChunks = originalBytes.slice(jsonChunkEnd);
3660
+ const totalLength = GLB_HEADER_BYTES2 + GLB_CHUNK_HEADER_BYTES2 + paddedJsonLength + remainingChunks.length;
3661
+ const sanitized = new ArrayBuffer(totalLength);
3662
+ const sanitizedBytes = new Uint8Array(sanitized);
3663
+ const sanitizedView = new DataView(sanitized);
3664
+ sanitizedView.setUint32(0, GLB_MAGIC2, true);
3665
+ sanitizedView.setUint32(4, GLB_VERSION2, true);
3666
+ sanitizedView.setUint32(8, totalLength, true);
3667
+ sanitizedView.setUint32(GLB_HEADER_BYTES2, paddedJsonLength, true);
3668
+ sanitizedView.setUint32(GLB_HEADER_BYTES2 + 4, GLB_JSON_CHUNK_TYPE2, true);
3669
+ sanitizedBytes.set(paddedJson, jsonChunkStart);
3670
+ sanitizedBytes.set(remainingChunks, jsonChunkStart + paddedJsonLength);
3671
+ return sanitized;
3672
+ }
3233
3673
  function exportScene(data, fileNameOrOptions = "scene.glb") {
3234
3674
  const options = typeof fileNameOrOptions === "string" ? { fileName: fileNameOrOptions } : fileNameOrOptions ?? {};
3235
3675
  const fileName = options.fileName ?? "scene.glb";
@@ -3244,7 +3684,15 @@ function exportScene(data, fileNameOrOptions = "scene.glb") {
3244
3684
  }
3245
3685
  }
3246
3686
  }));
3247
- const detachBundle = shouldAttachBundle && options.bundle ? applyVizijBundle(data, options.bundle) : () => {
3687
+ const sourceRoot = data;
3688
+ const exportRoot = sourceRoot instanceof THREE6.Scene ? sourceRoot : sourceRoot.clone(true);
3689
+ const exportTarget = exportRoot instanceof THREE6.Scene ? exportRoot : (() => {
3690
+ const scene = new THREE6.Scene();
3691
+ scene.name = data.name?.trim() || "Scene";
3692
+ scene.add(exportRoot);
3693
+ return scene;
3694
+ })();
3695
+ const detachBundle = shouldAttachBundle && options.bundle ? applyVizijBundle(exportRoot, options.bundle) : () => {
3248
3696
  };
3249
3697
  const binary = options.binary ?? true;
3250
3698
  const exporterOptions = {
@@ -3258,33 +3706,40 @@ function exportScene(data, fileNameOrOptions = "scene.glb") {
3258
3706
  }
3259
3707
  try {
3260
3708
  exporter.parse(
3261
- data,
3709
+ exportTarget,
3262
3710
  (gltf) => {
3263
3711
  detachBundle();
3264
3712
  if (!(gltf instanceof ArrayBuffer)) {
3265
- throw new Error("Failed to export scene!");
3713
+ const error = new Error("Failed to export scene.");
3714
+ options.onError?.(error);
3715
+ return;
3266
3716
  }
3267
- const link = document.createElement("a");
3268
- link.href = URL.createObjectURL(
3269
- new Blob([gltf], {
3270
- type: "application/octet-stream"
3271
- })
3717
+ const sanitizedGltf = sanitizeExportedGlb(
3718
+ gltf,
3719
+ data.name?.trim() || void 0
3272
3720
  );
3273
3721
  const trimmed = fileName.trim();
3274
3722
  const safeFileName = trimmed.length > 0 ? trimmed : "scene.glb";
3275
3723
  const downloadName = safeFileName.toLowerCase().endsWith(".glb") ? safeFileName : `${safeFileName}.glb`;
3276
- link.download = downloadName;
3277
- link.click();
3278
- URL.revokeObjectURL(link.href);
3724
+ triggerBlobDownload(
3725
+ new Blob([sanitizedGltf], {
3726
+ type: "application/octet-stream"
3727
+ }),
3728
+ downloadName
3729
+ );
3730
+ options.onComplete?.();
3279
3731
  },
3280
- () => {
3732
+ (error) => {
3281
3733
  detachBundle();
3734
+ options.onError?.(normalizeExportError(error));
3282
3735
  },
3283
3736
  exporterOptions
3284
3737
  );
3285
3738
  } catch (error) {
3286
3739
  detachBundle();
3287
- throw error;
3740
+ const normalizedError = normalizeExportError(error);
3741
+ options.onError?.(normalizedError);
3742
+ throw normalizedError;
3288
3743
  }
3289
3744
  }
3290
3745
  // Annotate the CommonJS export names for ESM import in node:
@@ -3304,6 +3759,7 @@ function exportScene(data, fileNameOrOptions = "scene.glb") {
3304
3759
  loadGLTFFromBlobWithBundle,
3305
3760
  loadGLTFWithBundle,
3306
3761
  loadGltfFromBlob,
3762
+ parseGlbJsonChunk,
3307
3763
  useDefaultVizijStore,
3308
3764
  useFeatures,
3309
3765
  useVizijStore,