@tscircuit/3d-viewer 0.0.570 → 0.0.572

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.
Files changed (2) hide show
  1. package/dist/index.js +224 -132
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14432,7 +14432,7 @@ var require_browser = __commonJS({
14432
14432
  });
14433
14433
 
14434
14434
  // src/CadViewer.tsx
14435
- import { useState as useState37, useCallback as useCallback23, useRef as useRef28, useEffect as useEffect47 } from "react";
14435
+ import { useState as useState37, useCallback as useCallback23, useRef as useRef29, useEffect as useEffect46 } from "react";
14436
14436
  import * as THREE44 from "three";
14437
14437
 
14438
14438
  // src/CadViewerJscad.tsx
@@ -31205,12 +31205,15 @@ function GltfModel({
31205
31205
  useEffect7(() => {
31206
31206
  if (!model) return;
31207
31207
  model.traverse((child) => {
31208
- if (child instanceof THREE7.Mesh && child.material instanceof THREE7.MeshStandardMaterial) {
31208
+ if (!(child instanceof THREE7.Mesh)) return;
31209
+ const materials = Array.isArray(child.material) ? child.material : [child.material];
31210
+ for (const material of materials) {
31211
+ if (!(material instanceof THREE7.MeshStandardMaterial)) continue;
31209
31212
  if (isHovered) {
31210
- child.material.emissive.setHex(255);
31211
- child.material.emissiveIntensity = 0.2;
31213
+ material.emissive.setHex(255);
31214
+ material.emissiveIntensity = 0.2;
31212
31215
  } else {
31213
- child.material.emissiveIntensity = 0;
31216
+ material.emissiveIntensity = 0;
31214
31217
  }
31215
31218
  }
31216
31219
  });
@@ -31486,6 +31489,9 @@ function MixedStlModel({
31486
31489
 
31487
31490
  // src/three-components/StepModel.tsx
31488
31491
  import { useEffect as useEffect10, useState as useState6 } from "react";
31492
+ import { GLTFExporter } from "three-stdlib";
31493
+
31494
+ // src/three-components/step-mesh-to-group.ts
31489
31495
  import {
31490
31496
  BufferGeometry as BufferGeometry2,
31491
31497
  Color as Color3,
@@ -31494,7 +31500,88 @@ import {
31494
31500
  Mesh as Mesh6,
31495
31501
  MeshStandardMaterial as MeshStandardMaterial5
31496
31502
  } from "three";
31497
- import { GLTFExporter } from "three-stdlib";
31503
+ var DEFAULT_COLOR = new Color3(0.82, 0.82, 0.82);
31504
+ var createMaterial = (color) => new MeshStandardMaterial5({
31505
+ color: color ? new Color3(color[0], color[1], color[2]) : DEFAULT_COLOR
31506
+ });
31507
+ function applyFaceColorGroups(geometry, mesh) {
31508
+ const defaultMaterial = createMaterial(mesh.color);
31509
+ const brepFaces = mesh.brep_faces ?? [];
31510
+ if (brepFaces.length === 0) {
31511
+ return [defaultMaterial];
31512
+ }
31513
+ const sortedBrepFaces = brepFaces.length > 1 ? [...brepFaces].sort((a, b) => a.first - b.first) : brepFaces;
31514
+ const materials = [defaultMaterial];
31515
+ const materialIndexByColor = /* @__PURE__ */ new Map();
31516
+ const triangleCount = mesh.index.array.length / 3;
31517
+ let triangleIndex = 0;
31518
+ let faceIndex = 0;
31519
+ const getMaterialIndexForFace = (color) => {
31520
+ if (!color) return 0;
31521
+ const key = color.join(",");
31522
+ const existingIndex = materialIndexByColor.get(key);
31523
+ if (existingIndex !== void 0) {
31524
+ return existingIndex;
31525
+ }
31526
+ const nextIndex = materials.length;
31527
+ materials.push(createMaterial(color));
31528
+ materialIndexByColor.set(key, nextIndex);
31529
+ return nextIndex;
31530
+ };
31531
+ while (triangleIndex < triangleCount) {
31532
+ const currentFace = sortedBrepFaces[faceIndex];
31533
+ let lastTriangleExclusive = triangleCount;
31534
+ let materialIndex = 0;
31535
+ if (currentFace) {
31536
+ if (triangleIndex < currentFace.first) {
31537
+ lastTriangleExclusive = currentFace.first;
31538
+ } else {
31539
+ lastTriangleExclusive = currentFace.last + 1;
31540
+ materialIndex = getMaterialIndexForFace(currentFace.color);
31541
+ faceIndex += 1;
31542
+ }
31543
+ }
31544
+ if (lastTriangleExclusive > triangleIndex) {
31545
+ geometry.addGroup(
31546
+ triangleIndex * 3,
31547
+ (lastTriangleExclusive - triangleIndex) * 3,
31548
+ materialIndex
31549
+ );
31550
+ }
31551
+ triangleIndex = lastTriangleExclusive;
31552
+ }
31553
+ return materials;
31554
+ }
31555
+ function occtMeshesToGroup(meshes) {
31556
+ const group = new Group3();
31557
+ for (const mesh of meshes) {
31558
+ const positions = mesh.attributes.position?.array ?? [];
31559
+ const indices = mesh.index?.array ?? [];
31560
+ if (!positions.length || !indices.length) {
31561
+ continue;
31562
+ }
31563
+ const geometry = new BufferGeometry2();
31564
+ geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
31565
+ const normals = mesh.attributes.normal?.array ?? [];
31566
+ if (normals.length) {
31567
+ geometry.setAttribute("normal", new Float32BufferAttribute(normals, 3));
31568
+ } else {
31569
+ geometry.computeVertexNormals();
31570
+ }
31571
+ geometry.setIndex(indices);
31572
+ geometry.clearGroups();
31573
+ const materials = applyFaceColorGroups(geometry, mesh);
31574
+ const threeMesh = new Mesh6(
31575
+ geometry,
31576
+ materials.length > 1 ? materials : materials[0]
31577
+ );
31578
+ threeMesh.name = mesh.name;
31579
+ group.add(threeMesh);
31580
+ }
31581
+ return group;
31582
+ }
31583
+
31584
+ // src/three-components/StepModel.tsx
31498
31585
  import { jsx as jsx9 } from "react/jsx-runtime";
31499
31586
  var occtImportPromise;
31500
31587
  var OCCT_CDN_BASE_URL = "https://cdn.jsdelivr.net/npm/occt-import-js@0.0.23/dist";
@@ -31520,32 +31607,6 @@ async function loadOcctImport() {
31520
31607
  }
31521
31608
  return occtImportPromise;
31522
31609
  }
31523
- function occtMeshesToGroup(meshes) {
31524
- const group = new Group3();
31525
- for (const mesh of meshes) {
31526
- const positions = mesh.attributes.position?.array ?? [];
31527
- const indices = mesh.index?.array ?? [];
31528
- if (!positions.length || !indices.length) {
31529
- continue;
31530
- }
31531
- const geometry = new BufferGeometry2();
31532
- geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
31533
- const normals = mesh.attributes.normal?.array ?? [];
31534
- if (normals.length) {
31535
- geometry.setAttribute("normal", new Float32BufferAttribute(normals, 3));
31536
- } else {
31537
- geometry.computeVertexNormals();
31538
- }
31539
- geometry.setIndex(indices);
31540
- const material = new MeshStandardMaterial5({
31541
- color: mesh.color ? new Color3(mesh.color[0], mesh.color[1], mesh.color[2]) : new Color3(0.82, 0.82, 0.82)
31542
- });
31543
- const threeMesh = new Mesh6(geometry, material);
31544
- threeMesh.name = mesh.name;
31545
- group.add(threeMesh);
31546
- }
31547
- return group;
31548
- }
31549
31610
  async function convertStepUrlToGlb(stepUrl) {
31550
31611
  const response = await fetch(stepUrl);
31551
31612
  if (!response.ok) {
@@ -31577,7 +31638,7 @@ async function convertStepUrlToGlb(stepUrl) {
31577
31638
  });
31578
31639
  return glb;
31579
31640
  }
31580
- var CACHE_PREFIX = "step-glb-cache:";
31641
+ var CACHE_PREFIX = "step-glb-cache:v2:";
31581
31642
  function arrayBufferToBase64(buffer) {
31582
31643
  const bytes = new Uint8Array(buffer);
31583
31644
  const chunkSize = 32768;
@@ -32337,7 +32398,7 @@ import * as THREE20 from "three";
32337
32398
  // package.json
32338
32399
  var package_default = {
32339
32400
  name: "@tscircuit/3d-viewer",
32340
- version: "0.0.569",
32401
+ version: "0.0.571",
32341
32402
  main: "./dist/index.js",
32342
32403
  module: "./dist/index.js",
32343
32404
  type: "module",
@@ -32966,25 +33027,35 @@ import { useEffect as useEffect15, useMemo as useMemo13 } from "react";
32966
33027
  import * as THREE17 from "three";
32967
33028
  var Lights = () => {
32968
33029
  const { scene } = useThree();
32969
- const ambientLight = useMemo13(
32970
- () => new THREE17.AmbientLight(16777215, Math.PI / 2),
32971
- []
32972
- );
32973
- const pointLight = useMemo13(() => {
32974
- const light = new THREE17.PointLight(16777215, Math.PI / 4);
32975
- light.position.set(-10, -10, 10);
32976
- light.decay = 0;
32977
- return light;
33030
+ const lightRig = useMemo13(() => {
33031
+ const rig = new THREE17.Group();
33032
+ rig.name = "cad-viewer-light-rig";
33033
+ const ambientLight = new THREE17.AmbientLight(16777215, 0.18);
33034
+ ambientLight.name = "cad-viewer-soft-ambient";
33035
+ rig.add(ambientLight);
33036
+ const hemisphereLight = new THREE17.HemisphereLight(14543103, 2040093, 0.45);
33037
+ hemisphereLight.name = "cad-viewer-hemisphere";
33038
+ rig.add(hemisphereLight);
33039
+ const addDirectionalLight = (name, color, intensity, position) => {
33040
+ const light = new THREE17.DirectionalLight(color, intensity);
33041
+ light.name = name;
33042
+ light.position.set(...position);
33043
+ light.target.position.set(0, 0, 0);
33044
+ rig.add(light);
33045
+ rig.add(light.target);
33046
+ };
33047
+ addDirectionalLight("cad-viewer-key-light", 16773343, 2.4, [8, -10, 12]);
33048
+ addDirectionalLight("cad-viewer-fill-light", 14543103, 0.7, [-10, 8, 5]);
33049
+ addDirectionalLight("cad-viewer-rim-light", 16777215, 1.1, [-4, 10, 8]);
33050
+ return rig;
32978
33051
  }, []);
32979
33052
  useEffect15(() => {
32980
33053
  if (!scene) return;
32981
- scene.add(ambientLight);
32982
- scene.add(pointLight);
33054
+ scene.add(lightRig);
32983
33055
  return () => {
32984
- scene.remove(ambientLight);
32985
- scene.remove(pointLight);
33056
+ scene.remove(lightRig);
32986
33057
  };
32987
- }, [scene, ambientLight, pointLight]);
33058
+ }, [scene, lightRig]);
32988
33059
  return null;
32989
33060
  };
32990
33061
 
@@ -39472,7 +39543,7 @@ function composeContextScopes(...scopes) {
39472
39543
  }
39473
39544
 
39474
39545
  // node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
39475
- import * as React15 from "react";
39546
+ import * as React16 from "react";
39476
39547
 
39477
39548
  // node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
39478
39549
  import * as React14 from "react";
@@ -39481,7 +39552,32 @@ var useLayoutEffect2 = globalThis?.document ? React14.useLayoutEffect : () => {
39481
39552
 
39482
39553
  // node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
39483
39554
  import * as React22 from "react";
39484
- var useInsertionEffect = React15[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
39555
+
39556
+ // node_modules/@radix-ui/react-use-effect-event/dist/index.mjs
39557
+ import * as React15 from "react";
39558
+ var useReactEffectEvent = React15[" useEffectEvent ".trim().toString()];
39559
+ var useReactInsertionEffect = React15[" useInsertionEffect ".trim().toString()];
39560
+ function useEffectEvent(callback) {
39561
+ if (typeof useReactEffectEvent === "function") {
39562
+ return useReactEffectEvent(callback);
39563
+ }
39564
+ const ref = React15.useRef(() => {
39565
+ throw new Error("Cannot call an event handler while rendering.");
39566
+ });
39567
+ if (typeof useReactInsertionEffect === "function") {
39568
+ useReactInsertionEffect(() => {
39569
+ ref.current = callback;
39570
+ });
39571
+ } else {
39572
+ useLayoutEffect2(() => {
39573
+ ref.current = callback;
39574
+ });
39575
+ }
39576
+ return React15.useMemo(() => ((...args) => ref.current?.(...args)), []);
39577
+ }
39578
+
39579
+ // node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
39580
+ var useInsertionEffect = React16[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
39485
39581
  function useControllableState({
39486
39582
  prop,
39487
39583
  defaultProp,
@@ -39496,8 +39592,8 @@ function useControllableState({
39496
39592
  const isControlled = prop !== void 0;
39497
39593
  const value = isControlled ? prop : uncontrolledProp;
39498
39594
  if (true) {
39499
- const isControlledRef = React15.useRef(prop !== void 0);
39500
- React15.useEffect(() => {
39595
+ const isControlledRef = React16.useRef(prop !== void 0);
39596
+ React16.useEffect(() => {
39501
39597
  const wasControlled = isControlledRef.current;
39502
39598
  if (wasControlled !== isControlled) {
39503
39599
  const from = wasControlled ? "controlled" : "uncontrolled";
@@ -39509,7 +39605,7 @@ function useControllableState({
39509
39605
  isControlledRef.current = isControlled;
39510
39606
  }, [isControlled, caller]);
39511
39607
  }
39512
- const setValue = React15.useCallback(
39608
+ const setValue = React16.useCallback(
39513
39609
  (nextValue) => {
39514
39610
  if (isControlled) {
39515
39611
  const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
@@ -39528,13 +39624,13 @@ function useUncontrolledState({
39528
39624
  defaultProp,
39529
39625
  onChange
39530
39626
  }) {
39531
- const [value, setValue] = React15.useState(defaultProp);
39532
- const prevValueRef = React15.useRef(value);
39533
- const onChangeRef = React15.useRef(onChange);
39627
+ const [value, setValue] = React16.useState(defaultProp);
39628
+ const prevValueRef = React16.useRef(value);
39629
+ const onChangeRef = React16.useRef(onChange);
39534
39630
  useInsertionEffect(() => {
39535
39631
  onChangeRef.current = onChange;
39536
39632
  }, [onChange]);
39537
- React15.useEffect(() => {
39633
+ React16.useEffect(() => {
39538
39634
  if (prevValueRef.current !== value) {
39539
39635
  onChangeRef.current?.(value);
39540
39636
  prevValueRef.current = value;
@@ -39547,14 +39643,14 @@ function isFunction(value) {
39547
39643
  }
39548
39644
 
39549
39645
  // node_modules/@radix-ui/react-primitive/dist/index.mjs
39550
- import * as React17 from "react";
39646
+ import * as React18 from "react";
39551
39647
  import * as ReactDOM2 from "react-dom";
39552
39648
 
39553
39649
  // node_modules/@radix-ui/react-slot/dist/index.mjs
39554
- import * as React16 from "react";
39650
+ import * as React17 from "react";
39555
39651
  // @__NO_SIDE_EFFECTS__
39556
39652
  function createSlot(ownerName) {
39557
- const Slot2 = React16.forwardRef((props, forwardedRef) => {
39653
+ const Slot2 = React17.forwardRef((props, forwardedRef) => {
39558
39654
  let { children, ...slotProps } = props;
39559
39655
  let slottableElement = null;
39560
39656
  let hasSlottable = false;
@@ -39562,7 +39658,7 @@ function createSlot(ownerName) {
39562
39658
  if (isLazyComponent(children) && typeof use === "function") {
39563
39659
  children = use(children._payload);
39564
39660
  }
39565
- React16.Children.forEach(children, (maybeSlottable) => {
39661
+ React17.Children.forEach(children, (maybeSlottable) => {
39566
39662
  if (isSlottable(maybeSlottable)) {
39567
39663
  hasSlottable = true;
39568
39664
  const slottable = maybeSlottable;
@@ -39577,13 +39673,13 @@ function createSlot(ownerName) {
39577
39673
  }
39578
39674
  });
39579
39675
  if (slottableElement) {
39580
- slottableElement = React16.cloneElement(slottableElement, void 0, newChildren);
39676
+ slottableElement = React17.cloneElement(slottableElement, void 0, newChildren);
39581
39677
  } else if (
39582
39678
  // A `Slottable` was found but it didn't resolve to a single element (e.g.
39583
39679
  // it wrapped multiple elements, text, or a render-prop `child` that
39584
39680
  // wasn't an element). Don't fall back to treating the `Slottable` wrapper
39585
39681
  // itself as the slot target — throw a descriptive error below instead.
39586
- !hasSlottable && React16.Children.count(children) === 1 && React16.isValidElement(children)
39682
+ !hasSlottable && React17.Children.count(children) === 1 && React17.isValidElement(children)
39587
39683
  ) {
39588
39684
  slottableElement = children;
39589
39685
  }
@@ -39598,10 +39694,10 @@ function createSlot(ownerName) {
39598
39694
  return children;
39599
39695
  }
39600
39696
  const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});
39601
- if (slottableElement.type !== React16.Fragment) {
39697
+ if (slottableElement.type !== React17.Fragment) {
39602
39698
  mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;
39603
39699
  }
39604
- return React16.cloneElement(slottableElement, mergedProps);
39700
+ return React17.cloneElement(slottableElement, mergedProps);
39605
39701
  });
39606
39702
  Slot2.displayName = `${ownerName}.Slot`;
39607
39703
  return Slot2;
@@ -39610,10 +39706,10 @@ var SLOTTABLE_IDENTIFIER = /* @__PURE__ */ Symbol.for("radix.slottable");
39610
39706
  var getSlottableElementFromSlottable = (slottable, child) => {
39611
39707
  if ("child" in slottable.props) {
39612
39708
  const child2 = slottable.props.child;
39613
- if (!React16.isValidElement(child2)) return null;
39614
- return React16.cloneElement(child2, void 0, slottable.props.children(child2.props.children));
39709
+ if (!React17.isValidElement(child2)) return null;
39710
+ return React17.cloneElement(child2, void 0, slottable.props.children(child2.props.children));
39615
39711
  }
39616
- return React16.isValidElement(child) ? child : null;
39712
+ return React17.isValidElement(child) ? child : null;
39617
39713
  };
39618
39714
  function mergeProps(slotProps, childProps) {
39619
39715
  const overrideProps = { ...childProps };
@@ -39653,7 +39749,7 @@ function getElementRef(element) {
39653
39749
  return element.props.ref || element.ref;
39654
39750
  }
39655
39751
  function isSlottable(child) {
39656
- return React16.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
39752
+ return React17.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
39657
39753
  }
39658
39754
  var REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
39659
39755
  function isLazyComponent(element) {
@@ -39668,7 +39764,7 @@ var createSlotError = (ownerName) => {
39668
39764
  var createSlottableError = (ownerName) => {
39669
39765
  return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
39670
39766
  };
39671
- var use = React16[" use ".trim().toString()];
39767
+ var use = React17[" use ".trim().toString()];
39672
39768
 
39673
39769
  // node_modules/@radix-ui/react-primitive/dist/index.mjs
39674
39770
  import { jsx as jsx21 } from "react/jsx-runtime";
@@ -39693,7 +39789,7 @@ var NODES = [
39693
39789
  ];
39694
39790
  var Primitive = NODES.reduce((primitive, node) => {
39695
39791
  const Slot2 = createSlot(`Primitive.${node}`);
39696
- const Node2 = React17.forwardRef((props, forwardedRef) => {
39792
+ const Node2 = React18.forwardRef((props, forwardedRef) => {
39697
39793
  const { asChild, ...primitiveProps } = props;
39698
39794
  const Comp = asChild ? Slot2 : node;
39699
39795
  if (typeof window !== "undefined") {
@@ -39712,7 +39808,7 @@ function dispatchDiscreteCustomEvent(target, event) {
39712
39808
  import * as React42 from "react";
39713
39809
 
39714
39810
  // node_modules/@radix-ui/react-collection/dist/index.mjs
39715
- import * as React18 from "react";
39811
+ import * as React19 from "react";
39716
39812
  import { jsx as jsx22 } from "react/jsx-runtime";
39717
39813
  import * as React23 from "react";
39718
39814
  import { jsx as jsx23 } from "react/jsx-runtime";
@@ -39725,14 +39821,14 @@ function createCollection(name) {
39725
39821
  );
39726
39822
  const CollectionProvider = (props) => {
39727
39823
  const { scope, children } = props;
39728
- const ref = React18.useRef(null);
39729
- const itemMap = React18.useRef(/* @__PURE__ */ new Map()).current;
39824
+ const ref = React19.useRef(null);
39825
+ const itemMap = React19.useRef(/* @__PURE__ */ new Map()).current;
39730
39826
  return /* @__PURE__ */ jsx22(CollectionProviderImpl, { scope, itemMap, collectionRef: ref, children });
39731
39827
  };
39732
39828
  CollectionProvider.displayName = PROVIDER_NAME;
39733
39829
  const COLLECTION_SLOT_NAME = name + "CollectionSlot";
39734
39830
  const CollectionSlotImpl = createSlot(COLLECTION_SLOT_NAME);
39735
- const CollectionSlot = React18.forwardRef(
39831
+ const CollectionSlot = React19.forwardRef(
39736
39832
  (props, forwardedRef) => {
39737
39833
  const { scope, children } = props;
39738
39834
  const context = useCollectionContext(COLLECTION_SLOT_NAME, scope);
@@ -39744,13 +39840,13 @@ function createCollection(name) {
39744
39840
  const ITEM_SLOT_NAME = name + "CollectionItemSlot";
39745
39841
  const ITEM_DATA_ATTR = "data-radix-collection-item";
39746
39842
  const CollectionItemSlotImpl = createSlot(ITEM_SLOT_NAME);
39747
- const CollectionItemSlot = React18.forwardRef(
39843
+ const CollectionItemSlot = React19.forwardRef(
39748
39844
  (props, forwardedRef) => {
39749
39845
  const { scope, children, ...itemData } = props;
39750
- const ref = React18.useRef(null);
39846
+ const ref = React19.useRef(null);
39751
39847
  const composedRefs = useComposedRefs(forwardedRef, ref);
39752
39848
  const context = useCollectionContext(ITEM_SLOT_NAME, scope);
39753
- React18.useEffect(() => {
39849
+ React19.useEffect(() => {
39754
39850
  context.itemMap.set(ref, { ref, ...itemData });
39755
39851
  return () => void context.itemMap.delete(ref);
39756
39852
  });
@@ -39760,7 +39856,7 @@ function createCollection(name) {
39760
39856
  CollectionItemSlot.displayName = ITEM_SLOT_NAME;
39761
39857
  function useCollection3(scope) {
39762
39858
  const context = useCollectionContext(name + "CollectionConsumer", scope);
39763
- const getItems = React18.useCallback(() => {
39859
+ const getItems = React19.useCallback(() => {
39764
39860
  const collectionNode = context.collectionRef.current;
39765
39861
  if (!collectionNode) return [];
39766
39862
  const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`));
@@ -39780,11 +39876,11 @@ function createCollection(name) {
39780
39876
  }
39781
39877
 
39782
39878
  // node_modules/@radix-ui/react-direction/dist/index.mjs
39783
- import * as React19 from "react";
39879
+ import * as React20 from "react";
39784
39880
  import { jsx as jsx24 } from "react/jsx-runtime";
39785
- var DirectionContext = React19.createContext(void 0);
39881
+ var DirectionContext = React20.createContext(void 0);
39786
39882
  function useDirection(localDir) {
39787
- const globalDir = React19.useContext(DirectionContext);
39883
+ const globalDir = React20.useContext(DirectionContext);
39788
39884
  return localDir || globalDir || "ltr";
39789
39885
  }
39790
39886
 
@@ -39792,28 +39888,13 @@ function useDirection(localDir) {
39792
39888
  import * as React24 from "react";
39793
39889
 
39794
39890
  // node_modules/@radix-ui/react-use-callback-ref/dist/index.mjs
39795
- import * as React20 from "react";
39891
+ import * as React21 from "react";
39796
39892
  function useCallbackRef(callback) {
39797
- const callbackRef = React20.useRef(callback);
39798
- React20.useEffect(() => {
39893
+ const callbackRef = React21.useRef(callback);
39894
+ React21.useEffect(() => {
39799
39895
  callbackRef.current = callback;
39800
39896
  });
39801
- return React20.useMemo(() => ((...args) => callbackRef.current?.(...args)), []);
39802
- }
39803
-
39804
- // node_modules/@radix-ui/react-use-escape-keydown/dist/index.mjs
39805
- import * as React21 from "react";
39806
- function useEscapeKeydown(onEscapeKeyDownProp, ownerDocument = globalThis?.document) {
39807
- const onEscapeKeyDown = useCallbackRef(onEscapeKeyDownProp);
39808
- React21.useEffect(() => {
39809
- const handleKeyDown = (event) => {
39810
- if (event.key === "Escape") {
39811
- onEscapeKeyDown(event);
39812
- }
39813
- };
39814
- ownerDocument.addEventListener("keydown", handleKeyDown, { capture: true });
39815
- return () => ownerDocument.removeEventListener("keydown", handleKeyDown, { capture: true });
39816
- }, [onEscapeKeyDown, ownerDocument]);
39897
+ return React21.useMemo(() => ((...args) => callbackRef.current?.(...args)), []);
39817
39898
  }
39818
39899
 
39819
39900
  // node_modules/@radix-ui/react-dismissable-layer/dist/index.mjs
@@ -39850,7 +39931,7 @@ var DismissableLayer = React24.forwardRef(
39850
39931
  const [node, setNode] = React24.useState(null);
39851
39932
  const ownerDocument = node?.ownerDocument ?? globalThis?.document;
39852
39933
  const [, force] = React24.useState({});
39853
- const composedRefs = useComposedRefs(forwardedRef, (node2) => setNode(node2));
39934
+ const composedRefs = useComposedRefs(forwardedRef, setNode);
39854
39935
  const layers = Array.from(context.layers);
39855
39936
  const [highestLayerWithOutsidePointerEventsDisabled] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1);
39856
39937
  const highestLayerWithOutsidePointerEventsDisabledIndex = layers.indexOf(highestLayerWithOutsidePointerEventsDisabled);
@@ -39890,15 +39971,24 @@ var DismissableLayer = React24.forwardRef(
39890
39971
  onInteractOutside?.(event);
39891
39972
  if (!event.defaultPrevented) onDismiss?.();
39892
39973
  }, ownerDocument);
39893
- useEscapeKeydown((event) => {
39894
- const isHighestLayer = index2 === context.layers.size - 1;
39895
- if (!isHighestLayer) return;
39974
+ const isHighestLayer = node ? index2 === layers.length - 1 : false;
39975
+ const handleKeyDown = useEffectEvent((event) => {
39976
+ if (event.key !== "Escape") {
39977
+ return;
39978
+ }
39896
39979
  onEscapeKeyDown?.(event);
39897
39980
  if (!event.defaultPrevented && onDismiss) {
39898
39981
  event.preventDefault();
39899
39982
  onDismiss();
39900
39983
  }
39901
- }, ownerDocument);
39984
+ });
39985
+ React24.useEffect(() => {
39986
+ if (!isHighestLayer) {
39987
+ return;
39988
+ }
39989
+ ownerDocument.addEventListener("keydown", handleKeyDown, { capture: true });
39990
+ return () => ownerDocument.removeEventListener("keydown", handleKeyDown, { capture: true });
39991
+ }, [ownerDocument, isHighestLayer]);
39902
39992
  React24.useEffect(() => {
39903
39993
  if (!node) return;
39904
39994
  if (disableOutsidePointerEvents) {
@@ -40174,7 +40264,7 @@ var FocusScope = React26.forwardRef((props, forwardedRef) => {
40174
40264
  const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);
40175
40265
  const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);
40176
40266
  const lastFocusedElementRef = React26.useRef(null);
40177
- const composedRefs = useComposedRefs(forwardedRef, (node) => setContainer(node));
40267
+ const composedRefs = useComposedRefs(forwardedRef, setContainer);
40178
40268
  const focusScope = React26.useRef({
40179
40269
  paused: false,
40180
40270
  pause() {
@@ -42448,7 +42538,7 @@ var PopperContent = React31.forwardRef(
42448
42538
  } = props;
42449
42539
  const context = usePopperContext(CONTENT_NAME, __scopePopper);
42450
42540
  const [content, setContent] = React31.useState(null);
42451
- const composedRefs = useComposedRefs(forwardedRef, (node) => setContent(node));
42541
+ const composedRefs = useComposedRefs(forwardedRef, setContent);
42452
42542
  const [arrow4, setArrow] = React31.useState(null);
42453
42543
  const arrowSize = useSize(arrow4);
42454
42544
  const arrowWidth = arrowSize?.width ?? 0;
@@ -44538,6 +44628,7 @@ var MenuSubTrigger = React42.forwardRef(
44538
44628
  onPointerGraceIntentChange(null);
44539
44629
  };
44540
44630
  }, [pointerGraceTimerRef, onPointerGraceIntentChange]);
44631
+ const composedRefs = useComposedRefs(forwardedRef, subContext.onTriggerChange);
44541
44632
  return /* @__PURE__ */ jsx31(MenuAnchor, { asChild: true, ...scope, children: /* @__PURE__ */ jsx31(
44542
44633
  MenuItemImpl,
44543
44634
  {
@@ -44547,7 +44638,7 @@ var MenuSubTrigger = React42.forwardRef(
44547
44638
  "aria-controls": context.open ? subContext.contentId : void 0,
44548
44639
  "data-state": getOpenState(context.open),
44549
44640
  ...props,
44550
- ref: composeRefs(forwardedRef, subContext.onTriggerChange),
44641
+ ref: composedRefs,
44551
44642
  onClick: (event) => {
44552
44643
  props.onClick?.(event);
44553
44644
  if (props.disabled || event.defaultPrevented) return;
@@ -44724,7 +44815,7 @@ var Root32 = Menu;
44724
44815
  var Anchor2 = MenuAnchor;
44725
44816
  var Portal2 = MenuPortal;
44726
44817
  var Content2 = MenuContent;
44727
- var Group6 = MenuGroup;
44818
+ var Group7 = MenuGroup;
44728
44819
  var Label = MenuLabel;
44729
44820
  var Item2 = MenuItem;
44730
44821
  var CheckboxItem = MenuCheckboxItem;
@@ -44786,6 +44877,7 @@ var DropdownMenuTrigger = React43.forwardRef(
44786
44877
  const { __scopeDropdownMenu, disabled = false, ...triggerProps } = props;
44787
44878
  const context = useDropdownMenuContext(TRIGGER_NAME, __scopeDropdownMenu);
44788
44879
  const menuScope = useMenuScope(__scopeDropdownMenu);
44880
+ const composedRefs = useComposedRefs(forwardedRef, context.triggerRef);
44789
44881
  return /* @__PURE__ */ jsx32(Anchor2, { asChild: true, ...menuScope, children: /* @__PURE__ */ jsx32(
44790
44882
  Primitive.button,
44791
44883
  {
@@ -44798,7 +44890,7 @@ var DropdownMenuTrigger = React43.forwardRef(
44798
44890
  "data-disabled": disabled ? "" : void 0,
44799
44891
  disabled,
44800
44892
  ...triggerProps,
44801
- ref: composeRefs(forwardedRef, context.triggerRef),
44893
+ ref: composedRefs,
44802
44894
  onPointerDown: composeEventHandlers(props.onPointerDown, (event) => {
44803
44895
  if (!disabled && event.button === 0 && event.ctrlKey === false) {
44804
44896
  context.onOpenToggle();
@@ -44870,7 +44962,7 @@ var DropdownMenuGroup = React43.forwardRef(
44870
44962
  (props, forwardedRef) => {
44871
44963
  const { __scopeDropdownMenu, ...groupProps } = props;
44872
44964
  const menuScope = useMenuScope(__scopeDropdownMenu);
44873
- return /* @__PURE__ */ jsx32(Group6, { ...menuScope, ...groupProps, ref: forwardedRef });
44965
+ return /* @__PURE__ */ jsx32(Group7, { ...menuScope, ...groupProps, ref: forwardedRef });
44874
44966
  }
44875
44967
  );
44876
44968
  DropdownMenuGroup.displayName = GROUP_NAME3;
@@ -45722,16 +45814,16 @@ var ContextMenu = ({
45722
45814
  };
45723
45815
 
45724
45816
  // src/components/KeyboardShortcutsDialog.tsx
45725
- import { useEffect as useEffect46, useMemo as useMemo29, useRef as useRef27, useState as useState36 } from "react";
45817
+ import { useEffect as useEffect45, useMemo as useMemo30, useRef as useRef28, useState as useState36 } from "react";
45726
45818
  import { jsx as jsx36, jsxs as jsxs10 } from "react/jsx-runtime";
45727
45819
  var KeyboardShortcutsDialog = ({
45728
45820
  open,
45729
45821
  onClose
45730
45822
  }) => {
45731
45823
  const [query, setQuery] = useState36("");
45732
- const inputRef = useRef27(null);
45824
+ const inputRef = useRef28(null);
45733
45825
  const hotkeys = useHotkeyRegistry();
45734
- useEffect46(() => {
45826
+ useEffect45(() => {
45735
45827
  if (!open) return void 0;
45736
45828
  const handleKeyDown = (event) => {
45737
45829
  if (event.key === "Escape") {
@@ -45742,14 +45834,14 @@ var KeyboardShortcutsDialog = ({
45742
45834
  window.addEventListener("keydown", handleKeyDown);
45743
45835
  return () => window.removeEventListener("keydown", handleKeyDown);
45744
45836
  }, [open, onClose]);
45745
- useEffect46(() => {
45837
+ useEffect45(() => {
45746
45838
  if (open) {
45747
45839
  setTimeout(() => {
45748
45840
  inputRef.current?.focus();
45749
45841
  }, 0);
45750
45842
  }
45751
45843
  }, [open]);
45752
- const filteredHotkeys = useMemo29(() => {
45844
+ const filteredHotkeys = useMemo30(() => {
45753
45845
  const normalizedQuery = query.trim().toLowerCase();
45754
45846
  if (!normalizedQuery) {
45755
45847
  return hotkeys;
@@ -45925,7 +46017,7 @@ var CadViewerInner = (props) => {
45925
46017
  const stored = window.localStorage.getItem("cadViewerEngine");
45926
46018
  return stored === "jscad" || stored === "manifold" ? stored : "manifold";
45927
46019
  });
45928
- const containerRef = useRef28(null);
46020
+ const containerRef = useRef29(null);
45929
46021
  const [isKeyboardShortcutsDialogOpen, setIsKeyboardShortcutsDialogOpen] = useState37(false);
45930
46022
  const [autoRotate, setAutoRotate] = useState37(() => {
45931
46023
  const stored = window.localStorage.getItem("cadViewerAutoRotate");
@@ -45939,7 +46031,7 @@ var CadViewerInner = (props) => {
45939
46031
  const { cameraType, setCameraType } = useCameraController();
45940
46032
  const { visibility, setLayerVisibility } = useLayerVisibility();
45941
46033
  const { showToast } = useToast();
45942
- const cameraControllerRef = useRef28(null);
46034
+ const cameraControllerRef = useRef29(null);
45943
46035
  const externalCameraControllerReady = props.onCameraControllerReady;
45944
46036
  const {
45945
46037
  menuVisible,
@@ -45948,10 +46040,10 @@ var CadViewerInner = (props) => {
45948
46040
  contextMenuEventHandlers,
45949
46041
  setMenuVisible
45950
46042
  } = useContextMenu({ containerRef });
45951
- const autoRotateUserToggledRef = useRef28(autoRotateUserToggled);
46043
+ const autoRotateUserToggledRef = useRef29(autoRotateUserToggled);
45952
46044
  autoRotateUserToggledRef.current = autoRotateUserToggled;
45953
- const isAnimatingRef = useRef28(false);
45954
- const lastPresetSelectTime = useRef28(0);
46045
+ const isAnimatingRef = useRef29(false);
46046
+ const lastPresetSelectTime = useRef29(0);
45955
46047
  const PRESET_COOLDOWN = 1e3;
45956
46048
  const handleUserInteraction = useCallback23(() => {
45957
46049
  if (isAnimatingRef.current || Date.now() - lastPresetSelectTime.current < PRESET_COOLDOWN) {
@@ -46046,24 +46138,24 @@ var CadViewerInner = (props) => {
46046
46138
  description: "Toggle translucent components"
46047
46139
  }
46048
46140
  );
46049
- useEffect47(() => {
46141
+ useEffect46(() => {
46050
46142
  if (containerRef.current) {
46051
46143
  registerHotkeyViewer(containerRef.current);
46052
46144
  }
46053
46145
  }, []);
46054
- useEffect47(() => {
46146
+ useEffect46(() => {
46055
46147
  window.localStorage.setItem("cadViewerEngine", engine);
46056
46148
  }, [engine]);
46057
- useEffect47(() => {
46149
+ useEffect46(() => {
46058
46150
  window.localStorage.setItem("cadViewerAutoRotate", String(autoRotate));
46059
46151
  }, [autoRotate]);
46060
- useEffect47(() => {
46152
+ useEffect46(() => {
46061
46153
  window.localStorage.setItem(
46062
46154
  "cadViewerAutoRotateUserToggled",
46063
46155
  String(autoRotateUserToggled)
46064
46156
  );
46065
46157
  }, [autoRotateUserToggled]);
46066
- useEffect47(() => {
46158
+ useEffect46(() => {
46067
46159
  window.localStorage.setItem("cadViewerCameraType", cameraType);
46068
46160
  }, [cameraType]);
46069
46161
  const viewerKey = props.circuitJson ? JSON.stringify(props.circuitJson) : void 0;
@@ -46464,10 +46556,10 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
46464
46556
 
46465
46557
  // src/hooks/exporter/gltf.ts
46466
46558
  import { GLTFExporter as GLTFExporter3 } from "three-stdlib";
46467
- import { useEffect as useEffect48, useState as useState38, useMemo as useMemo30, useCallback as useCallback24 } from "react";
46559
+ import { useEffect as useEffect47, useState as useState38, useMemo as useMemo31, useCallback as useCallback24 } from "react";
46468
46560
  function useSaveGltfAs(options = {}) {
46469
46561
  const parse2 = useParser(options);
46470
- const link = useMemo30(() => document.createElement("a"), []);
46562
+ const link = useMemo31(() => document.createElement("a"), []);
46471
46563
  const saveAs = async (filename) => {
46472
46564
  const name = filename ?? options.filename ?? "";
46473
46565
  if (options.binary == null) options.binary = name.endsWith(".glb");
@@ -46477,7 +46569,7 @@ function useSaveGltfAs(options = {}) {
46477
46569
  link.dispatchEvent(new MouseEvent("click"));
46478
46570
  URL.revokeObjectURL(url);
46479
46571
  };
46480
- useEffect48(
46572
+ useEffect47(
46481
46573
  () => () => {
46482
46574
  link.remove();
46483
46575
  instance = null;
@@ -46498,11 +46590,11 @@ function useExportGltfUrl(options = {}) {
46498
46590
  (instance) => parse2(instance).then(setUrl).catch(setError),
46499
46591
  []
46500
46592
  );
46501
- useEffect48(() => () => URL.revokeObjectURL(url), [url]);
46593
+ useEffect47(() => () => URL.revokeObjectURL(url), [url]);
46502
46594
  return [ref, url, error];
46503
46595
  }
46504
46596
  function useParser(options = {}) {
46505
- const exporter = useMemo30(() => new GLTFExporter3(), []);
46597
+ const exporter = useMemo31(() => new GLTFExporter3(), []);
46506
46598
  return (instance) => {
46507
46599
  const { promise, resolve, reject } = Promise.withResolvers();
46508
46600
  exporter.parse(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.570",
3
+ "version": "0.0.572",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",