@tscircuit/3d-viewer 0.0.624 → 0.0.626

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
@@ -18978,9 +18978,44 @@ var useHiddenCadComponents = (circuitKey) => {
18978
18978
  };
18979
18979
  };
18980
18980
 
18981
+ // src/utils/configure-runtime-font.ts
18982
+ import { configureTextBuilder } from "troika-three-text";
18983
+
18984
+ // src/utils/get-runtime-asset-url.ts
18985
+ function getRuntimeAssetUrl(packageName, version, path) {
18986
+ const baseUrl = typeof window !== "undefined" ? window.TSCIRCUIT_JSCDN_BASE_URL : void 0;
18987
+ if (!baseUrl) {
18988
+ return `https://cdn.jsdelivr.net/npm/${packageName}@${version}/${path}`;
18989
+ }
18990
+ return new URL(
18991
+ `${baseUrl.replace(/\/+$/, "")}/${packageName}/${version}/${path}`,
18992
+ window.location.href
18993
+ ).href;
18994
+ }
18995
+
18996
+ // src/utils/configure-runtime-font.ts
18997
+ var configured = false;
18998
+ function configureRuntimeFont() {
18999
+ if (configured || typeof window === "undefined" || !window.TSCIRCUIT_JSCDN_BASE_URL)
19000
+ return;
19001
+ configureTextBuilder({
19002
+ defaultFontURL: getRuntimeAssetUrl(
19003
+ "three",
19004
+ "0.165.0",
19005
+ "examples/fonts/ttf/kenpixel.ttf"
19006
+ ),
19007
+ unicodeFontsURL: getRuntimeAssetUrl(
19008
+ "troika-three-text",
19009
+ "0.52.5",
19010
+ "unicode-fonts/"
19011
+ )
19012
+ });
19013
+ configured = true;
19014
+ }
19015
+
18981
19016
  // src/CadViewer.tsx
18982
- import { useCallback as useCallback25, useContext as useContext11, useEffect as useEffect50, useRef as useRef29, useState as useState42 } from "react";
18983
- import * as THREE56 from "three";
19017
+ import { useCallback as useCallback25, useContext as useContext11, useEffect as useEffect51, useRef as useRef29, useState as useState42 } from "react";
19018
+ import * as THREE57 from "three";
18984
19019
 
18985
19020
  // src/hooks/use-convert-children-to-soup.ts
18986
19021
  import { Circuit } from "@tscircuit/core";
@@ -20129,7 +20164,6 @@ function occtMeshesToGroup(meshes) {
20129
20164
  // src/three-components/StepModel.tsx
20130
20165
  import { jsx as jsx9 } from "react/jsx-runtime";
20131
20166
  var occtImportPromise;
20132
- var OCCT_CDN_BASE_URL = "https://cdn.jsdelivr.net/npm/occt-import-js@0.0.23/dist";
20133
20167
  function resolveOcctFactory(candidate) {
20134
20168
  if (typeof candidate === "function") {
20135
20169
  return candidate;
@@ -20141,13 +20175,19 @@ function resolveOcctFactory(candidate) {
20141
20175
  }
20142
20176
  async function loadOcctImport() {
20143
20177
  if (!occtImportPromise) {
20144
- const imported = await import(
20145
- /* @vite-ignore */
20146
- "https://cdn.jsdelivr.net/npm/occt-import-js@0.0.23/+esm"
20147
- );
20148
- const factory = resolveOcctFactory(imported);
20149
- occtImportPromise = factory({
20150
- locateFile: (path) => `${OCCT_CDN_BASE_URL}/${path}`
20178
+ occtImportPromise = (async () => {
20179
+ const imported = await import(
20180
+ /* @vite-ignore */
20181
+ /* webpackIgnore: true */
20182
+ getRuntimeAssetUrl("occt-import-js", "0.0.23", "+esm")
20183
+ );
20184
+ const factory = resolveOcctFactory(imported);
20185
+ return factory({
20186
+ locateFile: (path) => getRuntimeAssetUrl("occt-import-js", "0.0.23", `dist/${path}`)
20187
+ });
20188
+ })().catch((error) => {
20189
+ occtImportPromise = void 0;
20190
+ throw error;
20151
20191
  });
20152
20192
  }
20153
20193
  return occtImportPromise;
@@ -20945,13 +20985,13 @@ var AnyCadComponent = ({
20945
20985
  };
20946
20986
 
20947
20987
  // src/CadViewerContainer.tsx
20948
- import { forwardRef as forwardRef2, useEffect as useEffect21, useMemo as useMemo17, useState as useState13 } from "react";
20949
- import * as THREE26 from "three";
20988
+ import { forwardRef as forwardRef2, useEffect as useEffect22, useMemo as useMemo17, useState as useState13 } from "react";
20989
+ import * as THREE27 from "three";
20950
20990
 
20951
20991
  // package.json
20952
20992
  var package_default = {
20953
20993
  name: "@tscircuit/3d-viewer",
20954
- version: "0.0.623",
20994
+ version: "0.0.625",
20955
20995
  repository: {
20956
20996
  type: "git",
20957
20997
  url: "https://github.com/tscircuit/3d-viewer"
@@ -21105,44 +21145,79 @@ import {
21105
21145
  useRef as useRef3,
21106
21146
  useState as useState11
21107
21147
  } from "react";
21148
+ import * as THREE16 from "three";
21149
+
21150
+ // src/utils/camera-session-storage.ts
21108
21151
  import * as THREE15 from "three";
21109
- import { jsx as jsx12 } from "react/jsx-runtime";
21110
- var CAMERA_KEY = "cadViewerCameraStateSession";
21111
- var saveCameraToSession = (camera, controls) => {
21152
+ var CAMERA_SESSION_STORAGE_KEY = "cadViewerCameraStateSession";
21153
+ function getCameraSessionStorageKey(cameraSessionScope) {
21154
+ if (!cameraSessionScope) return CAMERA_SESSION_STORAGE_KEY;
21155
+ return `${CAMERA_SESSION_STORAGE_KEY}:${cameraSessionScope}`;
21156
+ }
21157
+ function saveCameraSession({
21158
+ camera,
21159
+ controls,
21160
+ cameraSessionScope
21161
+ }) {
21112
21162
  try {
21113
- const savedCameraSession = {
21163
+ const storedCameraSession = {
21114
21164
  position: camera.position.toArray(),
21115
21165
  quaternion: camera.quaternion.toArray(),
21116
21166
  up: camera.up.toArray(),
21117
- fov: camera.fov ?? 50,
21167
+ fov: camera instanceof THREE15.PerspectiveCamera ? camera.fov : 50,
21118
21168
  target: controls.target.toArray()
21119
21169
  };
21120
- sessionStorage.setItem(CAMERA_KEY, JSON.stringify(savedCameraSession));
21121
- } catch (err) {
21122
- console.warn("Failed to save camera:", err);
21170
+ sessionStorage.setItem(
21171
+ getCameraSessionStorageKey(cameraSessionScope),
21172
+ JSON.stringify(storedCameraSession)
21173
+ );
21174
+ } catch (error) {
21175
+ console.warn("Failed to save camera:", error);
21123
21176
  }
21124
- };
21125
- var loadCameraFromSession = (camera, controls) => {
21177
+ }
21178
+ function loadCameraSession({
21179
+ camera,
21180
+ controls,
21181
+ cameraSessionScope
21182
+ }) {
21126
21183
  try {
21127
- const raw = sessionStorage.getItem(CAMERA_KEY);
21128
- if (!raw) return false;
21129
- const state = JSON.parse(raw);
21130
- camera.position.fromArray(state.position);
21131
- camera.quaternion.fromArray(state.quaternion);
21132
- camera.up.fromArray(state.up);
21133
- if ("fov" in camera) {
21134
- const persp = camera;
21135
- persp.fov = state.fov;
21136
- persp.updateProjectionMatrix?.();
21137
- }
21138
- controls.target.fromArray(state.target);
21184
+ const storedCameraSessionJson = sessionStorage.getItem(
21185
+ getCameraSessionStorageKey(cameraSessionScope)
21186
+ );
21187
+ if (!storedCameraSessionJson) return false;
21188
+ const storedCameraSession = JSON.parse(
21189
+ storedCameraSessionJson
21190
+ );
21191
+ camera.position.fromArray(storedCameraSession.position);
21192
+ camera.quaternion.fromArray(storedCameraSession.quaternion);
21193
+ camera.up.fromArray(storedCameraSession.up);
21194
+ if (camera instanceof THREE15.PerspectiveCamera) {
21195
+ camera.fov = storedCameraSession.fov;
21196
+ camera.updateProjectionMatrix();
21197
+ }
21198
+ controls.target.fromArray(storedCameraSession.target);
21139
21199
  controls.update();
21140
21200
  camera.updateMatrixWorld();
21141
21201
  return true;
21142
- } catch (err) {
21143
- console.warn("Failed to restore camera:", err);
21202
+ } catch (error) {
21203
+ console.warn("Failed to restore camera:", error);
21144
21204
  return false;
21145
21205
  }
21206
+ }
21207
+
21208
+ // src/contexts/CameraControllerContext.tsx
21209
+ import { jsx as jsx12 } from "react/jsx-runtime";
21210
+ function resolveCameraSessionContext(cameraSessionContext) {
21211
+ if ("controls" in cameraSessionContext) return cameraSessionContext;
21212
+ return { controls: cameraSessionContext };
21213
+ }
21214
+ var saveCameraToSession = (camera, cameraSessionContext) => {
21215
+ const { controls, cameraSessionScope } = resolveCameraSessionContext(cameraSessionContext);
21216
+ saveCameraSession({ camera, controls, cameraSessionScope });
21217
+ };
21218
+ var loadCameraFromSession = (camera, cameraSessionContext) => {
21219
+ const { controls, cameraSessionScope } = resolveCameraSessionContext(cameraSessionContext);
21220
+ return loadCameraSession({ camera, controls, cameraSessionScope });
21146
21221
  };
21147
21222
  var CameraControllerContext = createContext6(void 0);
21148
21223
  var CameraControllerProvider = ({ children, defaultTarget, initialCameraPosition, initialCameraType }) => {
@@ -21154,7 +21229,7 @@ var CameraControllerProvider = ({ children, defaultTarget, initialCameraPosition
21154
21229
  );
21155
21230
  const [cameraPosition, setCameraPosition] = useState11(initialCameraPosition ?? null);
21156
21231
  const [cameraRotation, setCameraRotation] = useState11(
21157
- new THREE15.Euler(0, 0, 0)
21232
+ new THREE16.Euler(0, 0, 0)
21158
21233
  );
21159
21234
  const baseDistance = useMemo11(() => {
21160
21235
  const [x, y, z] = initialCameraPosition ?? [5, -5, 5];
@@ -21308,7 +21383,7 @@ var useCameraController = () => {
21308
21383
 
21309
21384
  // src/hooks/cameraAnimation.ts
21310
21385
  import { useCallback as useCallback5, useEffect as useEffect14, useRef as useRef4 } from "react";
21311
- import * as THREE16 from "three";
21386
+ import * as THREE17 from "three";
21312
21387
  var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
21313
21388
  var CameraAnimatorWithContext = () => {
21314
21389
  const {
@@ -21319,24 +21394,24 @@ var CameraAnimatorWithContext = () => {
21319
21394
  getPresetConfig
21320
21395
  } = useCameraController();
21321
21396
  const animationRef = useRef4(null);
21322
- const tempQuaternion = useRef4(new THREE16.Quaternion());
21323
- const tempTarget = useRef4(new THREE16.Vector3());
21324
- const tempUp = useRef4(new THREE16.Vector3());
21325
- const tempRoll = useRef4(new THREE16.Quaternion());
21326
- const tempRollTarget = useRef4(new THREE16.Quaternion());
21327
- const baseOrientationHelper = useRef4(new THREE16.Object3D());
21328
- const orientationHelper = useRef4(new THREE16.Object3D());
21397
+ const tempQuaternion = useRef4(new THREE17.Quaternion());
21398
+ const tempTarget = useRef4(new THREE17.Vector3());
21399
+ const tempUp = useRef4(new THREE17.Vector3());
21400
+ const tempRoll = useRef4(new THREE17.Quaternion());
21401
+ const tempRollTarget = useRef4(new THREE17.Quaternion());
21402
+ const baseOrientationHelper = useRef4(new THREE17.Object3D());
21403
+ const orientationHelper = useRef4(new THREE17.Object3D());
21329
21404
  const animateTo = useCallback5(
21330
21405
  ({ position, target, up, durationMs = 600 }) => {
21331
21406
  if (!mainCameraRef.current) return;
21332
21407
  const currentTarget = controlsRef.current?.target ?? defaultTarget;
21333
- const toPosition = new THREE16.Vector3(
21408
+ const toPosition = new THREE17.Vector3(
21334
21409
  position[0],
21335
21410
  position[1],
21336
21411
  position[2]
21337
21412
  );
21338
- const resolvedTarget = target ? new THREE16.Vector3(target[0], target[1], target[2]) : defaultTarget.clone();
21339
- const resolvedUp = new THREE16.Vector3(...up ?? [0, 0, 1]).normalize();
21413
+ const resolvedTarget = target ? new THREE17.Vector3(target[0], target[1], target[2]) : defaultTarget.clone();
21414
+ const resolvedUp = new THREE17.Vector3(...up ?? [0, 0, 1]).normalize();
21340
21415
  const toOrientationHelper = orientationHelper.current;
21341
21416
  toOrientationHelper.position.copy(toPosition);
21342
21417
  toOrientationHelper.up.copy(resolvedUp);
@@ -21448,43 +21523,63 @@ var CameraAnimatorWithContext = () => {
21448
21523
  };
21449
21524
 
21450
21525
  // src/hooks/useCameraSession.ts
21451
- import { useCallback as useCallback6, useRef as useRef5 } from "react";
21452
- var useCameraSession = () => {
21526
+ import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef5 } from "react";
21527
+ var CAMERA_SESSION_NOT_RESTORED = /* @__PURE__ */ Symbol("camera-session-not-restored");
21528
+ var useCameraSession = ({
21529
+ cameraSessionScope
21530
+ } = {}) => {
21453
21531
  const { controlsRef } = useCameraController();
21454
21532
  const cameraRef = useRef5(null);
21455
- const cameraRestoredRef = useRef5(false);
21533
+ const restoredCameraSessionScopeRef = useRef5(CAMERA_SESSION_NOT_RESTORED);
21456
21534
  const saveTimeoutRef = useRef5(null);
21535
+ useEffect15(() => {
21536
+ return () => {
21537
+ if (saveTimeoutRef.current !== null) {
21538
+ clearTimeout(saveTimeoutRef.current);
21539
+ saveTimeoutRef.current = null;
21540
+ }
21541
+ };
21542
+ }, [cameraSessionScope]);
21457
21543
  const handleCameraCreated = useCallback6(
21458
21544
  (camera) => {
21459
21545
  cameraRef.current = camera;
21460
- if (!cameraRestoredRef.current && controlsRef.current) {
21461
- const restored = loadCameraFromSession(camera, controlsRef.current);
21546
+ if (restoredCameraSessionScopeRef.current !== cameraSessionScope && controlsRef.current) {
21547
+ const restored = loadCameraFromSession(camera, {
21548
+ controls: controlsRef.current,
21549
+ cameraSessionScope
21550
+ });
21551
+ restoredCameraSessionScopeRef.current = cameraSessionScope;
21462
21552
  if (restored) {
21463
- cameraRestoredRef.current = true;
21553
+ return;
21464
21554
  }
21465
21555
  }
21466
21556
  },
21467
- [controlsRef]
21557
+ [cameraSessionScope, controlsRef]
21468
21558
  );
21469
21559
  const handleControlsChange = useCallback6(() => {
21470
21560
  if (!cameraRef.current || !controlsRef.current) return;
21471
- if (!cameraRestoredRef.current) {
21472
- const restored = loadCameraFromSession(
21473
- cameraRef.current,
21474
- controlsRef.current
21475
- );
21561
+ if (restoredCameraSessionScopeRef.current !== cameraSessionScope) {
21562
+ const restored = loadCameraFromSession(cameraRef.current, {
21563
+ controls: controlsRef.current,
21564
+ cameraSessionScope
21565
+ });
21566
+ restoredCameraSessionScopeRef.current = cameraSessionScope;
21476
21567
  if (restored) {
21477
- cameraRestoredRef.current = true;
21478
21568
  return;
21479
21569
  }
21480
21570
  }
21481
- clearTimeout(saveTimeoutRef.current);
21571
+ if (saveTimeoutRef.current !== null) {
21572
+ clearTimeout(saveTimeoutRef.current);
21573
+ }
21482
21574
  saveTimeoutRef.current = setTimeout(() => {
21483
21575
  if (cameraRef.current && controlsRef.current) {
21484
- saveCameraToSession(cameraRef.current, controlsRef.current);
21576
+ saveCameraToSession(cameraRef.current, {
21577
+ controls: controlsRef.current,
21578
+ cameraSessionScope
21579
+ });
21485
21580
  }
21486
21581
  }, 150);
21487
- }, [controlsRef]);
21582
+ }, [cameraSessionScope, controlsRef]);
21488
21583
  return {
21489
21584
  handleCameraCreated,
21490
21585
  handleControlsChange
@@ -21494,14 +21589,14 @@ var useCameraSession = () => {
21494
21589
  // src/react-three/Canvas.tsx
21495
21590
  import {
21496
21591
  useRef as useRef6,
21497
- useEffect as useEffect15,
21592
+ useEffect as useEffect16,
21498
21593
  useState as useState12,
21499
21594
  useCallback as useCallback7,
21500
21595
  forwardRef,
21501
21596
  useImperativeHandle,
21502
21597
  useMemo as useMemo12
21503
21598
  } from "react";
21504
- import * as THREE18 from "three";
21599
+ import * as THREE19 from "three";
21505
21600
 
21506
21601
  // src/react-three/remove-existing-canvases.ts
21507
21602
  function removeExistingCanvases(container) {
@@ -21509,13 +21604,13 @@ function removeExistingCanvases(container) {
21509
21604
  }
21510
21605
 
21511
21606
  // src/react-three/configure-renderer.ts
21512
- import * as THREE17 from "three";
21607
+ import * as THREE18 from "three";
21513
21608
  var configureRenderer = (renderer) => {
21514
- renderer.outputColorSpace = THREE17.SRGBColorSpace;
21515
- renderer.toneMapping = THREE17.ACESFilmicToneMapping;
21609
+ renderer.outputColorSpace = THREE18.SRGBColorSpace;
21610
+ renderer.toneMapping = THREE18.ACESFilmicToneMapping;
21516
21611
  renderer.toneMappingExposure = 1;
21517
21612
  renderer.shadowMap.enabled = true;
21518
- renderer.shadowMap.type = THREE17.PCFSoftShadowMap;
21613
+ renderer.shadowMap.type = THREE18.PCFSoftShadowMap;
21519
21614
  };
21520
21615
 
21521
21616
  // src/react-three/Canvas.tsx
@@ -21547,16 +21642,16 @@ var Canvas = forwardRef(
21547
21642
  },
21548
21643
  []
21549
21644
  );
21550
- const scene = useMemo12(() => new THREE18.Scene(), []);
21645
+ const scene = useMemo12(() => new THREE19.Scene(), []);
21551
21646
  if (sceneProps?.up) {
21552
21647
  scene.up.set(sceneProps.up.x, sceneProps.up.y, sceneProps.up.z);
21553
21648
  }
21554
- const rootObject = useRef6(new THREE18.Object3D());
21649
+ const rootObject = useRef6(new THREE19.Object3D());
21555
21650
  useImperativeHandle(ref, () => rootObject.current);
21556
- useEffect15(() => {
21651
+ useEffect16(() => {
21557
21652
  if (!mountRef.current) return;
21558
21653
  removeExistingCanvases(mountRef.current);
21559
- const renderer = new THREE18.WebGLRenderer({ antialias: true, alpha: true });
21654
+ const renderer = new THREE19.WebGLRenderer({ antialias: true, alpha: true });
21560
21655
  configureRenderer(renderer);
21561
21656
  renderer.setSize(
21562
21657
  mountRef.current.clientWidth,
@@ -21565,7 +21660,7 @@ var Canvas = forwardRef(
21565
21660
  renderer.setPixelRatio(window.devicePixelRatio);
21566
21661
  mountRef.current.appendChild(renderer.domElement);
21567
21662
  const aspect = mountRef.current.clientWidth / mountRef.current.clientHeight;
21568
- const camera = cameraType === "perspective" ? new THREE18.PerspectiveCamera(75, aspect, 0.1, 1e3) : new THREE18.OrthographicCamera(
21663
+ const camera = cameraType === "perspective" ? new THREE19.PerspectiveCamera(75, aspect, 0.1, 1e3) : new THREE19.OrthographicCamera(
21569
21664
  -10 * aspect,
21570
21665
  10 * aspect,
21571
21666
  10,
@@ -21602,7 +21697,7 @@ var Canvas = forwardRef(
21602
21697
  });
21603
21698
  onCreatedRef.current?.({ camera, renderer });
21604
21699
  let animationFrameId;
21605
- const clock = new THREE18.Clock();
21700
+ const clock = new THREE19.Clock();
21606
21701
  let isVisible = true;
21607
21702
  const visibilityObserver = new IntersectionObserver(([entry]) => {
21608
21703
  isVisible = entry?.isIntersecting ?? false;
@@ -21621,9 +21716,9 @@ var Canvas = forwardRef(
21621
21716
  const handleResize = () => {
21622
21717
  if (mountRef.current?.clientWidth && mountRef.current.clientHeight) {
21623
21718
  const newAspect = mountRef.current.clientWidth / mountRef.current.clientHeight;
21624
- if (camera instanceof THREE18.PerspectiveCamera) {
21719
+ if (camera instanceof THREE19.PerspectiveCamera) {
21625
21720
  camera.aspect = newAspect;
21626
- } else if (camera instanceof THREE18.OrthographicCamera) {
21721
+ } else if (camera instanceof THREE19.OrthographicCamera) {
21627
21722
  camera.left = -10 * newAspect;
21628
21723
  camera.right = 10 * newAspect;
21629
21724
  camera.top = 10;
@@ -21661,8 +21756,8 @@ var Canvas = forwardRef(
21661
21756
  );
21662
21757
 
21663
21758
  // src/react-three/Grid.tsx
21664
- import { useEffect as useEffect16, useMemo as useMemo13 } from "react";
21665
- import * as THREE19 from "three";
21759
+ import { useEffect as useEffect17, useMemo as useMemo13 } from "react";
21760
+ import * as THREE20 from "three";
21666
21761
  var vertexShader = `
21667
21762
  varying vec3 worldPosition;
21668
21763
  void main() {
@@ -21708,24 +21803,24 @@ var Grid = ({
21708
21803
  const { scene, camera } = useThree();
21709
21804
  const size4 = 1e3;
21710
21805
  const gridMesh = useMemo13(() => {
21711
- const geometry = new THREE19.PlaneGeometry(size4, size4);
21806
+ const geometry = new THREE20.PlaneGeometry(size4, size4);
21712
21807
  geometry.rotateX(-Math.PI / 2);
21713
- const material = new THREE19.ShaderMaterial({
21808
+ const material = new THREE20.ShaderMaterial({
21714
21809
  vertexShader,
21715
21810
  fragmentShader,
21716
21811
  uniforms: {
21717
21812
  cellSize: { value: cellSize },
21718
21813
  sectionSize: { value: sectionSize },
21719
- gridColor: { value: new THREE19.Color(15658734) },
21720
- sectionColor: { value: new THREE19.Color(13421823) },
21814
+ gridColor: { value: new THREE20.Color(15658734) },
21815
+ sectionColor: { value: new THREE20.Color(13421823) },
21721
21816
  fadeDistance: { value: 100 },
21722
21817
  // Fade out based on sectionSize
21723
21818
  fadeStrength: { value: 1.5 }
21724
21819
  },
21725
21820
  transparent: true,
21726
- side: THREE19.DoubleSide
21821
+ side: THREE20.DoubleSide
21727
21822
  });
21728
- const mesh = new THREE19.Mesh(geometry, material);
21823
+ const mesh = new THREE20.Mesh(geometry, material);
21729
21824
  if (rotation) {
21730
21825
  mesh.rotation.fromArray(rotation);
21731
21826
  }
@@ -21736,7 +21831,7 @@ var Grid = ({
21736
21831
  gridMesh.position.set(camera.position.x, camera.position.y, 0);
21737
21832
  }
21738
21833
  });
21739
- useEffect16(() => {
21834
+ useEffect17(() => {
21740
21835
  if (!scene || !gridMesh) return;
21741
21836
  scene.add(gridMesh);
21742
21837
  return () => {
@@ -21753,8 +21848,8 @@ var Grid = ({
21753
21848
  };
21754
21849
 
21755
21850
  // src/react-three/Lights.tsx
21756
- import { useEffect as useEffect17, useMemo as useMemo14 } from "react";
21757
- import * as THREE20 from "three";
21851
+ import { useEffect as useEffect18, useMemo as useMemo14 } from "react";
21852
+ import * as THREE21 from "three";
21758
21853
  var UNDERSIDE_LIGHT_FACTOR = 0.75;
21759
21854
  var Lights = ({
21760
21855
  boardDimensions,
@@ -21764,7 +21859,7 @@ var Lights = ({
21764
21859
  }) => {
21765
21860
  const { scene } = useThree();
21766
21861
  const lightRig = useMemo14(() => {
21767
- const rig = new THREE20.Group();
21862
+ const rig = new THREE21.Group();
21768
21863
  rig.name = "cad-viewer-light-rig";
21769
21864
  const centerX = boardCenter?.x ?? 0;
21770
21865
  const centerY = boardCenter?.y ?? 0;
@@ -21776,14 +21871,14 @@ var Lights = ({
21776
21871
  const shadowHalfSize = largestBoardDimension * 0.8;
21777
21872
  const lightDistance = largestBoardDimension;
21778
21873
  const keyLightDistance = largestBoardDimension * 1.7;
21779
- const ambientLight = new THREE20.AmbientLight(16186360, 0.22);
21874
+ const ambientLight = new THREE21.AmbientLight(16186360, 0.22);
21780
21875
  ambientLight.name = "cad-viewer-soft-ambient";
21781
21876
  rig.add(ambientLight);
21782
- const hemisphereLight = new THREE20.HemisphereLight(15004141, 1581597, 0.2);
21877
+ const hemisphereLight = new THREE21.HemisphereLight(15004141, 1581597, 0.2);
21783
21878
  hemisphereLight.name = "cad-viewer-hemisphere";
21784
21879
  rig.add(hemisphereLight);
21785
21880
  const addDirectionalLight = (name, color, intensity, position, castShadow = false) => {
21786
- const light = new THREE20.DirectionalLight(color, intensity);
21881
+ const light = new THREE21.DirectionalLight(color, intensity);
21787
21882
  light.name = name;
21788
21883
  light.position.set(
21789
21884
  centerX + position[0],
@@ -21857,17 +21952,17 @@ var Lights = ({
21857
21952
  );
21858
21953
  return rig;
21859
21954
  }, [boardCenter, boardDimensions, shadowsEnabled]);
21860
- useEffect17(() => {
21955
+ useEffect18(() => {
21861
21956
  const previousBackground = scene.background;
21862
21957
  const previousEnvironment = scene.environment;
21863
- scene.background = darkBackgroundEnabled ? new THREE20.Color(1053456) : null;
21958
+ scene.background = darkBackgroundEnabled ? new THREE21.Color(1053456) : null;
21864
21959
  scene.environment = null;
21865
21960
  return () => {
21866
21961
  scene.background = previousBackground;
21867
21962
  scene.environment = previousEnvironment;
21868
21963
  };
21869
21964
  }, [darkBackgroundEnabled, scene]);
21870
- useEffect17(() => {
21965
+ useEffect18(() => {
21871
21966
  if (!scene) return;
21872
21967
  scene.add(lightRig);
21873
21968
  return () => {
@@ -21878,12 +21973,12 @@ var Lights = ({
21878
21973
  };
21879
21974
 
21880
21975
  // src/react-three/OrbitControls.tsx
21881
- import { useEffect as useEffect18, useMemo as useMemo15 } from "react";
21882
- import * as THREE22 from "three";
21976
+ import { useEffect as useEffect19, useMemo as useMemo15 } from "react";
21977
+ import * as THREE23 from "three";
21883
21978
  import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
21884
21979
 
21885
21980
  // src/react-three/infinite-dolly.ts
21886
- import * as THREE21 from "three";
21981
+ import * as THREE22 from "three";
21887
21982
  var MINIMUM_DOLLY_DISTANCE_RATIO = 0.05;
21888
21983
  var getMinimumDollyDistance = ({
21889
21984
  camera,
@@ -21897,12 +21992,12 @@ var applyInfiniteDolly = ({
21897
21992
  controls,
21898
21993
  minimumDollyDistance
21899
21994
  }) => {
21900
- if (!(camera instanceof THREE21.PerspectiveCamera)) return false;
21995
+ if (!(camera instanceof THREE22.PerspectiveCamera)) return false;
21901
21996
  const orbitDistance = controls.getDistance();
21902
21997
  if (Number.isFinite(orbitDistance) && orbitDistance >= minimumDollyDistance) {
21903
21998
  return false;
21904
21999
  }
21905
- const viewDirection = new THREE21.Vector3();
22000
+ const viewDirection = new THREE22.Vector3();
21906
22001
  camera.getWorldDirection(viewDirection);
21907
22002
  controls.target.copy(camera.position).addScaledVector(viewDirection, minimumDollyDistance);
21908
22003
  return true;
@@ -21927,7 +22022,7 @@ var OrbitControls = ({
21927
22022
  return new ThreeOrbitControls(camera, renderer.domElement);
21928
22023
  }, [camera, renderer]);
21929
22024
  const minimumDollyDistance = useMemo15(() => {
21930
- if (!controls || !(camera instanceof THREE22.PerspectiveCamera)) {
22025
+ if (!controls || !(camera instanceof THREE23.PerspectiveCamera)) {
21931
22026
  return Number.EPSILON;
21932
22027
  }
21933
22028
  const initialOrbitDistance = target ? Math.hypot(
@@ -21937,11 +22032,11 @@ var OrbitControls = ({
21937
22032
  ) : controls.getDistance();
21938
22033
  return getMinimumDollyDistance({ camera, initialOrbitDistance });
21939
22034
  }, [camera, controls, target]);
21940
- useEffect18(() => {
22035
+ useEffect19(() => {
21941
22036
  onControlsChange?.(controls ?? null);
21942
22037
  return () => onControlsChange?.(null);
21943
22038
  }, [controls, onControlsChange]);
21944
- useEffect18(() => {
22039
+ useEffect19(() => {
21945
22040
  if (!controls) return;
21946
22041
  const handleChange = () => {
21947
22042
  onControlsChange?.(controls);
@@ -21951,7 +22046,7 @@ var OrbitControls = ({
21951
22046
  controls.removeEventListener("change", handleChange);
21952
22047
  };
21953
22048
  }, [controls, onControlsChange]);
21954
- useEffect18(() => {
22049
+ useEffect19(() => {
21955
22050
  if (!controls) return;
21956
22051
  controls.autoRotate = autoRotate || false;
21957
22052
  controls.autoRotateSpeed = autoRotateSpeed || 1;
@@ -21962,9 +22057,9 @@ var OrbitControls = ({
21962
22057
  if (dampingFactor !== void 0) controls.dampingFactor = dampingFactor;
21963
22058
  controls.zoomToCursor = true;
21964
22059
  controls.mouseButtons = {
21965
- LEFT: THREE22.MOUSE.ROTATE,
22060
+ LEFT: THREE23.MOUSE.ROTATE,
21966
22061
  // Left click to rotate
21967
- MIDDLE: THREE22.MOUSE.PAN,
22062
+ MIDDLE: THREE23.MOUSE.PAN,
21968
22063
  // Middle click to pan
21969
22064
  RIGHT: null
21970
22065
  // Right-click always disabled - only context menu
@@ -21984,14 +22079,14 @@ var OrbitControls = ({
21984
22079
  dampingFactor,
21985
22080
  target
21986
22081
  ]);
21987
- useEffect18(() => {
22082
+ useEffect19(() => {
21988
22083
  if (!controls || !onStart) return;
21989
22084
  controls.addEventListener("start", onStart);
21990
22085
  return () => {
21991
22086
  controls.removeEventListener("start", onStart);
21992
22087
  };
21993
22088
  }, [controls, onStart]);
21994
- useEffect18(() => {
22089
+ useEffect19(() => {
21995
22090
  if (!controls) return;
21996
22091
  return () => {
21997
22092
  controls.dispose();
@@ -22087,15 +22182,15 @@ var getComparisonBounds = (type, boardDimensions, boardCenter) => {
22087
22182
  };
22088
22183
 
22089
22184
  // src/three-components/OrientationCubeCanvas.tsx
22090
- import { useEffect as useEffect19, useRef as useRef7 } from "react";
22091
- import * as THREE23 from "three";
22185
+ import { useEffect as useEffect20, useRef as useRef7 } from "react";
22186
+ import * as THREE24 from "three";
22092
22187
  import { Text as TroikaText } from "troika-three-text";
22093
22188
  import { jsx as jsx14 } from "react/jsx-runtime";
22094
22189
  function computePointInFront(rotationVector, distance5) {
22095
- const quaternion = new THREE23.Quaternion().setFromEuler(
22096
- new THREE23.Euler(rotationVector.x, rotationVector.y, rotationVector.z)
22190
+ const quaternion = new THREE24.Quaternion().setFromEuler(
22191
+ new THREE24.Euler(rotationVector.x, rotationVector.y, rotationVector.z)
22097
22192
  );
22098
- const forwardVector = new THREE23.Vector3(0, 0, 1);
22193
+ const forwardVector = new THREE24.Vector3(0, 0, 1);
22099
22194
  forwardVector.applyQuaternion(quaternion);
22100
22195
  const result = forwardVector.multiplyScalar(distance5);
22101
22196
  return result;
@@ -22103,36 +22198,36 @@ function computePointInFront(rotationVector, distance5) {
22103
22198
  var OrientationCubeCanvas = () => {
22104
22199
  const { mainCameraRef } = useCameraController();
22105
22200
  const containerRef = useRef7(null);
22106
- useEffect19(() => {
22201
+ useEffect20(() => {
22107
22202
  if (!containerRef.current) return;
22108
22203
  const container = containerRef.current;
22109
22204
  const canvas = document.createElement("canvas");
22110
22205
  container.appendChild(canvas);
22111
- const renderer = new THREE23.WebGLRenderer({
22206
+ const renderer = new THREE24.WebGLRenderer({
22112
22207
  canvas,
22113
22208
  antialias: true,
22114
22209
  alpha: true
22115
22210
  });
22116
22211
  renderer.setSize(120, 120);
22117
22212
  renderer.setPixelRatio(window.devicePixelRatio);
22118
- const scene = new THREE23.Scene();
22119
- const camera = new THREE23.PerspectiveCamera(75, 1, 0.1, 1e3);
22213
+ const scene = new THREE24.Scene();
22214
+ const camera = new THREE24.PerspectiveCamera(75, 1, 0.1, 1e3);
22120
22215
  camera.up.set(0, 0, 1);
22121
- const ambientLight = new THREE23.AmbientLight(16777215, Math.PI / 2);
22216
+ const ambientLight = new THREE24.AmbientLight(16777215, Math.PI / 2);
22122
22217
  scene.add(ambientLight);
22123
- const group = new THREE23.Group();
22218
+ const group = new THREE24.Group();
22124
22219
  group.rotation.fromArray([Math.PI / 2, 0, 0]);
22125
22220
  const cubeSize = 1;
22126
- const box = new THREE23.Mesh(
22127
- new THREE23.BoxGeometry(cubeSize, cubeSize, cubeSize),
22128
- new THREE23.MeshStandardMaterial({ color: "white" })
22221
+ const box = new THREE24.Mesh(
22222
+ new THREE24.BoxGeometry(cubeSize, cubeSize, cubeSize),
22223
+ new THREE24.MeshStandardMaterial({ color: "white" })
22129
22224
  );
22130
22225
  group.add(box);
22131
- const edges = new THREE23.LineSegments(
22132
- new THREE23.EdgesGeometry(
22133
- new THREE23.BoxGeometry(cubeSize, cubeSize, cubeSize)
22226
+ const edges = new THREE24.LineSegments(
22227
+ new THREE24.EdgesGeometry(
22228
+ new THREE24.BoxGeometry(cubeSize, cubeSize, cubeSize)
22134
22229
  ),
22135
- new THREE23.LineBasicMaterial({ color: 0, linewidth: 2 })
22230
+ new THREE24.LineBasicMaterial({ color: 0, linewidth: 2 })
22136
22231
  );
22137
22232
  group.add(edges);
22138
22233
  scene.add(group);
@@ -22194,7 +22289,7 @@ var OrientationCubeCanvas = () => {
22194
22289
  if (!isVisible) return;
22195
22290
  if (mainCameraRef.current) {
22196
22291
  const cameraPosition = computePointInFront(
22197
- mainCameraRef.current.rotation ?? new THREE23.Euler(0, 0, 0),
22292
+ mainCameraRef.current.rotation ?? new THREE24.Euler(0, 0, 0),
22198
22293
  2
22199
22294
  );
22200
22295
  if (!cameraPosition.equals(camera.position)) {
@@ -22243,15 +22338,15 @@ var OrientationCubeCanvas = () => {
22243
22338
  };
22244
22339
 
22245
22340
  // src/three-components/reference-object.tsx
22246
- import { useEffect as useEffect20, useMemo as useMemo16 } from "react";
22341
+ import { useEffect as useEffect21, useMemo as useMemo16 } from "react";
22247
22342
 
22248
22343
  // src/reference-objects/create-reference-object.ts
22249
- import * as THREE24 from "three";
22344
+ import * as THREE25 from "three";
22250
22345
  var createRoundedRectangleShape = (width, height, radius) => {
22251
22346
  const halfWidth = width / 2;
22252
22347
  const halfHeight = height / 2;
22253
22348
  const safeRadius = Math.min(radius, halfWidth, halfHeight);
22254
- const shape = new THREE24.Shape();
22349
+ const shape = new THREE25.Shape();
22255
22350
  shape.moveTo(-halfWidth + safeRadius, -halfHeight);
22256
22351
  shape.lineTo(halfWidth - safeRadius, -halfHeight);
22257
22352
  shape.quadraticCurveTo(
@@ -22290,7 +22385,7 @@ var createRoundedPrismGeometry = (width, height, depth, radius) => {
22290
22385
  );
22291
22386
  };
22292
22387
  var createPrismGeometryFromShape = (shape, depth) => {
22293
- const geometry = new THREE24.ExtrudeGeometry(shape, {
22388
+ const geometry = new THREE25.ExtrudeGeometry(shape, {
22294
22389
  depth,
22295
22390
  bevelEnabled: false,
22296
22391
  curveSegments: 24
@@ -22302,16 +22397,16 @@ var createPrismGeometryFromShape = (shape, depth) => {
22302
22397
  var normalizeObjectToSpec = (object, type) => {
22303
22398
  const spec = REFERENCE_OBJECT_SPECS[type];
22304
22399
  object.updateMatrixWorld(true);
22305
- const initialBounds = new THREE24.Box3().setFromObject(object);
22306
- const initialSize = initialBounds.getSize(new THREE24.Vector3());
22400
+ const initialBounds = new THREE25.Box3().setFromObject(object);
22401
+ const initialSize = initialBounds.getSize(new THREE25.Vector3());
22307
22402
  object.scale.set(
22308
22403
  spec.width / initialSize.x,
22309
22404
  spec.height / initialSize.y,
22310
22405
  spec.depth / initialSize.z
22311
22406
  );
22312
22407
  object.updateMatrixWorld(true);
22313
- const scaledBounds = new THREE24.Box3().setFromObject(object);
22314
- const scaledCenter = scaledBounds.getCenter(new THREE24.Vector3());
22408
+ const scaledBounds = new THREE25.Box3().setFromObject(object);
22409
+ const scaledCenter = scaledBounds.getCenter(new THREE25.Vector3());
22315
22410
  object.position.set(
22316
22411
  -scaledCenter.x,
22317
22412
  -scaledCenter.y,
@@ -22320,15 +22415,15 @@ var normalizeObjectToSpec = (object, type) => {
22320
22415
  object.updateMatrixWorld(true);
22321
22416
  };
22322
22417
  var createBananaGeometry = () => {
22323
- const curve = new THREE24.CatmullRomCurve3([
22324
- new THREE24.Vector3(-82, 18, 16),
22325
- new THREE24.Vector3(-67, 7, 15),
22326
- new THREE24.Vector3(-43, -9, 14),
22327
- new THREE24.Vector3(-15, -18, 14),
22328
- new THREE24.Vector3(16, -18, 14),
22329
- new THREE24.Vector3(47, -8, 14.5),
22330
- new THREE24.Vector3(72, 8, 16),
22331
- new THREE24.Vector3(84, 19, 17)
22418
+ const curve = new THREE25.CatmullRomCurve3([
22419
+ new THREE25.Vector3(-82, 18, 16),
22420
+ new THREE25.Vector3(-67, 7, 15),
22421
+ new THREE25.Vector3(-43, -9, 14),
22422
+ new THREE25.Vector3(-15, -18, 14),
22423
+ new THREE25.Vector3(16, -18, 14),
22424
+ new THREE25.Vector3(47, -8, 14.5),
22425
+ new THREE25.Vector3(72, 8, 16),
22426
+ new THREE25.Vector3(84, 19, 17)
22332
22427
  ]);
22333
22428
  const tubularSegments = 84;
22334
22429
  const radialSegments = 30;
@@ -22338,8 +22433,8 @@ var createBananaGeometry = () => {
22338
22433
  const colors2 = [];
22339
22434
  const uvs = [];
22340
22435
  const indices = [];
22341
- const ripeYellow = new THREE24.Color(16766023);
22342
- const stemGreen = new THREE24.Color(12038965);
22436
+ const ripeYellow = new THREE25.Color(16766023);
22437
+ const stemGreen = new THREE25.Color(12038965);
22343
22438
  for (let segment = 0; segment <= tubularSegments; segment++) {
22344
22439
  const t = segment / tubularSegments;
22345
22440
  const point = curve.getPointAt(t);
@@ -22354,7 +22449,7 @@ var createBananaGeometry = () => {
22354
22449
  const sin = Math.sin(angle3);
22355
22450
  const cos = Math.cos(angle3);
22356
22451
  const ridgeScale = 1 + Math.cos(angle3 * 5 + 0.35) * 0.035;
22357
- const radialNormal = new THREE24.Vector3().addScaledVector(normal, cos).addScaledVector(binormal, sin * 0.9).normalize();
22452
+ const radialNormal = new THREE25.Vector3().addScaledVector(normal, cos).addScaledVector(binormal, sin * 0.9).normalize();
22358
22453
  const vertex = point.clone().addScaledVector(normal, cos * radius * ridgeScale).addScaledVector(binormal, sin * radius * 0.9 * ridgeScale);
22359
22454
  positions.push(vertex.x, vertex.y, vertex.z);
22360
22455
  normals.push(radialNormal.x, radialNormal.y, radialNormal.z);
@@ -22372,26 +22467,26 @@ var createBananaGeometry = () => {
22372
22467
  indices.push(a, d, b, b, d, c);
22373
22468
  }
22374
22469
  }
22375
- const geometry = new THREE24.BufferGeometry();
22470
+ const geometry = new THREE25.BufferGeometry();
22376
22471
  geometry.setIndex(indices);
22377
22472
  geometry.setAttribute(
22378
22473
  "position",
22379
- new THREE24.Float32BufferAttribute(positions, 3)
22474
+ new THREE25.Float32BufferAttribute(positions, 3)
22380
22475
  );
22381
- geometry.setAttribute("normal", new THREE24.Float32BufferAttribute(normals, 3));
22382
- geometry.setAttribute("color", new THREE24.Float32BufferAttribute(colors2, 3));
22383
- geometry.setAttribute("uv", new THREE24.Float32BufferAttribute(uvs, 2));
22476
+ geometry.setAttribute("normal", new THREE25.Float32BufferAttribute(normals, 3));
22477
+ geometry.setAttribute("color", new THREE25.Float32BufferAttribute(colors2, 3));
22478
+ geometry.setAttribute("uv", new THREE25.Float32BufferAttribute(uvs, 2));
22384
22479
  geometry.computeBoundingBox();
22385
22480
  geometry.computeBoundingSphere();
22386
22481
  return { geometry, curve };
22387
22482
  };
22388
22483
  var createBanana = () => {
22389
- const group = new THREE24.Group();
22390
- const banana = new THREE24.Group();
22484
+ const group = new THREE25.Group();
22485
+ const banana = new THREE25.Group();
22391
22486
  const { geometry, curve } = createBananaGeometry();
22392
- const peel = new THREE24.Mesh(
22487
+ const peel = new THREE25.Mesh(
22393
22488
  geometry,
22394
- new THREE24.MeshStandardMaterial({
22489
+ new THREE25.MeshStandardMaterial({
22395
22490
  color: 16777215,
22396
22491
  roughness: 0.72,
22397
22492
  metalness: 0,
@@ -22400,9 +22495,9 @@ var createBanana = () => {
22400
22495
  );
22401
22496
  peel.name = "banana-peel";
22402
22497
  banana.add(peel);
22403
- const blossomTip = new THREE24.Mesh(
22404
- new THREE24.SphereGeometry(3.4, 20, 12),
22405
- new THREE24.MeshStandardMaterial({
22498
+ const blossomTip = new THREE25.Mesh(
22499
+ new THREE25.SphereGeometry(3.4, 20, 12),
22500
+ new THREE25.MeshStandardMaterial({
22406
22501
  color: 6242844,
22407
22502
  roughness: 0.92
22408
22503
  })
@@ -22413,20 +22508,20 @@ var createBanana = () => {
22413
22508
  banana.add(blossomTip);
22414
22509
  const stemLength = 11;
22415
22510
  const stemTangent = curve.getTangentAt(1).normalize();
22416
- const stem = new THREE24.Mesh(
22417
- new THREE24.CylinderGeometry(2.5, 4.1, stemLength, 18),
22418
- new THREE24.MeshStandardMaterial({
22511
+ const stem = new THREE25.Mesh(
22512
+ new THREE25.CylinderGeometry(2.5, 4.1, stemLength, 18),
22513
+ new THREE25.MeshStandardMaterial({
22419
22514
  color: 8483625,
22420
22515
  roughness: 0.82
22421
22516
  })
22422
22517
  );
22423
- stem.quaternion.setFromUnitVectors(new THREE24.Vector3(0, 1, 0), stemTangent);
22518
+ stem.quaternion.setFromUnitVectors(new THREE25.Vector3(0, 1, 0), stemTangent);
22424
22519
  stem.position.copy(curve.getPointAt(1)).addScaledVector(stemTangent, stemLength / 2);
22425
22520
  stem.name = "banana-stem";
22426
22521
  banana.add(stem);
22427
- const stemTip = new THREE24.Mesh(
22428
- new THREE24.SphereGeometry(2.55, 18, 10),
22429
- new THREE24.MeshStandardMaterial({
22522
+ const stemTip = new THREE25.Mesh(
22523
+ new THREE25.SphereGeometry(2.55, 18, 10),
22524
+ new THREE25.MeshStandardMaterial({
22430
22525
  color: 5981470,
22431
22526
  roughness: 0.9
22432
22527
  })
@@ -22440,7 +22535,7 @@ var createBanana = () => {
22440
22535
  };
22441
22536
  var createMacbookLidGeometry = (width, height, depth, radius, indentRadius) => {
22442
22537
  const lidShape = createRoundedRectangleShape(width, height, radius);
22443
- const indent = new THREE24.Path();
22538
+ const indent = new THREE25.Path();
22444
22539
  indent.absarc(0, 0, indentRadius, 0, Math.PI * 2, true);
22445
22540
  lidShape.holes.push(indent);
22446
22541
  return createPrismGeometryFromShape(lidShape, depth);
@@ -22448,9 +22543,9 @@ var createMacbookLidGeometry = (width, height, depth, radius, indentRadius) => {
22448
22543
  var createMacbookIndent = (topZ, radius) => {
22449
22544
  const indentDepth = 0.35;
22450
22545
  const diskDepth = 0.25;
22451
- const disk = new THREE24.Mesh(
22452
- new THREE24.CylinderGeometry(radius - 0.15, radius - 0.15, diskDepth, 48),
22453
- new THREE24.MeshStandardMaterial({
22546
+ const disk = new THREE25.Mesh(
22547
+ new THREE25.CylinderGeometry(radius - 0.15, radius - 0.15, diskDepth, 48),
22548
+ new THREE25.MeshStandardMaterial({
22454
22549
  color: 10199202,
22455
22550
  roughness: 0.48,
22456
22551
  metalness: 0.28
@@ -22463,10 +22558,10 @@ var createMacbookIndent = (topZ, radius) => {
22463
22558
  };
22464
22559
  var createCreditCard = () => {
22465
22560
  const spec = REFERENCE_OBJECT_SPECS["credit-card"];
22466
- const group = new THREE24.Group();
22467
- const body = new THREE24.Mesh(
22561
+ const group = new THREE25.Group();
22562
+ const body = new THREE25.Mesh(
22468
22563
  createRoundedPrismGeometry(spec.width, spec.height, spec.depth, 3.18),
22469
- new THREE24.MeshStandardMaterial({
22564
+ new THREE25.MeshStandardMaterial({
22470
22565
  color: 2383272,
22471
22566
  roughness: 0.38,
22472
22567
  metalness: 0.08
@@ -22475,9 +22570,9 @@ var createCreditCard = () => {
22475
22570
  body.name = "credit-card-body";
22476
22571
  group.add(body);
22477
22572
  const surfaceZ = spec.depth / 2 + 0.08;
22478
- const chip = new THREE24.Mesh(
22573
+ const chip = new THREE25.Mesh(
22479
22574
  createRoundedPrismGeometry(12, 9, 0.14, 1.2),
22480
- new THREE24.MeshStandardMaterial({
22575
+ new THREE25.MeshStandardMaterial({
22481
22576
  color: 14003525,
22482
22577
  roughness: 0.3,
22483
22578
  metalness: 0.62
@@ -22486,14 +22581,14 @@ var createCreditCard = () => {
22486
22581
  chip.position.set(-21, 3, surfaceZ);
22487
22582
  chip.name = "credit-card-chip";
22488
22583
  group.add(chip);
22489
- const stripeMaterial = new THREE24.MeshStandardMaterial({
22584
+ const stripeMaterial = new THREE25.MeshStandardMaterial({
22490
22585
  color: 15331061,
22491
22586
  roughness: 0.45,
22492
22587
  metalness: 0.05
22493
22588
  });
22494
22589
  for (let index2 = 0; index2 < 4; index2++) {
22495
- const stripe = new THREE24.Mesh(
22496
- new THREE24.BoxGeometry(index2 === 3 ? 8 : 12, 0.8, 0.1),
22590
+ const stripe = new THREE25.Mesh(
22591
+ new THREE25.BoxGeometry(index2 === 3 ? 8 : 12, 0.8, 0.1),
22497
22592
  stripeMaterial
22498
22593
  );
22499
22594
  stripe.position.set(-20 + index2 * 14, -14, surfaceZ + 0.08);
@@ -22504,13 +22599,13 @@ var createCreditCard = () => {
22504
22599
  };
22505
22600
  var createMacbook = () => {
22506
22601
  const spec = REFERENCE_OBJECT_SPECS["14in-macbook"];
22507
- const group = new THREE24.Group();
22508
- const aluminumMaterial = new THREE24.MeshStandardMaterial({
22602
+ const group = new THREE25.Group();
22603
+ const aluminumMaterial = new THREE25.MeshStandardMaterial({
22509
22604
  color: 12896201,
22510
22605
  roughness: 0.3,
22511
22606
  metalness: 0.38
22512
22607
  });
22513
- const body = new THREE24.Mesh(
22608
+ const body = new THREE25.Mesh(
22514
22609
  createRoundedPrismGeometry(spec.width, spec.height, 13.5, 9),
22515
22610
  aluminumMaterial
22516
22611
  );
@@ -22521,7 +22616,7 @@ var createMacbook = () => {
22521
22616
  const lidCenterZ = 6.75;
22522
22617
  const lidTopZ = lidCenterZ + lidDepth / 2;
22523
22618
  const indentRadius = 12;
22524
- const lid = new THREE24.Mesh(
22619
+ const lid = new THREE25.Mesh(
22525
22620
  createMacbookLidGeometry(
22526
22621
  spec.width - 1.8,
22527
22622
  spec.height - 1.8,
@@ -22534,9 +22629,9 @@ var createMacbook = () => {
22534
22629
  lid.position.z = lidCenterZ;
22535
22630
  lid.name = "macbook-lid";
22536
22631
  group.add(lid);
22537
- const hinge = new THREE24.Mesh(
22538
- new THREE24.BoxGeometry(spec.width - 42, 3.2, 1.2),
22539
- new THREE24.MeshStandardMaterial({
22632
+ const hinge = new THREE25.Mesh(
22633
+ new THREE25.BoxGeometry(spec.width - 42, 3.2, 1.2),
22634
+ new THREE25.MeshStandardMaterial({
22540
22635
  color: 3158838,
22541
22636
  roughness: 0.55,
22542
22637
  metalness: 0.45
@@ -22546,9 +22641,9 @@ var createMacbook = () => {
22546
22641
  hinge.name = "macbook-hinge";
22547
22642
  group.add(hinge);
22548
22643
  group.add(createMacbookIndent(lidTopZ, indentRadius));
22549
- const frontNotch = new THREE24.Mesh(
22550
- new THREE24.BoxGeometry(54, 1.2, 0.8),
22551
- new THREE24.MeshStandardMaterial({
22644
+ const frontNotch = new THREE25.Mesh(
22645
+ new THREE25.BoxGeometry(54, 1.2, 0.8),
22646
+ new THREE25.MeshStandardMaterial({
22552
22647
  color: 6712174,
22553
22648
  roughness: 0.45,
22554
22649
  metalness: 0.5
@@ -22571,7 +22666,7 @@ var disposeReferenceObject = (object) => {
22571
22666
  const geometries = /* @__PURE__ */ new Set();
22572
22667
  const materials = /* @__PURE__ */ new Set();
22573
22668
  object.traverse((child) => {
22574
- if (!(child instanceof THREE24.Mesh)) return;
22669
+ if (!(child instanceof THREE25.Mesh)) return;
22575
22670
  geometries.add(child.geometry);
22576
22671
  const childMaterials = Array.isArray(child.material) ? child.material : [child.material];
22577
22672
  for (const material of childMaterials) materials.add(material);
@@ -22581,28 +22676,28 @@ var disposeReferenceObject = (object) => {
22581
22676
  };
22582
22677
 
22583
22678
  // src/reference-objects/fit-camera-to-bounds.ts
22584
- import * as THREE25 from "three";
22679
+ import * as THREE26 from "three";
22585
22680
  var fitCameraToBounds = (camera, controls, bounds, padding = 1.18) => {
22586
22681
  const width = Math.max(bounds.maxX - bounds.minX, 0);
22587
22682
  const height = Math.max(bounds.maxY - bounds.minY, 0);
22588
22683
  const depth = Math.max(bounds.maxZ - bounds.minZ, 0);
22589
- const center = new THREE25.Vector3(
22684
+ const center = new THREE26.Vector3(
22590
22685
  (bounds.minX + bounds.maxX) / 2,
22591
22686
  (bounds.minY + bounds.maxY) / 2,
22592
22687
  (bounds.minZ + bounds.maxZ) / 2
22593
22688
  );
22594
22689
  const radius = Math.max(Math.hypot(width, height, depth) / 2, 1);
22595
- const previousTarget = controls?.target ?? new THREE25.Vector3();
22596
- const direction = new THREE25.Vector3(0, -0.75, 0.9).normalize();
22690
+ const previousTarget = controls?.target ?? new THREE26.Vector3();
22691
+ const direction = new THREE26.Vector3(0, -0.75, 0.9).normalize();
22597
22692
  let distance5 = Math.max(camera.position.distanceTo(previousTarget), 5);
22598
- if (camera instanceof THREE25.PerspectiveCamera) {
22599
- const verticalFov = THREE25.MathUtils.degToRad(camera.fov);
22693
+ if (camera instanceof THREE26.PerspectiveCamera) {
22694
+ const verticalFov = THREE26.MathUtils.degToRad(camera.fov);
22600
22695
  const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * camera.aspect);
22601
22696
  const limitingFov = Math.min(verticalFov, horizontalFov);
22602
22697
  distance5 = radius * padding / Math.sin(limitingFov / 2);
22603
22698
  camera.far = Math.max(camera.far, distance5 + radius * padding * 3);
22604
22699
  camera.updateProjectionMatrix();
22605
- } else if (camera instanceof THREE25.OrthographicCamera) {
22700
+ } else if (camera instanceof THREE26.OrthographicCamera) {
22606
22701
  const halfViewWidth = Math.abs(camera.right - camera.left) / 2;
22607
22702
  const halfViewHeight = Math.abs(camera.top - camera.bottom) / 2;
22608
22703
  camera.zoom = Math.min(
@@ -22639,7 +22734,7 @@ var ReferenceObject = ({
22639
22734
  nextObject.position.set(position.x, position.y, position.z);
22640
22735
  return nextObject;
22641
22736
  }, [type, boardDimensions, boardCenter]);
22642
- useEffect20(() => {
22737
+ useEffect21(() => {
22643
22738
  scene.add(object);
22644
22739
  return () => {
22645
22740
  scene.remove(object);
@@ -22651,7 +22746,7 @@ var ReferenceObject = ({
22651
22746
  var FitCameraToComparison = ({ bounds }) => {
22652
22747
  const { camera } = useThree();
22653
22748
  const { controlsRef } = useCameraController();
22654
- useEffect20(() => {
22749
+ useEffect21(() => {
22655
22750
  fitCameraToBounds(camera, controlsRef.current, bounds);
22656
22751
  }, [bounds, camera, controlsRef]);
22657
22752
  return null;
@@ -22677,6 +22772,7 @@ var CadViewerContainer = forwardRef2(
22677
22772
  clickToInteractEnabled = false,
22678
22773
  boardDimensions,
22679
22774
  boardCenter,
22775
+ cameraSessionScope,
22680
22776
  referenceObject,
22681
22777
  onUserInteraction,
22682
22778
  onCameraControllerReady
@@ -22689,8 +22785,8 @@ var CadViewerContainer = forwardRef2(
22689
22785
  const {
22690
22786
  handleCameraCreated,
22691
22787
  handleControlsChange: handleSessionControlsChange
22692
- } = useCameraSession();
22693
- useEffect21(() => {
22788
+ } = useCameraSession({ cameraSessionScope });
22789
+ useEffect22(() => {
22694
22790
  if (onCameraControllerReady) {
22695
22791
  onCameraControllerReady(controller);
22696
22792
  }
@@ -22722,7 +22818,7 @@ var CadViewerContainer = forwardRef2(
22722
22818
  Canvas,
22723
22819
  {
22724
22820
  ref,
22725
- scene: { up: new THREE26.Vector3(0, 0, 1) },
22821
+ scene: { up: new THREE27.Vector3(0, 0, 1) },
22726
22822
  camera: { up: [0, 0, 1], position: initialCameraPosition },
22727
22823
  onCreated: ({ camera }) => {
22728
22824
  mainCameraRef.current = camera;
@@ -22779,7 +22875,8 @@ var CadViewerContainer = forwardRef2(
22779
22875
  ),
22780
22876
  children
22781
22877
  ]
22782
- }
22878
+ },
22879
+ cameraSessionScope
22783
22880
  ),
22784
22881
  /* @__PURE__ */ jsxs2(
22785
22882
  "div",
@@ -22840,12 +22937,12 @@ var CadViewerContainer = forwardRef2(
22840
22937
  );
22841
22938
 
22842
22939
  // src/hooks/use-stls-from-geom.ts
22843
- import { useState as useState14, useEffect as useEffect22 } from "react";
22940
+ import { useState as useState14, useEffect as useEffect23 } from "react";
22844
22941
  import stlSerializer from "@jscad/stl-serializer";
22845
22942
  var useStlsFromGeom = (geom) => {
22846
22943
  const [stls, setStls] = useState14([]);
22847
22944
  const [loading, setLoading] = useState14(true);
22848
- useEffect22(() => {
22945
+ useEffect23(() => {
22849
22946
  if (!geom) return;
22850
22947
  const generateStls = async () => {
22851
22948
  setLoading(true);
@@ -22874,7 +22971,7 @@ var useStlsFromGeom = (geom) => {
22874
22971
  };
22875
22972
 
22876
22973
  // src/hooks/useBoardGeomBuilder.ts
22877
- import { useState as useState15, useEffect as useEffect23, useRef as useRef8 } from "react";
22974
+ import { useState as useState15, useEffect as useEffect24, useRef as useRef8 } from "react";
22878
22975
 
22879
22976
  // src/soup-to-3d/index.ts
22880
22977
  var import_colors = __toESM(require_colors(), 1);
@@ -22913,7 +23010,7 @@ var createBoardGeomWithOutline = (board, depth = 1.2, options = {}) => {
22913
23010
  };
22914
23011
 
22915
23012
  // src/utils/get-board-edge-color.ts
22916
- import * as THREE28 from "three";
23013
+ import * as THREE29 from "three";
22917
23014
 
22918
23015
  // src/geoms/constants.ts
22919
23016
  var M = 0.01;
@@ -22946,7 +23043,7 @@ var soldermaskColors = {
22946
23043
  };
22947
23044
 
22948
23045
  // src/utils/soldermask-color.ts
22949
- import * as THREE27 from "three";
23046
+ import * as THREE28 from "three";
22950
23047
  var SOLDERMASK_PRESET_HEX = {
22951
23048
  red: "#650202",
22952
23049
  blue: "#003f7d",
@@ -22977,9 +23074,9 @@ var resolveSoldermaskColor = (requestedColor) => {
22977
23074
  }
22978
23075
  const presetName = lowercaseColor;
22979
23076
  const presetColor = SOLDERMASK_PRESET_HEX[presetName];
22980
- if (presetColor) return new THREE27.Color(presetColor);
22981
- const isCssColor = Object.hasOwn(THREE27.Color.NAMES, lowercaseColor) || CSS_HEX_COLOR_PATTERN.test(normalizedColor) || CSS_RGB_INTEGER_PATTERN.test(normalizedColor) || CSS_RGB_PERCENT_PATTERN.test(normalizedColor) || CSS_HSL_PATTERN.test(normalizedColor);
22982
- return isCssColor ? new THREE27.Color(lowercaseColor) : null;
23077
+ if (presetColor) return new THREE28.Color(presetColor);
23078
+ const isCssColor = Object.hasOwn(THREE28.Color.NAMES, lowercaseColor) || CSS_HEX_COLOR_PATTERN.test(normalizedColor) || CSS_RGB_INTEGER_PATTERN.test(normalizedColor) || CSS_RGB_PERCENT_PATTERN.test(normalizedColor) || CSS_HSL_PATTERN.test(normalizedColor);
23079
+ return isCssColor ? new THREE28.Color(lowercaseColor) : null;
22983
23080
  };
22984
23081
  var resolveSoldermaskOpacity = (requestedColor) => {
22985
23082
  const presetName = requestedColor?.trim().toLowerCase();
@@ -23006,7 +23103,7 @@ var soldermaskColorToCss = (color) => {
23006
23103
  };
23007
23104
 
23008
23105
  // src/utils/get-board-edge-color.ts
23009
- var LEGACY_FR4_EDGE_COLOR = new THREE28.Color(1063462);
23106
+ var LEGACY_FR4_EDGE_COLOR = new THREE29.Color(1063462);
23010
23107
  var getBoardEdgeColor = ({
23011
23108
  material,
23012
23109
  solder_mask_color
@@ -23015,7 +23112,7 @@ var getBoardEdgeColor = ({
23015
23112
  if (soldermaskColor) return soldermaskColor;
23016
23113
  if (material === "fr4") return LEGACY_FR4_EDGE_COLOR.clone();
23017
23114
  const materialColor = boardMaterialColors[material] ?? colors.fr4Tan;
23018
- return new THREE28.Color(materialColor[0], materialColor[1], materialColor[2]);
23115
+ return new THREE29.Color(materialColor[0], materialColor[1], materialColor[2]);
23019
23116
  };
23020
23117
 
23021
23118
  // src/soup-to-3d/index.ts
@@ -24363,7 +24460,7 @@ var BoardGeomBuilder = class {
24363
24460
  var useBoardGeomBuilder = (circuitJson) => {
24364
24461
  const [boardGeom, setBoardGeom] = useState15(null);
24365
24462
  const isProcessingRef = useRef8(false);
24366
- useEffect23(() => {
24463
+ useEffect24(() => {
24367
24464
  let isCancelled = false;
24368
24465
  if (!circuitJson) {
24369
24466
  setBoardGeom(null);
@@ -24409,10 +24506,10 @@ var useBoardGeomBuilder = (circuitJson) => {
24409
24506
  };
24410
24507
 
24411
24508
  // src/three-components/Error3d.tsx
24412
- import { useState as useState16, useCallback as useCallback8, useEffect as useEffect25, useMemo as useMemo19 } from "react";
24509
+ import { useState as useState16, useCallback as useCallback8, useEffect as useEffect26, useMemo as useMemo19 } from "react";
24413
24510
 
24414
24511
  // src/react-three/Text.tsx
24415
- import { useEffect as useEffect24, useMemo as useMemo18 } from "react";
24512
+ import { useEffect as useEffect25, useMemo as useMemo18 } from "react";
24416
24513
  import { Text as TroikaText2 } from "troika-three-text";
24417
24514
  var Text = ({
24418
24515
  children,
@@ -24452,7 +24549,7 @@ var Text = ({
24452
24549
  anchorY,
24453
24550
  depthOffset
24454
24551
  ]);
24455
- useEffect24(() => {
24552
+ useEffect25(() => {
24456
24553
  const parentObject = parent || rootObject;
24457
24554
  if (!parentObject || !mesh) return;
24458
24555
  parentObject.add(mesh);
@@ -24465,7 +24562,7 @@ var Text = ({
24465
24562
  };
24466
24563
 
24467
24564
  // src/three-components/Error3d.tsx
24468
- import * as THREE29 from "three";
24565
+ import * as THREE30 from "three";
24469
24566
  import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs3 } from "react/jsx-runtime";
24470
24567
  var Error3d = ({
24471
24568
  error,
@@ -24499,13 +24596,13 @@ var Error3d = ({
24499
24596
  return [0, 0, 0];
24500
24597
  }, [cad_component]);
24501
24598
  const group = useMemo19(() => {
24502
- const g = new THREE29.Group();
24599
+ const g = new THREE30.Group();
24503
24600
  g.position.fromArray(position);
24504
24601
  return g;
24505
24602
  }, [position]);
24506
24603
  group.userData.cad_component_id = cad_component?.cad_component_id;
24507
24604
  group.userData.pcb_component_id = cad_component?.pcb_component_id;
24508
- useEffect25(() => {
24605
+ useEffect26(() => {
24509
24606
  if (!rootObject) return;
24510
24607
  rootObject.add(group);
24511
24608
  return () => {
@@ -24561,9 +24658,9 @@ var Error3d = ({
24561
24658
  };
24562
24659
  var ErrorBox = ({ parent }) => {
24563
24660
  const mesh = useMemo19(() => {
24564
- const m = new THREE29.Mesh(
24565
- new THREE29.BoxGeometry(0.5, 0.5, 0.5),
24566
- new THREE29.MeshStandardMaterial({
24661
+ const m = new THREE30.Mesh(
24662
+ new THREE30.BoxGeometry(0.5, 0.5, 0.5),
24663
+ new THREE30.MeshStandardMaterial({
24567
24664
  depthTest: false,
24568
24665
  transparent: true,
24569
24666
  color: "red",
@@ -24574,7 +24671,7 @@ var ErrorBox = ({ parent }) => {
24574
24671
  m.rotation.fromArray([Math.PI / 4, Math.PI / 4, 0]);
24575
24672
  return m;
24576
24673
  }, []);
24577
- useEffect25(() => {
24674
+ useEffect26(() => {
24578
24675
  parent.add(mesh);
24579
24676
  return () => {
24580
24677
  parent.remove(mesh);
@@ -24585,7 +24682,7 @@ var ErrorBox = ({ parent }) => {
24585
24682
 
24586
24683
  // src/three-components/JscadBoardTextures.tsx
24587
24684
  import { su as su9 } from "@tscircuit/circuit-json-util";
24588
- import { useEffect as useEffect26, useMemo as useMemo21 } from "react";
24685
+ import { useEffect as useEffect27, useMemo as useMemo21 } from "react";
24589
24686
 
24590
24687
  // src/textures/open-via-holes-at-pads.ts
24591
24688
  import { CircuitToCanvasDrawer } from "circuit-to-canvas";
@@ -24740,12 +24837,12 @@ function clearViaHolesFromSurface({
24740
24837
  }
24741
24838
 
24742
24839
  // src/textures/create-combined-board-textures.ts
24743
- import * as THREE42 from "three";
24840
+ import * as THREE43 from "three";
24744
24841
 
24745
24842
  // src/utils/pad-texture.ts
24746
24843
  import { su as su6 } from "@tscircuit/circuit-json-util";
24747
24844
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer2 } from "circuit-to-canvas";
24748
- import * as THREE30 from "three";
24845
+ import * as THREE31 from "three";
24749
24846
  function createPadTextureForLayer({
24750
24847
  layer,
24751
24848
  circuitJson,
@@ -24825,10 +24922,10 @@ function createPadTextureForLayer({
24825
24922
  drawSoldermaskTop: false,
24826
24923
  drawSoldermaskBottom: false
24827
24924
  });
24828
- const texture = new THREE30.CanvasTexture(canvas);
24925
+ const texture = new THREE31.CanvasTexture(canvas);
24829
24926
  texture.generateMipmaps = true;
24830
- texture.minFilter = THREE30.LinearMipmapLinearFilter;
24831
- texture.magFilter = THREE30.LinearFilter;
24927
+ texture.minFilter = THREE31.LinearMipmapLinearFilter;
24928
+ texture.magFilter = THREE31.LinearFilter;
24832
24929
  texture.anisotropy = 16;
24833
24930
  texture.needsUpdate = true;
24834
24931
  return texture;
@@ -24836,7 +24933,7 @@ function createPadTextureForLayer({
24836
24933
 
24837
24934
  // src/utils/panel-outline-texture.ts
24838
24935
  import { su as su7 } from "@tscircuit/circuit-json-util";
24839
- import * as THREE31 from "three";
24936
+ import * as THREE32 from "three";
24840
24937
  var resolvePanelIdForTexture = (circuitJson) => {
24841
24938
  const panels = circuitJson.filter(
24842
24939
  (e) => e.type === "pcb_panel"
@@ -24904,17 +25001,17 @@ function createPanelOutlineTextureForLayer({
24904
25001
  );
24905
25002
  }
24906
25003
  });
24907
- const texture = new THREE31.CanvasTexture(canvas);
25004
+ const texture = new THREE32.CanvasTexture(canvas);
24908
25005
  texture.generateMipmaps = true;
24909
- texture.minFilter = THREE31.LinearMipmapLinearFilter;
24910
- texture.magFilter = THREE31.LinearFilter;
25006
+ texture.minFilter = THREE32.LinearMipmapLinearFilter;
25007
+ texture.magFilter = THREE32.LinearFilter;
24911
25008
  texture.anisotropy = 16;
24912
25009
  texture.needsUpdate = true;
24913
25010
  return texture;
24914
25011
  }
24915
25012
 
24916
25013
  // src/utils/trace-texture.ts
24917
- import * as THREE32 from "three";
25014
+ import * as THREE33 from "three";
24918
25015
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer3 } from "circuit-to-canvas";
24919
25016
  import { getElementRenderLayers, su as su8 } from "@tscircuit/circuit-json-util";
24920
25017
  function createTraceTextureForLayer({
@@ -25000,10 +25097,10 @@ function createTraceTextureForLayer({
25000
25097
  drawSoldermaskTop: false,
25001
25098
  drawSoldermaskBottom: false
25002
25099
  });
25003
- const texture = new THREE32.CanvasTexture(canvas);
25100
+ const texture = new THREE33.CanvasTexture(canvas);
25004
25101
  texture.generateMipmaps = true;
25005
- texture.minFilter = THREE32.LinearMipmapLinearFilter;
25006
- texture.magFilter = THREE32.LinearFilter;
25102
+ texture.minFilter = THREE33.LinearMipmapLinearFilter;
25103
+ texture.magFilter = THREE33.LinearFilter;
25007
25104
  texture.anisotropy = 16;
25008
25105
  texture.needsUpdate = true;
25009
25106
  return texture;
@@ -25011,7 +25108,7 @@ function createTraceTextureForLayer({
25011
25108
 
25012
25109
  // src/textures/create-copper-pour-texture-for-layer.ts
25013
25110
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer4 } from "circuit-to-canvas";
25014
- import * as THREE33 from "three";
25111
+ import * as THREE34 from "three";
25015
25112
  var toRgb = (colorArr) => {
25016
25113
  const [r = 0, g = 0, b = 0] = colorArr;
25017
25114
  return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
@@ -25135,17 +25232,17 @@ function createCopperPourTextureForLayer({
25135
25232
  if (!onlyCoveredBySoldermask) {
25136
25233
  setColorAndDraw(uncoveredPours, uncoveredColor);
25137
25234
  }
25138
- const texture = new THREE33.CanvasTexture(canvas);
25235
+ const texture = new THREE34.CanvasTexture(canvas);
25139
25236
  texture.generateMipmaps = true;
25140
- texture.minFilter = THREE33.LinearMipmapLinearFilter;
25141
- texture.magFilter = THREE33.LinearFilter;
25237
+ texture.minFilter = THREE34.LinearMipmapLinearFilter;
25238
+ texture.magFilter = THREE34.LinearFilter;
25142
25239
  texture.anisotropy = 16;
25143
25240
  texture.needsUpdate = true;
25144
25241
  return texture;
25145
25242
  }
25146
25243
 
25147
25244
  // src/textures/create-copper-text-texture-for-layer.ts
25148
- import * as THREE34 from "three";
25245
+ import * as THREE35 from "three";
25149
25246
 
25150
25247
  // src/textures/copper-text/copper-text-drawing.ts
25151
25248
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer5 } from "circuit-to-canvas";
@@ -25260,17 +25357,17 @@ function createCopperTextTextureForLayer({
25260
25357
  elements,
25261
25358
  copperColor
25262
25359
  });
25263
- const texture = new THREE34.CanvasTexture(canvas);
25360
+ const texture = new THREE35.CanvasTexture(canvas);
25264
25361
  texture.generateMipmaps = true;
25265
- texture.minFilter = THREE34.LinearMipmapLinearFilter;
25266
- texture.magFilter = THREE34.LinearFilter;
25362
+ texture.minFilter = THREE35.LinearMipmapLinearFilter;
25363
+ texture.magFilter = THREE35.LinearFilter;
25267
25364
  texture.anisotropy = 16;
25268
25365
  texture.needsUpdate = true;
25269
25366
  return texture;
25270
25367
  }
25271
25368
 
25272
25369
  // src/textures/create-fabrication-note-texture-for-layer.ts
25273
- import * as THREE35 from "three";
25370
+ import * as THREE36 from "three";
25274
25371
 
25275
25372
  // src/textures/fabrication-note/fabrication-note-drawing.ts
25276
25373
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer6 } from "circuit-to-canvas";
@@ -25500,17 +25597,17 @@ function createFabricationNoteTextureForLayer({
25500
25597
  bounds,
25501
25598
  elements
25502
25599
  });
25503
- const texture = new THREE35.CanvasTexture(canvas);
25600
+ const texture = new THREE36.CanvasTexture(canvas);
25504
25601
  texture.generateMipmaps = true;
25505
- texture.minFilter = THREE35.LinearMipmapLinearFilter;
25506
- texture.magFilter = THREE35.LinearFilter;
25602
+ texture.minFilter = THREE36.LinearMipmapLinearFilter;
25603
+ texture.magFilter = THREE36.LinearFilter;
25507
25604
  texture.anisotropy = 16;
25508
25605
  texture.needsUpdate = true;
25509
25606
  return texture;
25510
25607
  }
25511
25608
 
25512
25609
  // src/textures/create-keepout-texture-for-layer.ts
25513
- import * as THREE36 from "three";
25610
+ import * as THREE37 from "three";
25514
25611
 
25515
25612
  // src/textures/keepout/keepout-drawing.ts
25516
25613
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer7 } from "circuit-to-canvas";
@@ -25585,17 +25682,17 @@ function createKeepoutTextureForLayer({
25585
25682
  elements,
25586
25683
  keepoutColor
25587
25684
  });
25588
- const texture = new THREE36.CanvasTexture(canvas);
25685
+ const texture = new THREE37.CanvasTexture(canvas);
25589
25686
  texture.generateMipmaps = true;
25590
- texture.minFilter = THREE36.LinearMipmapLinearFilter;
25591
- texture.magFilter = THREE36.LinearFilter;
25687
+ texture.minFilter = THREE37.LinearMipmapLinearFilter;
25688
+ texture.magFilter = THREE37.LinearFilter;
25592
25689
  texture.anisotropy = 16;
25593
25690
  texture.needsUpdate = true;
25594
25691
  return texture;
25595
25692
  }
25596
25693
 
25597
25694
  // src/textures/create-pcb-note-texture-for-layer.ts
25598
- import * as THREE37 from "three";
25695
+ import * as THREE38 from "three";
25599
25696
 
25600
25697
  // src/textures/pcb-note/pcb-note-drawing.ts
25601
25698
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer8 } from "circuit-to-canvas";
@@ -25749,17 +25846,17 @@ function createPcbNoteTextureForLayer({
25749
25846
  bounds,
25750
25847
  elements
25751
25848
  });
25752
- const texture = new THREE37.CanvasTexture(canvas);
25849
+ const texture = new THREE38.CanvasTexture(canvas);
25753
25850
  texture.generateMipmaps = true;
25754
- texture.minFilter = THREE37.LinearMipmapLinearFilter;
25755
- texture.magFilter = THREE37.LinearFilter;
25851
+ texture.minFilter = THREE38.LinearMipmapLinearFilter;
25852
+ texture.magFilter = THREE38.LinearFilter;
25756
25853
  texture.anisotropy = 16;
25757
25854
  texture.needsUpdate = true;
25758
25855
  return texture;
25759
25856
  }
25760
25857
 
25761
25858
  // src/textures/create-silkscreen-texture-for-layer.ts
25762
- import * as THREE38 from "three";
25859
+ import * as THREE39 from "three";
25763
25860
 
25764
25861
  // src/textures/silkscreen/silkscreen-drawing.ts
25765
25862
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer9 } from "circuit-to-canvas";
@@ -25910,21 +26007,21 @@ function createSilkscreenTextureForLayer({
25910
26007
  silkscreenColor,
25911
26008
  soldermaskVisible
25912
26009
  });
25913
- const texture = new THREE38.CanvasTexture(canvas);
26010
+ const texture = new THREE39.CanvasTexture(canvas);
25914
26011
  texture.generateMipmaps = true;
25915
- texture.minFilter = THREE38.LinearMipmapLinearFilter;
25916
- texture.magFilter = THREE38.LinearFilter;
26012
+ texture.minFilter = THREE39.LinearMipmapLinearFilter;
26013
+ texture.magFilter = THREE39.LinearFilter;
25917
26014
  texture.anisotropy = 16;
25918
26015
  texture.needsUpdate = true;
25919
26016
  return texture;
25920
26017
  }
25921
26018
 
25922
26019
  // src/textures/create-soldermask-texture-for-layer.ts
25923
- import * as THREE40 from "three";
26020
+ import * as THREE41 from "three";
25924
26021
 
25925
26022
  // src/textures/soldermask/soldermask-drawing.ts
25926
26023
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer10 } from "circuit-to-canvas";
25927
- import * as THREE39 from "three";
26024
+ import * as THREE40 from "three";
25928
26025
  var toRgb2 = (colorArr) => {
25929
26026
  const [r = 0, g = 0, b = 0] = colorArr;
25930
26027
  return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
@@ -25947,11 +26044,11 @@ var getSoldermaskPalette = (boardData) => {
25947
26044
  transparent: "rgba(0,0,0,0)"
25948
26045
  };
25949
26046
  }
25950
- const copperColor = new THREE39.Color().setRGB(
26047
+ const copperColor = new THREE40.Color().setRGB(
25951
26048
  colors.copper[0],
25952
26049
  colors.copper[1],
25953
26050
  colors.copper[2],
25954
- THREE39.SRGBColorSpace
26051
+ THREE40.SRGBColorSpace
25955
26052
  );
25956
26053
  return {
25957
26054
  soldermask: soldermaskColorToCss(requestedSoldermaskColor),
@@ -26086,17 +26183,17 @@ function createSoldermaskTextureForLayer({
26086
26183
  elements,
26087
26184
  boardData
26088
26185
  });
26089
- const texture = new THREE40.CanvasTexture(canvas);
26186
+ const texture = new THREE41.CanvasTexture(canvas);
26090
26187
  texture.generateMipmaps = true;
26091
- texture.minFilter = THREE40.LinearMipmapLinearFilter;
26092
- texture.magFilter = THREE40.LinearFilter;
26188
+ texture.minFilter = THREE41.LinearMipmapLinearFilter;
26189
+ texture.magFilter = THREE41.LinearFilter;
26093
26190
  texture.anisotropy = 16;
26094
26191
  texture.needsUpdate = true;
26095
26192
  return texture;
26096
26193
  }
26097
26194
 
26098
26195
  // src/textures/create-through-hole-texture-for-layer.ts
26099
- import * as THREE41 from "three";
26196
+ import * as THREE42 from "three";
26100
26197
 
26101
26198
  // src/textures/through-hole/through-hole-drawing.ts
26102
26199
  import { CircuitToCanvasDrawer as CircuitToCanvasDrawer11 } from "circuit-to-canvas";
@@ -26191,10 +26288,10 @@ function createThroughHoleTextureForLayer({
26191
26288
  elements,
26192
26289
  copperColor
26193
26290
  });
26194
- const texture = new THREE41.CanvasTexture(canvas);
26291
+ const texture = new THREE42.CanvasTexture(canvas);
26195
26292
  texture.generateMipmaps = true;
26196
- texture.minFilter = THREE41.LinearMipmapLinearFilter;
26197
- texture.magFilter = THREE41.LinearFilter;
26293
+ texture.minFilter = THREE42.LinearMipmapLinearFilter;
26294
+ texture.magFilter = THREE42.LinearFilter;
26198
26295
  texture.anisotropy = 16;
26199
26296
  texture.needsUpdate = true;
26200
26297
  return texture;
@@ -26291,10 +26388,10 @@ var createCombinedTexture = ({
26291
26388
  applySoldermaskSurfaceFilter(ctx, canvasWidth, canvasHeight, {
26292
26389
  includeReflection: layer === "top"
26293
26390
  });
26294
- const combinedTexture = new THREE42.CanvasTexture(canvas);
26391
+ const combinedTexture = new THREE43.CanvasTexture(canvas);
26295
26392
  combinedTexture.generateMipmaps = false;
26296
- combinedTexture.minFilter = THREE42.LinearFilter;
26297
- combinedTexture.magFilter = THREE42.LinearFilter;
26393
+ combinedTexture.minFilter = THREE43.LinearFilter;
26394
+ combinedTexture.magFilter = THREE43.LinearFilter;
26298
26395
  combinedTexture.premultiplyAlpha = true;
26299
26396
  combinedTexture.anisotropy = 16;
26300
26397
  combinedTexture.needsUpdate = true;
@@ -26330,11 +26427,11 @@ var createMaskedCopperMask = ({
26330
26427
  height
26331
26428
  );
26332
26429
  ctx.globalCompositeOperation = "source-over";
26333
- const maskTexture = new THREE42.CanvasTexture(canvas);
26334
- maskTexture.colorSpace = THREE42.NoColorSpace;
26430
+ const maskTexture = new THREE43.CanvasTexture(canvas);
26431
+ maskTexture.colorSpace = THREE43.NoColorSpace;
26335
26432
  maskTexture.generateMipmaps = false;
26336
- maskTexture.minFilter = THREE42.LinearFilter;
26337
- maskTexture.magFilter = THREE42.LinearFilter;
26433
+ maskTexture.minFilter = THREE43.LinearFilter;
26434
+ maskTexture.magFilter = THREE43.LinearFilter;
26338
26435
  maskTexture.premultiplyAlpha = false;
26339
26436
  maskTexture.needsUpdate = true;
26340
26437
  return maskTexture;
@@ -26480,7 +26577,7 @@ function createCombinedBoardTextures({
26480
26577
  }
26481
26578
 
26482
26579
  // src/textures/create-three-texture-meshes.ts
26483
- import * as THREE44 from "three";
26580
+ import * as THREE45 from "three";
26484
26581
 
26485
26582
  // src/board-surface-textures.ts
26486
26583
  var REALISTIC_BOARD_SURFACE_MATERIAL = {
@@ -26503,7 +26600,7 @@ var PAD_COPPER_TEXTURE_MATERIAL = {
26503
26600
  };
26504
26601
 
26505
26602
  // src/utils/create-board-relief-textures.ts
26506
- import * as THREE43 from "three";
26603
+ import * as THREE44 from "three";
26507
26604
  var PLAIN_SOLDERMASK_HEIGHT = 0.22;
26508
26605
  var MASKED_COPPER_HEIGHT = 0.7;
26509
26606
  var EXPOSED_COPPER_HEIGHT = 0.86;
@@ -26598,13 +26695,13 @@ var getBoardSurfaceProfile = (r, g, b, hasMaskedCopper) => {
26598
26695
  return PLAIN_SOLDERMASK_PROFILE;
26599
26696
  };
26600
26697
  var createDataTexture = (canvas) => {
26601
- const texture = new THREE43.CanvasTexture(canvas);
26602
- texture.colorSpace = THREE43.NoColorSpace;
26698
+ const texture = new THREE44.CanvasTexture(canvas);
26699
+ texture.colorSpace = THREE44.NoColorSpace;
26603
26700
  texture.generateMipmaps = false;
26604
- texture.minFilter = THREE43.LinearFilter;
26605
- texture.magFilter = THREE43.LinearFilter;
26606
- texture.wrapS = THREE43.ClampToEdgeWrapping;
26607
- texture.wrapT = THREE43.ClampToEdgeWrapping;
26701
+ texture.minFilter = THREE44.LinearFilter;
26702
+ texture.magFilter = THREE44.LinearFilter;
26703
+ texture.wrapS = THREE44.ClampToEdgeWrapping;
26704
+ texture.wrapT = THREE44.ClampToEdgeWrapping;
26608
26705
  texture.needsUpdate = true;
26609
26706
  return texture;
26610
26707
  };
@@ -26728,16 +26825,16 @@ function createTexturePlane(config, boardData) {
26728
26825
  } = config;
26729
26826
  if (!texture) return null;
26730
26827
  const boardOutlineBounds = calculateOutlineBounds(boardData);
26731
- const planeGeom = new THREE44.PlaneGeometry(
26828
+ const planeGeom = new THREE45.PlaneGeometry(
26732
26829
  boardOutlineBounds.width,
26733
26830
  boardOutlineBounds.height
26734
26831
  );
26735
- texture.colorSpace = THREE44.SRGBColorSpace;
26832
+ texture.colorSpace = THREE45.SRGBColorSpace;
26736
26833
  const sharedMaterialOptions = {
26737
26834
  map: texture,
26738
26835
  transparent: true,
26739
26836
  alphaTest: 0.08,
26740
- side: THREE44.FrontSide,
26837
+ side: THREE45.FrontSide,
26741
26838
  depthWrite: true,
26742
26839
  polygonOffset: usePolygonOffset,
26743
26840
  polygonOffsetFactor: usePolygonOffset ? -4 : 0,
@@ -26746,12 +26843,12 @@ function createTexturePlane(config, boardData) {
26746
26843
  opacity: isFaux ? FAUX_BOARD_OPACITY : 1
26747
26844
  };
26748
26845
  const reliefTextures = createBoardReliefTextures(texture, maskedCopperMask);
26749
- const material = new THREE44.MeshPhysicalMaterial({
26846
+ const material = new THREE45.MeshPhysicalMaterial({
26750
26847
  ...sharedMaterialOptions,
26751
26848
  bumpMap: reliefTextures?.bumpMap ?? null,
26752
26849
  bumpScale: REALISTIC_BOARD_SURFACE_MATERIAL.bumpScale,
26753
26850
  normalMap: reliefTextures?.normalMap ?? null,
26754
- normalScale: new THREE44.Vector2(
26851
+ normalScale: new THREE45.Vector2(
26755
26852
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale,
26756
26853
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale
26757
26854
  ),
@@ -26765,7 +26862,7 @@ function createTexturePlane(config, boardData) {
26765
26862
  clearcoatRoughness: REALISTIC_BOARD_SURFACE_MATERIAL.clearcoatRoughness,
26766
26863
  envMapIntensity: 0.18
26767
26864
  });
26768
- const mesh = new THREE44.Mesh(planeGeom, material);
26865
+ const mesh = new THREE45.Mesh(planeGeom, material);
26769
26866
  mesh.position.set(
26770
26867
  boardOutlineBounds.centerX,
26771
26868
  boardOutlineBounds.centerY,
@@ -26813,7 +26910,7 @@ function createTextureMeshes(textures, boardData, pcbThickness, isFaux = false)
26813
26910
  }
26814
26911
 
26815
26912
  // src/three-components/JscadBoardTextures.tsx
26816
- import * as THREE45 from "three";
26913
+ import * as THREE46 from "three";
26817
26914
 
26818
26915
  // src/hooks/useBoardTextureVisibility.ts
26819
26916
  import { useMemo as useMemo20 } from "react";
@@ -26931,7 +27028,7 @@ function JscadBoardTextures({
26931
27028
  visibility: textureVisibility
26932
27029
  });
26933
27030
  }, [circuitJson, boardData, traceTextureResolution, textureVisibility]);
26934
- useEffect26(() => {
27031
+ useEffect27(() => {
26935
27032
  if (!rootObject || !boardData || !textures) return;
26936
27033
  const meshes = [];
26937
27034
  const disposeTextureMaterial = (material) => {
@@ -26951,7 +27048,7 @@ function JscadBoardTextures({
26951
27048
  const typedMaterial = material;
26952
27049
  for (const prop of textureProps) {
26953
27050
  const texture = typedMaterial[prop];
26954
- if (texture && texture instanceof THREE45.Texture) {
27051
+ if (texture && texture instanceof THREE46.Texture) {
26955
27052
  texture.dispose();
26956
27053
  typedMaterial[prop] = null;
26957
27054
  }
@@ -26970,16 +27067,16 @@ function JscadBoardTextures({
26970
27067
  }) => {
26971
27068
  if (!texture) return null;
26972
27069
  const boardOutlineBounds = calculateOutlineBounds(boardData);
26973
- const planeGeom = new THREE45.PlaneGeometry(
27070
+ const planeGeom = new THREE46.PlaneGeometry(
26974
27071
  boardOutlineBounds.width,
26975
27072
  boardOutlineBounds.height
26976
27073
  );
26977
- texture.colorSpace = THREE45.SRGBColorSpace;
27074
+ texture.colorSpace = THREE46.SRGBColorSpace;
26978
27075
  const sharedMaterialOptions = {
26979
27076
  map: texture,
26980
27077
  transparent: true,
26981
27078
  alphaTest: 0.08,
26982
- side: THREE45.FrontSide,
27079
+ side: THREE46.FrontSide,
26983
27080
  depthWrite,
26984
27081
  polygonOffset: usePolygonOffset,
26985
27082
  polygonOffsetFactor: usePolygonOffset ? -4 : 0,
@@ -26990,12 +27087,12 @@ function JscadBoardTextures({
26990
27087
  texture,
26991
27088
  maskedCopperMask
26992
27089
  );
26993
- const material = new THREE45.MeshPhysicalMaterial({
27090
+ const material = new THREE46.MeshPhysicalMaterial({
26994
27091
  ...sharedMaterialOptions,
26995
27092
  bumpMap: reliefTextures?.bumpMap ?? null,
26996
27093
  bumpScale: REALISTIC_BOARD_SURFACE_MATERIAL.bumpScale,
26997
27094
  normalMap: reliefTextures?.normalMap ?? null,
26998
- normalScale: new THREE45.Vector2(
27095
+ normalScale: new THREE46.Vector2(
26999
27096
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale,
27000
27097
  REALISTIC_BOARD_SURFACE_MATERIAL.normalScale
27001
27098
  ),
@@ -27007,7 +27104,7 @@ function JscadBoardTextures({
27007
27104
  clearcoatRoughness: REALISTIC_BOARD_SURFACE_MATERIAL.clearcoatRoughness,
27008
27105
  envMapIntensity: 0.18
27009
27106
  });
27010
- const mesh = new THREE45.Mesh(planeGeom, material);
27107
+ const mesh = new THREE46.Mesh(planeGeom, material);
27011
27108
  mesh.position.set(
27012
27109
  boardOutlineBounds.centerX,
27013
27110
  boardOutlineBounds.centerY,
@@ -27057,7 +27154,7 @@ function JscadBoardTextures({
27057
27154
  for (const material of mesh.material) {
27058
27155
  disposeTextureMaterial(material);
27059
27156
  }
27060
- } else if (mesh.material instanceof THREE45.Material) {
27157
+ } else if (mesh.material instanceof THREE46.Material) {
27061
27158
  disposeTextureMaterial(mesh.material);
27062
27159
  }
27063
27160
  }
@@ -27069,8 +27166,8 @@ function JscadBoardTextures({
27069
27166
  }
27070
27167
 
27071
27168
  // src/three-components/STLModel.tsx
27072
- import { useState as useState17, useEffect as useEffect27, useMemo as useMemo22 } from "react";
27073
- import * as THREE46 from "three";
27169
+ import { useState as useState17, useEffect as useEffect28, useMemo as useMemo22 } from "react";
27170
+ import * as THREE47 from "three";
27074
27171
  import { STLLoader } from "three-stdlib";
27075
27172
  function STLModel({
27076
27173
  stlUrl,
@@ -27082,7 +27179,7 @@ function STLModel({
27082
27179
  }) {
27083
27180
  const { rootObject } = useThree();
27084
27181
  const [geom, setGeom] = useState17(null);
27085
- useEffect27(() => {
27182
+ useEffect28(() => {
27086
27183
  const loader = new STLLoader();
27087
27184
  if (stlData) {
27088
27185
  try {
@@ -27103,15 +27200,15 @@ function STLModel({
27103
27200
  const mesh = useMemo22(() => {
27104
27201
  if (!geom) return null;
27105
27202
  const isBoardLayer = layerType === "board";
27106
- const material = new THREE46.MeshStandardMaterial({
27107
- color: Array.isArray(color) ? new THREE46.Color(color[0], color[1], color[2]) : color,
27203
+ const material = new THREE47.MeshStandardMaterial({
27204
+ color: Array.isArray(color) ? new THREE47.Color(color[0], color[1], color[2]) : color,
27108
27205
  transparent: opacity !== 1,
27109
27206
  opacity,
27110
27207
  polygonOffset: isBoardLayer,
27111
27208
  polygonOffsetFactor: isBoardLayer ? 6 : 0,
27112
27209
  polygonOffsetUnits: isBoardLayer ? 6 : 0
27113
27210
  });
27114
- const createdMesh = new THREE46.Mesh(geom, material);
27211
+ const createdMesh = new THREE47.Mesh(geom, material);
27115
27212
  createdMesh.renderOrder = isBoardLayer ? -1 : 1;
27116
27213
  configureObjectShadows(createdMesh, {
27117
27214
  castShadow: !isBoardLayer && opacity === 1,
@@ -27119,7 +27216,7 @@ function STLModel({
27119
27216
  });
27120
27217
  return createdMesh;
27121
27218
  }, [geom, color, opacity, layerType]);
27122
- useEffect27(() => {
27219
+ useEffect28(() => {
27123
27220
  if (!rootObject || !mesh) return;
27124
27221
  rootObject.add(mesh);
27125
27222
  return () => {
@@ -27173,8 +27270,8 @@ function VisibleSTLModel({
27173
27270
  // src/three-components/CadCable.tsx
27174
27271
  import { parseCableString } from "@tscircuit/cableprinter";
27175
27272
  import { createCableMeshes } from "jscad-electronics/cables";
27176
- import { useEffect as useEffect28, useMemo as useMemo23 } from "react";
27177
- import * as THREE47 from "three";
27273
+ import { useEffect as useEffect29, useMemo as useMemo23 } from "react";
27274
+ import * as THREE48 from "three";
27178
27275
  var isCadCableElement = (element) => {
27179
27276
  if (!element || typeof element !== "object") return false;
27180
27277
  const candidate = element;
@@ -27191,14 +27288,14 @@ var getConnectorPin1Side = ({
27191
27288
  const endpoint = end === "start" ? path[0] : path[path.length - 1];
27192
27289
  const adjacent = end === "start" ? path[1] : path[path.length - 2];
27193
27290
  if (!endpoint || !adjacent) return void 0;
27194
- const cableAxis = new THREE47.Vector3(
27291
+ const cableAxis = new THREE48.Vector3(
27195
27292
  adjacent.x - endpoint.x,
27196
27293
  adjacent.y - endpoint.y,
27197
27294
  adjacent.z - endpoint.z
27198
27295
  );
27199
27296
  if (cableAxis.lengthSq() < 1e-12) return void 0;
27200
27297
  cableAxis.normalize();
27201
- const pin1Side = new THREE47.Vector3(
27298
+ const pin1Side = new THREE48.Vector3(
27202
27299
  pin1Position.x - endpoint.x,
27203
27300
  pin1Position.y - endpoint.y,
27204
27301
  pin1Position.z - endpoint.z
@@ -27224,28 +27321,28 @@ var createCadCableObject = (cable) => {
27224
27321
  end: "end"
27225
27322
  })
27226
27323
  });
27227
- const group = new THREE47.Group();
27324
+ const group = new THREE48.Group();
27228
27325
  group.name = `cad-cable-${cable.cad_cable_id}`;
27229
27326
  group.userData = { cadCableId: cable.cad_cable_id, name: cable.name };
27230
27327
  for (const cableMesh of cableMeshes) {
27231
- let geometry = new THREE47.BufferGeometry();
27328
+ let geometry = new THREE48.BufferGeometry();
27232
27329
  geometry.setAttribute(
27233
27330
  "position",
27234
- new THREE47.Float32BufferAttribute(cableMesh.positions, 3)
27331
+ new THREE48.Float32BufferAttribute(cableMesh.positions, 3)
27235
27332
  );
27236
27333
  geometry.setIndex(cableMesh.indices);
27237
27334
  if (!cableMesh.smooth) geometry = geometry.toNonIndexed();
27238
27335
  geometry.computeVertexNormals();
27239
27336
  const [red, green, blue, alpha] = cableMesh.color;
27240
- const material = new THREE47.MeshStandardMaterial({
27241
- color: new THREE47.Color(red, green, blue),
27337
+ const material = new THREE48.MeshStandardMaterial({
27338
+ color: new THREE48.Color(red, green, blue),
27242
27339
  metalness: cableMesh.material?.metalness ?? 0,
27243
27340
  roughness: cableMesh.material?.roughness ?? 0.7,
27244
27341
  opacity: alpha,
27245
27342
  transparent: alpha < 1,
27246
- side: THREE47.DoubleSide
27343
+ side: THREE48.DoubleSide
27247
27344
  });
27248
- const mesh = new THREE47.Mesh(geometry, material);
27345
+ const mesh = new THREE48.Mesh(geometry, material);
27249
27346
  mesh.name = `${group.name}:${cableMesh.name}`;
27250
27347
  configureObjectShadows(mesh, { castShadow: true, receiveShadow: true });
27251
27348
  group.add(mesh);
@@ -27254,7 +27351,7 @@ var createCadCableObject = (cable) => {
27254
27351
  };
27255
27352
  var disposeCadCableObject = (group) => {
27256
27353
  group.traverse((object) => {
27257
- if (!(object instanceof THREE47.Mesh)) return;
27354
+ if (!(object instanceof THREE48.Mesh)) return;
27258
27355
  object.geometry.dispose();
27259
27356
  const materials = Array.isArray(object.material) ? object.material : [object.material];
27260
27357
  for (const material of materials) material.dispose();
@@ -27270,7 +27367,7 @@ var CadCable = ({ cable }) => {
27270
27367
  return null;
27271
27368
  }
27272
27369
  }, [cable]);
27273
- useEffect28(() => {
27370
+ useEffect29(() => {
27274
27371
  if (!cableObject) return;
27275
27372
  rootObject.add(cableObject);
27276
27373
  return () => {
@@ -27281,6 +27378,22 @@ var CadCable = ({ cable }) => {
27281
27378
  return null;
27282
27379
  };
27283
27380
 
27381
+ // src/utils/get-initial-camera-position.ts
27382
+ function getInitialCameraPosition({
27383
+ boardDimensions,
27384
+ boardCenter
27385
+ }) {
27386
+ if (!boardDimensions || !boardCenter) return [5, -5, 5];
27387
+ const safeWidth = Math.max(boardDimensions.width ?? 0, 1);
27388
+ const safeHeight = Math.max(boardDimensions.height ?? 0, 1);
27389
+ const largestDimension = Math.max(safeWidth, safeHeight, 5);
27390
+ return [
27391
+ boardCenter.x + largestDimension * 0.4,
27392
+ boardCenter.y - largestDimension * 0.7,
27393
+ largestDimension * 0.9
27394
+ ];
27395
+ }
27396
+
27284
27397
  // src/utils/preprocess-circuit-json.ts
27285
27398
  import { su as su11 } from "@tscircuit/circuit-json-util";
27286
27399
 
@@ -27390,32 +27503,6 @@ var CadViewerJscad = forwardRef3(
27390
27503
  );
27391
27504
  }, [circuitJson, childrenSoup]);
27392
27505
  const boardGeom = useBoardGeomBuilder(internalCircuitJson);
27393
- const initialCameraPosition = useMemo24(() => {
27394
- if (!internalCircuitJson) return [5, -5, 5];
27395
- try {
27396
- const board = su12(internalCircuitJson).pcb_board.list()[0];
27397
- if (!board) return [5, -5, 5];
27398
- const { width, height } = board;
27399
- if (!width && !height) {
27400
- return [5, -5, 5];
27401
- }
27402
- const minCameraDistance = 5;
27403
- const adjustedBoardWidth = Math.max(width, minCameraDistance);
27404
- const adjustedBoardHeight = Math.max(height, minCameraDistance);
27405
- const largestDim = Math.max(adjustedBoardWidth, adjustedBoardHeight);
27406
- return [
27407
- largestDim * 0.4,
27408
- // Move right
27409
- -largestDim * 0.7,
27410
- // Move back (negative Y)
27411
- largestDim * 0.9
27412
- // Keep height but slightly lower than top-down
27413
- ];
27414
- } catch (e) {
27415
- console.error(e);
27416
- return [5, -5, 5];
27417
- }
27418
- }, [internalCircuitJson]);
27419
27506
  const isFauxBoard = useMemo24(() => {
27420
27507
  if (!internalCircuitJson) return false;
27421
27508
  try {
@@ -27449,6 +27536,19 @@ var CadViewerJscad = forwardRef3(
27449
27536
  return void 0;
27450
27537
  }
27451
27538
  }, [internalCircuitJson]);
27539
+ const cameraSessionScope = useMemo24(() => {
27540
+ if (!internalCircuitJson) return void 0;
27541
+ const board = su12(internalCircuitJson).pcb_board.list()[0];
27542
+ if (!board) return void 0;
27543
+ return JSON.stringify({
27544
+ pcbBoardId: board.pcb_board_id,
27545
+ center: boardCenter,
27546
+ dimensions: boardDimensions
27547
+ });
27548
+ }, [internalCircuitJson, boardCenter, boardDimensions]);
27549
+ const initialCameraPosition = useMemo24(() => {
27550
+ return getInitialCameraPosition({ boardDimensions, boardCenter });
27551
+ }, [boardDimensions, boardCenter]);
27452
27552
  const pcbThickness = usePcbThickness(internalCircuitJson);
27453
27553
  const { stls: boardStls, loading } = useStlsFromGeom(boardGeom);
27454
27554
  const cad_components = su12(internalCircuitJson).cad_component.list().filter((cad) => !hiddenCadComponentIds?.has(cad.cad_component_id));
@@ -27462,6 +27562,7 @@ var CadViewerJscad = forwardRef3(
27462
27562
  clickToInteractEnabled,
27463
27563
  boardDimensions,
27464
27564
  boardCenter,
27565
+ cameraSessionScope,
27465
27566
  referenceObject,
27466
27567
  onUserInteraction,
27467
27568
  onCameraControllerReady,
@@ -27507,6 +27608,9 @@ var CadViewerJscad = forwardRef3(
27507
27608
  }
27508
27609
  );
27509
27610
 
27611
+ // src/CadViewerManifold.tsx
27612
+ import { su as su17 } from "@tscircuit/circuit-json-util";
27613
+
27510
27614
  // node_modules/earcut/src/earcut.js
27511
27615
  var steiners = /* @__PURE__ */ new Set();
27512
27616
  var filteredOut = false;
@@ -28437,215 +28541,69 @@ function extrudePolygon({
28437
28541
  b: { ...b, z: bottom },
28438
28542
  c: { ...b, z: top },
28439
28543
  pcbFace: "side"
28440
- });
28441
- add10({
28442
- a: { ...a, z: bottom },
28443
- b: { ...b, z: top },
28444
- c: { ...a, z: top },
28445
- pcbFace: "side"
28446
- });
28447
- }
28448
- return { triangles, boundingBox: boundsOfTriangles(triangles) };
28449
- }
28450
- function createStiffenerMesh({
28451
- stiffener,
28452
- boardThickness,
28453
- fold
28454
- }) {
28455
- const adhesive = stiffener.adhesive_thickness ?? 0;
28456
- if (!Number.isFinite(adhesive) || adhesive < 0 || !Number.isFinite(stiffener.thickness) || stiffener.thickness <= 0 || !["top", "bottom"].includes(stiffener.layer))
28457
- throw new Error(`Invalid PCB stiffener ${stiffener.pcb_stiffener_id}`);
28458
- const outline = stiffener.shape === "polygon" ? stiffener.outline : (() => {
28459
- if (!(stiffener.width > 0 && stiffener.height > 0))
28460
- throw new Error("Invalid stiffener dimensions");
28461
- const a = (stiffener.rotation ?? 0) * Math.PI / 180;
28462
- return [
28463
- [-1, -1],
28464
- [1, -1],
28465
- [1, 1],
28466
- [-1, 1]
28467
- ].map(([x, y]) => ({
28468
- x: stiffener.center.x + x * stiffener.width / 2 * Math.cos(a) - y * stiffener.height / 2 * Math.sin(a),
28469
- y: stiffener.center.y + x * stiffener.width / 2 * Math.sin(a) + y * stiffener.height / 2 * Math.cos(a)
28470
- }));
28471
- })();
28472
- const near = boardThickness / 2 + adhesive, far = near + stiffener.thickness;
28473
- const mesh = extrudePolygon({
28474
- outline,
28475
- bottom: stiffener.layer === "top" ? near : -far,
28476
- top: stiffener.layer === "top" ? far : -near
28477
- });
28478
- if (!fold) return mesh;
28479
- fold.assertRigid(
28480
- mesh.triangles.flatMap((t) => t.vertices),
28481
- stiffener.pcb_stiffener_id
28482
- );
28483
- const anchor = {
28484
- x: outline.reduce((s, p) => s + p.x, 0) / outline.length,
28485
- y: outline.reduce((s, p) => s + p.y, 0) / outline.length,
28486
- z: 0
28487
- };
28488
- const triangles = mesh.triangles.map((t) => ({
28489
- ...t,
28490
- vertices: t.vertices.map((p) => fold.point(p, anchor)),
28491
- normal: fold.direction(t.normal, anchor)
28492
- }));
28493
- return { ...mesh, triangles, boundingBox: boundsOfTriangles(triangles) };
28494
- }
28495
-
28496
- // src/utils/flex-meshes.ts
28497
- import * as THREE48 from "three";
28498
- function meshToSurface(mesh, center) {
28499
- mesh.updateMatrix();
28500
- const geometry = mesh.geometry.index ? mesh.geometry.toNonIndexed() : mesh.geometry;
28501
- const position = geometry.getAttribute("position"), uv = geometry.getAttribute("uv");
28502
- const triangles = [];
28503
- for (let i = 0; i < position.count; i += 3) {
28504
- const vertices = [0, 1, 2].map((j) => {
28505
- const v = new THREE48.Vector3().fromBufferAttribute(position, i + j).applyMatrix4(mesh.matrix);
28506
- return { x: v.x - center.x, y: v.y - center.y, z: v.z };
28507
- });
28508
- const [a, b, c] = vertices;
28509
- const n = new THREE48.Vector3(b.x - a.x, b.y - a.y, b.z - a.z).cross(new THREE48.Vector3(c.x - a.x, c.y - a.y, c.z - a.z)).normalize();
28510
- triangles.push({
28511
- vertices,
28512
- normal: { x: n.x, y: n.y, z: n.z },
28513
- ...uv ? {
28514
- uvs: [0, 1, 2].map((j) => ({
28515
- u: uv.getX(i + j),
28516
- v: uv.getY(i + j)
28517
- }))
28518
- } : {}
28519
- });
28520
- }
28521
- if (geometry !== mesh.geometry) geometry.dispose();
28522
- return { triangles, boundingBox: boundsOfTriangles(triangles) };
28523
- }
28524
- function surfaceToGeometry(surface, center) {
28525
- const positions = [], normals = [], uvs = [];
28526
- for (const triangle of surface.triangles)
28527
- triangle.vertices.forEach((p, i) => {
28528
- positions.push(p.x + center.x, p.y + center.y, p.z);
28529
- normals.push(triangle.normal.x, triangle.normal.y, triangle.normal.z);
28530
- uvs.push(triangle.uvs?.[i]?.u ?? 0, triangle.uvs?.[i]?.v ?? 0);
28531
- });
28532
- const geometry = new THREE48.BufferGeometry();
28533
- geometry.setAttribute(
28534
- "position",
28535
- new THREE48.Float32BufferAttribute(positions, 3)
28536
- );
28537
- geometry.setAttribute("normal", new THREE48.Float32BufferAttribute(normals, 3));
28538
- geometry.setAttribute("uv", new THREE48.Float32BufferAttribute(uvs, 2));
28539
- geometry.computeBoundingBox();
28540
- geometry.computeBoundingSphere();
28541
- return geometry;
28542
- }
28543
- function replaceGeometry(mesh, surface, board) {
28544
- const output = mesh.clone();
28545
- output.geometry = surfaceToGeometry(surface, board.center);
28546
- output.position.set(0, 0, 0);
28547
- output.rotation.set(0, 0, 0);
28548
- output.scale.set(1, 1, 1);
28549
- output.updateMatrix();
28550
- return output;
28551
- }
28552
- function createFlexMeshes(geometryMeshes, textureMeshes, json, foldPcbs) {
28553
- const boards = json.filter((e) => e.type === "pcb_board");
28554
- const bends = json.filter((e) => e.type === "pcb_bend"), stiffeners = json.filter((e) => e.type === "pcb_stiffener");
28555
- if (!bends.length && !stiffeners.length)
28556
- return { geometryMeshes, textureMeshes };
28557
- const board = boards[0];
28558
- if (!board || boards.length !== 1 || json.some((e) => e.type === "pcb_panel") || [...bends, ...stiffeners].some((e) => e.pcb_board_id !== board.pcb_board_id))
28559
- throw new Error(
28560
- "Flex rendering requires one board, matching board references, and no panel"
28561
- );
28562
- const fold = foldPcbs ? createPcbFold(bends, board.thickness ?? 1.6) : void 0;
28563
- const body = geometryMeshes.find((m) => m.name === "board-geom");
28564
- const flatBody = body ? meshToSurface(body, board.center) : void 0;
28565
- const geometry = fold ? geometryMeshes.map(
28566
- (mesh) => replaceGeometry(
28567
- mesh,
28568
- foldSurfaceMesh(meshToSurface(mesh, board.center), fold),
28569
- board
28570
- )
28571
- ) : [...geometryMeshes];
28572
- const textures = fold && flatBody ? textureMeshes.map((mesh) => {
28573
- mesh.updateMatrix();
28574
- const inverse4 = mesh.matrix.clone().invert();
28575
- const bottom = mesh.name.startsWith("bottom");
28576
- const dimensions = mesh.geometry.parameters;
28577
- const triangles = flatBody.triangles.filter((t) => bottom ? t.normal.z < -0.9 : t.normal.z > 0.9).map((t) => {
28578
- const vertices = t.vertices.map((p) => ({
28579
- ...p,
28580
- z: mesh.position.z
28581
- }));
28582
- const uvs = vertices.map((p) => {
28583
- const local = new THREE48.Vector3(
28584
- p.x + board.center.x,
28585
- p.y + board.center.y,
28586
- p.z
28587
- ).applyMatrix4(inverse4);
28588
- return {
28589
- u: local.x / dimensions.width + 0.5,
28590
- v: local.y / dimensions.height + 0.5
28591
- };
28592
- });
28593
- return { ...t, vertices, uvs };
28594
- });
28595
- const result = replaceGeometry(
28596
- mesh,
28597
- foldSurfaceMesh(
28598
- { triangles, boundingBox: boundsOfTriangles(triangles) },
28599
- fold
28600
- ),
28601
- board
28602
- );
28603
- mesh.geometry.dispose();
28604
- return result;
28605
- }) : textureMeshes;
28606
- for (const stiffener of stiffeners) {
28607
- const mesh = new THREE48.Mesh(
28608
- surfaceToGeometry(
28609
- createStiffenerMesh({
28610
- stiffener,
28611
- boardThickness: board.thickness ?? 1.6,
28612
- fold
28613
- }),
28614
- board.center
28615
- ),
28616
- new THREE48.MeshStandardMaterial({
28617
- color: stiffener.material === "fr4" ? "#879568" : stiffener.material === "polyimide" ? "#bd7f27" : "#b8bdc4",
28618
- side: THREE48.DoubleSide
28619
- })
28620
- );
28621
- mesh.name = "board-stiffener";
28622
- configureObjectShadows(mesh);
28623
- geometry.push(mesh);
28544
+ });
28545
+ add10({
28546
+ a: { ...a, z: bottom },
28547
+ b: { ...b, z: top },
28548
+ c: { ...a, z: top },
28549
+ pcbFace: "side"
28550
+ });
28624
28551
  }
28625
- const bounds = new THREE48.Box3();
28626
- for (const mesh of [...geometry, ...textures]) bounds.expandByObject(mesh);
28627
- return {
28628
- geometryMeshes: geometry,
28629
- textureMeshes: textures,
28630
- bounds: {
28631
- minX: bounds.min.x,
28632
- maxX: bounds.max.x,
28633
- minY: bounds.min.y,
28634
- maxY: bounds.max.y,
28635
- minZ: bounds.min.z,
28636
- maxZ: bounds.max.z
28637
- }
28552
+ return { triangles, boundingBox: boundsOfTriangles(triangles) };
28553
+ }
28554
+ function createStiffenerMesh({
28555
+ stiffener,
28556
+ boardThickness,
28557
+ fold
28558
+ }) {
28559
+ const adhesive = stiffener.adhesive_thickness ?? 0;
28560
+ if (!Number.isFinite(adhesive) || adhesive < 0 || !Number.isFinite(stiffener.thickness) || stiffener.thickness <= 0 || !["top", "bottom"].includes(stiffener.layer))
28561
+ throw new Error(`Invalid PCB stiffener ${stiffener.pcb_stiffener_id}`);
28562
+ const outline = stiffener.shape === "polygon" ? stiffener.outline : (() => {
28563
+ if (!(stiffener.width > 0 && stiffener.height > 0))
28564
+ throw new Error("Invalid stiffener dimensions");
28565
+ const a = (stiffener.rotation ?? 0) * Math.PI / 180;
28566
+ return [
28567
+ [-1, -1],
28568
+ [1, -1],
28569
+ [1, 1],
28570
+ [-1, 1]
28571
+ ].map(([x, y]) => ({
28572
+ x: stiffener.center.x + x * stiffener.width / 2 * Math.cos(a) - y * stiffener.height / 2 * Math.sin(a),
28573
+ y: stiffener.center.y + x * stiffener.width / 2 * Math.sin(a) + y * stiffener.height / 2 * Math.cos(a)
28574
+ }));
28575
+ })();
28576
+ const near = boardThickness / 2 + adhesive, far = near + stiffener.thickness;
28577
+ const mesh = extrudePolygon({
28578
+ outline,
28579
+ bottom: stiffener.layer === "top" ? near : -far,
28580
+ top: stiffener.layer === "top" ? far : -near
28581
+ });
28582
+ if (!fold) return mesh;
28583
+ fold.assertRigid(
28584
+ mesh.triangles.flatMap((t) => t.vertices),
28585
+ stiffener.pcb_stiffener_id
28586
+ );
28587
+ const anchor = {
28588
+ x: outline.reduce((s, p) => s + p.x, 0) / outline.length,
28589
+ y: outline.reduce((s, p) => s + p.y, 0) / outline.length,
28590
+ z: 0
28638
28591
  };
28592
+ const triangles = mesh.triangles.map((t) => ({
28593
+ ...t,
28594
+ vertices: t.vertices.map((p) => fold.point(p, anchor)),
28595
+ normal: fold.direction(t.normal, anchor)
28596
+ }));
28597
+ return { ...mesh, triangles, boundingBox: boundsOfTriangles(triangles) };
28639
28598
  }
28640
28599
 
28641
28600
  // src/CadViewerManifold.tsx
28642
- import { su as su17 } from "@tscircuit/circuit-json-util";
28643
- import { useEffect as useEffect30, useMemo as useMemo26, useState as useState19 } from "react";
28644
- import * as THREE55 from "three";
28601
+ import { useEffect as useEffect31, useMemo as useMemo26, useState as useState19 } from "react";
28602
+ import * as THREE56 from "three";
28645
28603
 
28646
28604
  // src/hooks/useManifoldBoardBuilder.ts
28647
28605
  import { su as su16 } from "@tscircuit/circuit-json-util";
28648
- import { useEffect as useEffect29, useMemo as useMemo25, useRef as useRef9, useState as useState18 } from "react";
28606
+ import { useEffect as useEffect30, useMemo as useMemo25, useRef as useRef9, useState as useState18 } from "react";
28649
28607
  import * as THREE52 from "three";
28650
28608
 
28651
28609
  // src/utils/manifold/create-manifold-board.ts
@@ -29824,7 +29782,7 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
29824
29782
  if (!boardData) return TRACE_TEXTURE_RESOLUTION;
29825
29783
  return getLayerTextureResolution(boardData, TRACE_TEXTURE_RESOLUTION);
29826
29784
  }, [boardData]);
29827
- useEffect29(() => {
29785
+ useEffect30(() => {
29828
29786
  if (!manifoldJSModule || !boardData) {
29829
29787
  setGeoms(null);
29830
29788
  setPcbThickness(null);
@@ -30024,12 +29982,157 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson, visibility) => {
30024
29982
  };
30025
29983
  };
30026
29984
 
29985
+ // src/utils/flex-meshes.ts
29986
+ import * as THREE53 from "three";
29987
+ function meshToSurface(mesh, center) {
29988
+ mesh.updateMatrix();
29989
+ const geometry = mesh.geometry.index ? mesh.geometry.toNonIndexed() : mesh.geometry;
29990
+ const position = geometry.getAttribute("position"), uv = geometry.getAttribute("uv");
29991
+ const triangles = [];
29992
+ for (let i = 0; i < position.count; i += 3) {
29993
+ const vertices = [0, 1, 2].map((j) => {
29994
+ const v = new THREE53.Vector3().fromBufferAttribute(position, i + j).applyMatrix4(mesh.matrix);
29995
+ return { x: v.x - center.x, y: v.y - center.y, z: v.z };
29996
+ });
29997
+ const [a, b, c] = vertices;
29998
+ const n = new THREE53.Vector3(b.x - a.x, b.y - a.y, b.z - a.z).cross(new THREE53.Vector3(c.x - a.x, c.y - a.y, c.z - a.z)).normalize();
29999
+ triangles.push({
30000
+ vertices,
30001
+ normal: { x: n.x, y: n.y, z: n.z },
30002
+ ...uv ? {
30003
+ uvs: [0, 1, 2].map((j) => ({
30004
+ u: uv.getX(i + j),
30005
+ v: uv.getY(i + j)
30006
+ }))
30007
+ } : {}
30008
+ });
30009
+ }
30010
+ if (geometry !== mesh.geometry) geometry.dispose();
30011
+ return { triangles, boundingBox: boundsOfTriangles(triangles) };
30012
+ }
30013
+ function surfaceToGeometry(surface, center) {
30014
+ const positions = [], normals = [], uvs = [];
30015
+ for (const triangle of surface.triangles)
30016
+ triangle.vertices.forEach((p, i) => {
30017
+ positions.push(p.x + center.x, p.y + center.y, p.z);
30018
+ normals.push(triangle.normal.x, triangle.normal.y, triangle.normal.z);
30019
+ uvs.push(triangle.uvs?.[i]?.u ?? 0, triangle.uvs?.[i]?.v ?? 0);
30020
+ });
30021
+ const geometry = new THREE53.BufferGeometry();
30022
+ geometry.setAttribute(
30023
+ "position",
30024
+ new THREE53.Float32BufferAttribute(positions, 3)
30025
+ );
30026
+ geometry.setAttribute("normal", new THREE53.Float32BufferAttribute(normals, 3));
30027
+ geometry.setAttribute("uv", new THREE53.Float32BufferAttribute(uvs, 2));
30028
+ geometry.computeBoundingBox();
30029
+ geometry.computeBoundingSphere();
30030
+ return geometry;
30031
+ }
30032
+ function replaceGeometry(mesh, surface, board) {
30033
+ const output = mesh.clone();
30034
+ output.geometry = surfaceToGeometry(surface, board.center);
30035
+ output.position.set(0, 0, 0);
30036
+ output.rotation.set(0, 0, 0);
30037
+ output.scale.set(1, 1, 1);
30038
+ output.updateMatrix();
30039
+ return output;
30040
+ }
30041
+ function createFlexMeshes(geometryMeshes, textureMeshes, json, foldPcbs) {
30042
+ const boards = json.filter((e) => e.type === "pcb_board");
30043
+ const bends = json.filter((e) => e.type === "pcb_bend"), stiffeners = json.filter((e) => e.type === "pcb_stiffener");
30044
+ if (!bends.length && !stiffeners.length)
30045
+ return { geometryMeshes, textureMeshes };
30046
+ const board = boards[0];
30047
+ if (!board || boards.length !== 1 || json.some((e) => e.type === "pcb_panel") || [...bends, ...stiffeners].some((e) => e.pcb_board_id !== board.pcb_board_id))
30048
+ throw new Error(
30049
+ "Flex rendering requires one board, matching board references, and no panel"
30050
+ );
30051
+ const fold = foldPcbs ? createPcbFold(bends, board.thickness ?? 1.6) : void 0;
30052
+ const body = geometryMeshes.find((m) => m.name === "board-geom");
30053
+ const flatBody = body ? meshToSurface(body, board.center) : void 0;
30054
+ const geometry = fold ? geometryMeshes.map(
30055
+ (mesh) => replaceGeometry(
30056
+ mesh,
30057
+ foldSurfaceMesh(meshToSurface(mesh, board.center), fold),
30058
+ board
30059
+ )
30060
+ ) : [...geometryMeshes];
30061
+ const textures = fold && flatBody ? textureMeshes.map((mesh) => {
30062
+ mesh.updateMatrix();
30063
+ const inverse4 = mesh.matrix.clone().invert();
30064
+ const bottom = mesh.name.startsWith("bottom");
30065
+ const dimensions = mesh.geometry.parameters;
30066
+ const triangles = flatBody.triangles.filter((t) => bottom ? t.normal.z < -0.9 : t.normal.z > 0.9).map((t) => {
30067
+ const vertices = t.vertices.map((p) => ({
30068
+ ...p,
30069
+ z: mesh.position.z
30070
+ }));
30071
+ const uvs = vertices.map((p) => {
30072
+ const local = new THREE53.Vector3(
30073
+ p.x + board.center.x,
30074
+ p.y + board.center.y,
30075
+ p.z
30076
+ ).applyMatrix4(inverse4);
30077
+ return {
30078
+ u: local.x / dimensions.width + 0.5,
30079
+ v: local.y / dimensions.height + 0.5
30080
+ };
30081
+ });
30082
+ return { ...t, vertices, uvs };
30083
+ });
30084
+ const result = replaceGeometry(
30085
+ mesh,
30086
+ foldSurfaceMesh(
30087
+ { triangles, boundingBox: boundsOfTriangles(triangles) },
30088
+ fold
30089
+ ),
30090
+ board
30091
+ );
30092
+ mesh.geometry.dispose();
30093
+ return result;
30094
+ }) : textureMeshes;
30095
+ for (const stiffener of stiffeners) {
30096
+ const mesh = new THREE53.Mesh(
30097
+ surfaceToGeometry(
30098
+ createStiffenerMesh({
30099
+ stiffener,
30100
+ boardThickness: board.thickness ?? 1.6,
30101
+ fold
30102
+ }),
30103
+ board.center
30104
+ ),
30105
+ new THREE53.MeshStandardMaterial({
30106
+ color: stiffener.material === "fr4" ? "#879568" : stiffener.material === "polyimide" ? "#bd7f27" : "#b8bdc4",
30107
+ side: THREE53.DoubleSide
30108
+ })
30109
+ );
30110
+ mesh.name = "board-stiffener";
30111
+ configureObjectShadows(mesh);
30112
+ geometry.push(mesh);
30113
+ }
30114
+ const bounds = new THREE53.Box3();
30115
+ for (const mesh of [...geometry, ...textures]) bounds.expandByObject(mesh);
30116
+ return {
30117
+ geometryMeshes: geometry,
30118
+ textureMeshes: textures,
30119
+ bounds: {
30120
+ minX: bounds.min.x,
30121
+ maxX: bounds.max.x,
30122
+ minY: bounds.min.y,
30123
+ maxY: bounds.max.y,
30124
+ minZ: bounds.min.z,
30125
+ maxZ: bounds.max.z
30126
+ }
30127
+ };
30128
+ }
30129
+
30027
30130
  // src/utils/manifold/create-three-geometry-meshes.ts
30028
- import * as THREE54 from "three";
30131
+ import * as THREE55 from "three";
30029
30132
 
30030
30133
  // src/utils/create-board-material.ts
30031
- import * as THREE53 from "three";
30032
- var DEFAULT_SIDE = THREE53.DoubleSide;
30134
+ import * as THREE54 from "three";
30135
+ var DEFAULT_SIDE = THREE54.DoubleSide;
30033
30136
  var createBoardMaterial = ({
30034
30137
  material,
30035
30138
  color,
@@ -30037,7 +30140,7 @@ var createBoardMaterial = ({
30037
30140
  isFaux = false
30038
30141
  }) => {
30039
30142
  if (material === "fr4") {
30040
- return new THREE53.MeshPhysicalMaterial({
30143
+ return new THREE54.MeshPhysicalMaterial({
30041
30144
  color,
30042
30145
  side,
30043
30146
  metalness: 0,
@@ -30055,7 +30158,7 @@ var createBoardMaterial = ({
30055
30158
  polygonOffsetUnits: 1
30056
30159
  });
30057
30160
  }
30058
- return new THREE53.MeshStandardMaterial({
30161
+ return new THREE54.MeshStandardMaterial({
30059
30162
  color,
30060
30163
  side,
30061
30164
  flatShading: true,
@@ -30074,12 +30177,12 @@ function createGeometryMeshes(geoms) {
30074
30177
  const meshes = [];
30075
30178
  if (!geoms) return meshes;
30076
30179
  if (geoms.board?.geometry) {
30077
- const mesh = new THREE54.Mesh(
30180
+ const mesh = new THREE55.Mesh(
30078
30181
  geoms.board.geometry,
30079
30182
  createBoardMaterial({
30080
30183
  material: geoms.board.material,
30081
30184
  color: geoms.board.color,
30082
- side: THREE54.DoubleSide,
30185
+ side: THREE55.DoubleSide,
30083
30186
  isFaux: geoms.board.isFaux
30084
30187
  })
30085
30188
  );
@@ -30090,11 +30193,11 @@ function createGeometryMeshes(geoms) {
30090
30193
  const createMeshesFromArray = (geomArray) => {
30091
30194
  if (geomArray) {
30092
30195
  for (const comp of geomArray) {
30093
- const mesh = new THREE54.Mesh(
30196
+ const mesh = new THREE55.Mesh(
30094
30197
  comp.geometry,
30095
- new THREE54.MeshStandardMaterial({
30198
+ new THREE55.MeshStandardMaterial({
30096
30199
  color: comp.color,
30097
- side: THREE54.DoubleSide,
30200
+ side: THREE55.DoubleSide,
30098
30201
  flatShading: true
30099
30202
  // Consistent with board
30100
30203
  })
@@ -30110,6 +30213,33 @@ function createGeometryMeshes(geoms) {
30110
30213
  return meshes;
30111
30214
  }
30112
30215
 
30216
+ // src/utils/manifold/load-manifold.ts
30217
+ var manifoldPromise;
30218
+ function loadManifold() {
30219
+ if (!manifoldPromise) {
30220
+ manifoldPromise = (async () => {
30221
+ const existing = window.ManifoldModule ?? window.MANIFOLD ?? window.MANIFOLD_MODULE;
30222
+ if (existing && typeof existing === "object" && existing.setup) {
30223
+ window.ManifoldModule = existing;
30224
+ return existing;
30225
+ }
30226
+ const factory = existing ?? (await import(
30227
+ /* @vite-ignore */
30228
+ /* webpackIgnore: true */
30229
+ getRuntimeAssetUrl("manifold-3d", "3.2.1", "manifold.js")
30230
+ )).default;
30231
+ const module = await factory();
30232
+ module.setup();
30233
+ window.ManifoldModule = module;
30234
+ return module;
30235
+ })().catch((error) => {
30236
+ manifoldPromise = void 0;
30237
+ throw error;
30238
+ });
30239
+ }
30240
+ return manifoldPromise;
30241
+ }
30242
+
30113
30243
  // src/CadViewerManifold.tsx
30114
30244
  import { jsx as jsx19, jsxs as jsxs5 } from "react/jsx-runtime";
30115
30245
  var BoardMeshes = ({
@@ -30139,14 +30269,14 @@ var BoardMeshes = ({
30139
30269
  const typedMaterial = material;
30140
30270
  for (const prop of textureProps) {
30141
30271
  const texture = typedMaterial[prop];
30142
- if (texture && texture instanceof THREE55.Texture) {
30272
+ if (texture && texture instanceof THREE56.Texture) {
30143
30273
  texture.dispose();
30144
30274
  }
30145
30275
  }
30146
30276
  material.dispose();
30147
30277
  }
30148
30278
  };
30149
- useEffect30(() => {
30279
+ useEffect31(() => {
30150
30280
  if (!rootObject) return;
30151
30281
  for (const mesh of geometryMeshes) {
30152
30282
  let shouldShow = true;
@@ -30168,7 +30298,7 @@ var BoardMeshes = ({
30168
30298
  }
30169
30299
  };
30170
30300
  }, [rootObject, geometryMeshes, visibility]);
30171
- useEffect30(() => {
30301
+ useEffect31(() => {
30172
30302
  if (!rootObject) return;
30173
30303
  for (const mesh of textureMeshes) {
30174
30304
  rootObject.add(mesh);
@@ -30184,7 +30314,6 @@ var BoardMeshes = ({
30184
30314
  }, [rootObject, textureMeshes]);
30185
30315
  return null;
30186
30316
  };
30187
- var MANIFOLD_CDN_BASE_URL = "https://cdn.jsdelivr.net/npm/manifold-3d@3.2.1";
30188
30317
  var CadViewerManifold = ({
30189
30318
  circuitJson: circuitJsonProp,
30190
30319
  foldPcbs,
@@ -30205,66 +30334,19 @@ var CadViewerManifold = ({
30205
30334
  const [manifoldJSModule, setManifoldJSModule] = useState19(null);
30206
30335
  const [manifoldLoadingError, setManifoldLoadingError] = useState19(null);
30207
30336
  const { visibility } = useLayerVisibility();
30208
- useEffect30(() => {
30209
- if (window.ManifoldModule && typeof window.ManifoldModule === "object" && window.ManifoldModule.setup) {
30210
- setManifoldJSModule(window.ManifoldModule);
30211
- return;
30212
- }
30213
- const initManifold = async (ManifoldModule) => {
30214
- try {
30215
- const loadedModule = await ManifoldModule();
30216
- loadedModule.setup();
30217
- window.ManifoldModule = loadedModule;
30218
- setManifoldJSModule(loadedModule);
30219
- } catch (error) {
30220
- console.error("Failed to initialize Manifold:", error);
30221
- setManifoldLoadingError(
30222
- `Failed to initialize Manifold: ${error instanceof Error ? error.message : "Unknown error"}`
30223
- );
30224
- }
30225
- };
30226
- const existingManifold = window.ManifoldModule ?? window.MANIFOLD ?? window.MANIFOLD_MODULE;
30227
- if (existingManifold) {
30228
- window.ManifoldModule = existingManifold;
30229
- initManifold(window.ManifoldModule);
30230
- return;
30231
- }
30232
- const eventName = "manifoldLoaded";
30233
- const handleLoad = () => {
30234
- const loadedManifold = window.ManifoldModule ?? window.MANIFOLD ?? window.MANIFOLD_MODULE;
30235
- if (loadedManifold) {
30236
- window.ManifoldModule = loadedManifold;
30237
- initManifold(window.ManifoldModule);
30238
- } else {
30239
- const errText = "ManifoldModule not found on window after script load.";
30240
- console.error(errText);
30241
- setManifoldLoadingError(errText);
30242
- }
30243
- };
30244
- window.addEventListener(eventName, handleLoad, { once: true });
30245
- const script = document.createElement("script");
30246
- script.type = "module";
30247
- script.innerHTML = `
30248
- try {
30249
- const { default: ManifoldModule } = await import('${MANIFOLD_CDN_BASE_URL}/manifold.js');
30250
- window.ManifoldModule = ManifoldModule;
30251
- } catch (e) {
30252
- console.error('Error importing manifold in dynamic script:', e);
30253
- } finally {
30254
- window.dispatchEvent(new CustomEvent('${eventName}'));
30255
- }
30256
- `.trim();
30257
- const scriptError = (err) => {
30258
- const errText = "Failed to load Manifold loader script.";
30259
- console.error(errText, err);
30260
- setManifoldLoadingError(errText);
30261
- window.removeEventListener(eventName, handleLoad);
30262
- };
30263
- script.addEventListener("error", scriptError);
30264
- document.body.appendChild(script);
30337
+ useEffect31(() => {
30338
+ let isActive = true;
30339
+ loadManifold().then((module) => {
30340
+ if (isActive) setManifoldJSModule(module);
30341
+ }).catch((error) => {
30342
+ if (!isActive) return;
30343
+ console.error("Failed to initialize Manifold:", error);
30344
+ setManifoldLoadingError(
30345
+ `Failed to initialize Manifold: ${error instanceof Error ? error.message : "Unknown error"}`
30346
+ );
30347
+ });
30265
30348
  return () => {
30266
- window.removeEventListener(eventName, handleLoad);
30267
- script.removeEventListener("error", scriptError);
30349
+ isActive = false;
30268
30350
  };
30269
30351
  }, []);
30270
30352
  const {
@@ -30326,20 +30408,16 @@ try {
30326
30408
  return { x: bounds.centerX, y: bounds.centerY };
30327
30409
  }, [boardData]);
30328
30410
  const initialCameraPosition = useMemo26(() => {
30329
- if (!boardData) return [5, -5, 5];
30330
- const { width = 0, height = 0 } = boardData;
30331
- const safeWidth = Math.max(width, 1);
30332
- const safeHeight = Math.max(height, 1);
30333
- const largestDim = Math.max(safeWidth, safeHeight, 5);
30334
- return [
30335
- largestDim * 0.4,
30336
- // Move right
30337
- -largestDim * 0.7,
30338
- // Move back (negative Y)
30339
- largestDim * 0.9
30340
- // Keep height but slightly lower than top-down
30341
- ];
30342
- }, [boardData]);
30411
+ return getInitialCameraPosition({ boardDimensions, boardCenter });
30412
+ }, [boardDimensions, boardCenter]);
30413
+ const cameraSessionScope = useMemo26(() => {
30414
+ if (!boardData) return void 0;
30415
+ return JSON.stringify({
30416
+ pcbBoardId: boardData.pcb_board_id,
30417
+ center: boardCenter,
30418
+ dimensions: boardDimensions
30419
+ });
30420
+ }, [boardData, boardCenter, boardDimensions]);
30343
30421
  if (manifoldLoadingError) {
30344
30422
  return /* @__PURE__ */ jsxs5(
30345
30423
  "div",
@@ -30389,6 +30467,7 @@ try {
30389
30467
  clickToInteractEnabled,
30390
30468
  boardDimensions,
30391
30469
  boardCenter,
30470
+ cameraSessionScope,
30392
30471
  referenceObject,
30393
30472
  onUserInteraction,
30394
30473
  onCameraControllerReady,
@@ -37871,10 +37950,10 @@ var ContextMenu = ({
37871
37950
  };
37872
37951
 
37873
37952
  // src/components/KeyboardShortcutsDialog.tsx
37874
- import { useEffect as useEffect48, useMemo as useMemo36, useRef as useRef27, useState as useState40 } from "react";
37953
+ import { useEffect as useEffect49, useMemo as useMemo36, useRef as useRef27, useState as useState40 } from "react";
37875
37954
 
37876
37955
  // src/hooks/useRegisteredHotkey.ts
37877
- import { useEffect as useEffect47, useMemo as useMemo35, useRef as useRef26, useState as useState39 } from "react";
37956
+ import { useEffect as useEffect48, useMemo as useMemo35, useRef as useRef26, useState as useState39 } from "react";
37878
37957
  var hotkeyRegistry = /* @__PURE__ */ new Map();
37879
37958
  var subscribers = /* @__PURE__ */ new Set();
37880
37959
  var isListenerAttached = false;
@@ -38005,7 +38084,7 @@ var useRegisteredHotkey = (id, handler, metadata) => {
38005
38084
  }),
38006
38085
  [metadata.shortcut, metadata.description]
38007
38086
  );
38008
- useEffect47(() => {
38087
+ useEffect48(() => {
38009
38088
  const registration = {
38010
38089
  id,
38011
38090
  ...normalizedMetadata,
@@ -38021,7 +38100,7 @@ var useHotkeyRegistry = () => {
38021
38100
  const [entries, setEntries] = useState39(
38022
38101
  () => Array.from(hotkeyRegistry.values())
38023
38102
  );
38024
- useEffect47(() => subscribeToRegistry(setEntries), []);
38103
+ useEffect48(() => subscribeToRegistry(setEntries), []);
38025
38104
  return entries;
38026
38105
  };
38027
38106
  var registerHotkeyViewer = (element) => {
@@ -38038,7 +38117,7 @@ var KeyboardShortcutsDialog = ({
38038
38117
  const [query, setQuery] = useState40("");
38039
38118
  const inputRef = useRef27(null);
38040
38119
  const hotkeys = useHotkeyRegistry();
38041
- useEffect48(() => {
38120
+ useEffect49(() => {
38042
38121
  if (!open) return void 0;
38043
38122
  const handleKeyDown = (event) => {
38044
38123
  if (event.key === "Escape") {
@@ -38049,7 +38128,7 @@ var KeyboardShortcutsDialog = ({
38049
38128
  window.addEventListener("keydown", handleKeyDown);
38050
38129
  return () => window.removeEventListener("keydown", handleKeyDown);
38051
38130
  }, [open, onClose]);
38052
- useEffect48(() => {
38131
+ useEffect49(() => {
38053
38132
  if (open) {
38054
38133
  setTimeout(() => {
38055
38134
  inputRef.current?.focus();
@@ -38278,7 +38357,7 @@ function useCameraPreset({
38278
38357
  }
38279
38358
 
38280
38359
  // src/hooks/useContextMenu.ts
38281
- import { useState as useState41, useCallback as useCallback23, useRef as useRef28, useEffect as useEffect49 } from "react";
38360
+ import { useState as useState41, useCallback as useCallback23, useRef as useRef28, useEffect as useEffect50 } from "react";
38282
38361
  var useContextMenu = ({ containerRef, onOpen }) => {
38283
38362
  const [menuVisible, setMenuVisible] = useState41(false);
38284
38363
  const [menuPos, setMenuPos] = useState41({
@@ -38395,7 +38474,7 @@ var useContextMenu = ({ containerRef, onOpen }) => {
38395
38474
  }
38396
38475
  setMenuVisible(false);
38397
38476
  }, []);
38398
- useEffect49(() => {
38477
+ useEffect50(() => {
38399
38478
  if (menuVisible) {
38400
38479
  document.addEventListener("mousedown", handleClickAway);
38401
38480
  document.addEventListener("touchstart", handleClickAway);
@@ -38459,7 +38538,7 @@ var useGlobalDownloadGltf = () => {
38459
38538
 
38460
38539
  // src/CadViewer.tsx
38461
38540
  import { jsx as jsx38, jsxs as jsxs10 } from "react/jsx-runtime";
38462
- var DEFAULT_TARGET = new THREE56.Vector3(0, 0, 0);
38541
+ var DEFAULT_TARGET = new THREE57.Vector3(0, 0, 0);
38463
38542
  var INITIAL_CAMERA_POSITION = [5, -5, 5];
38464
38543
  var readStoredCameraType = () => {
38465
38544
  if (typeof window === "undefined") return void 0;
@@ -38626,24 +38705,24 @@ var CadViewerInner = (props) => {
38626
38705
  description: "Toggle translucent components"
38627
38706
  }
38628
38707
  );
38629
- useEffect50(() => {
38708
+ useEffect51(() => {
38630
38709
  if (containerRef.current) {
38631
38710
  registerHotkeyViewer(containerRef.current);
38632
38711
  }
38633
38712
  }, []);
38634
- useEffect50(() => {
38713
+ useEffect51(() => {
38635
38714
  window.localStorage.setItem("cadViewerEngine", engine);
38636
38715
  }, [engine]);
38637
- useEffect50(() => {
38716
+ useEffect51(() => {
38638
38717
  window.localStorage.setItem("cadViewerAutoRotate", String(autoRotate));
38639
38718
  }, [autoRotate]);
38640
- useEffect50(() => {
38719
+ useEffect51(() => {
38641
38720
  window.localStorage.setItem(
38642
38721
  "cadViewerAutoRotateUserToggled",
38643
38722
  String(autoRotateUserToggled)
38644
38723
  );
38645
38724
  }, [autoRotateUserToggled]);
38646
- useEffect50(() => {
38725
+ useEffect51(() => {
38647
38726
  window.localStorage.setItem("cadViewerCameraType", cameraType);
38648
38727
  }, [cameraType]);
38649
38728
  const viewerKey = props.circuitJson ? circuitKey : void 0;
@@ -38767,6 +38846,7 @@ var CadViewerInner = (props) => {
38767
38846
  );
38768
38847
  };
38769
38848
  var CadViewer = (props) => {
38849
+ configureRuntimeFont();
38770
38850
  const sceneRef = useRef29(null);
38771
38851
  return /* @__PURE__ */ jsx38(SchematicNavigationContext.Provider, { value: sceneRef, children: /* @__PURE__ */ jsx38(
38772
38852
  CameraControllerProvider,
@@ -38782,11 +38862,11 @@ var CadViewer = (props) => {
38782
38862
  // src/convert-circuit-json-to-3d-svg.ts
38783
38863
  var import_debug = __toESM(require_browser(), 1);
38784
38864
  import { su as su18 } from "@tscircuit/circuit-json-util";
38785
- import * as THREE60 from "three";
38865
+ import * as THREE61 from "three";
38786
38866
  import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
38787
38867
 
38788
38868
  // src/utils/create-geometry-from-polygons.ts
38789
- import * as THREE57 from "three";
38869
+ import * as THREE58 from "three";
38790
38870
  import { BufferGeometry as BufferGeometry6, Float32BufferAttribute as Float32BufferAttribute6 } from "three";
38791
38871
  function createGeometryFromPolygons(polygons) {
38792
38872
  const geometry = new BufferGeometry6();
@@ -38800,12 +38880,12 @@ function createGeometryFromPolygons(polygons) {
38800
38880
  ...polygon2.vertices[i + 1]
38801
38881
  // Third vertex
38802
38882
  );
38803
- const v1 = new THREE57.Vector3(...polygon2.vertices[0]);
38804
- const v2 = new THREE57.Vector3(...polygon2.vertices[i]);
38805
- const v3 = new THREE57.Vector3(...polygon2.vertices[i + 1]);
38806
- const normal = new THREE57.Vector3().crossVectors(
38807
- new THREE57.Vector3().subVectors(v2, v1),
38808
- new THREE57.Vector3().subVectors(v3, v1)
38883
+ const v1 = new THREE58.Vector3(...polygon2.vertices[0]);
38884
+ const v2 = new THREE58.Vector3(...polygon2.vertices[i]);
38885
+ const v3 = new THREE58.Vector3(...polygon2.vertices[i + 1]);
38886
+ const normal = new THREE58.Vector3().crossVectors(
38887
+ new THREE58.Vector3().subVectors(v2, v1),
38888
+ new THREE58.Vector3().subVectors(v3, v1)
38809
38889
  ).normalize();
38810
38890
  normals.push(
38811
38891
  normal.x,
@@ -38833,10 +38913,10 @@ import {
38833
38913
  convertCSGToThreeGeom as convertCSGToThreeGeom3,
38834
38914
  getJscadModelForFootprint as getJscadModelForFootprint2
38835
38915
  } from "jscad-electronics/vanilla";
38836
- import * as THREE59 from "three";
38916
+ import * as THREE60 from "three";
38837
38917
 
38838
38918
  // src/utils/load-model.ts
38839
- import * as THREE58 from "three";
38919
+ import * as THREE59 from "three";
38840
38920
  import { GLTFLoader as GLTFLoader2 } from "three/examples/jsm/loaders/GLTFLoader.js";
38841
38921
  import { OBJLoader as OBJLoader2 } from "three/examples/jsm/loaders/OBJLoader.js";
38842
38922
  import { STLLoader as STLLoader2 } from "three/examples/jsm/loaders/STLLoader.js";
@@ -38844,12 +38924,12 @@ async function load3DModel(url) {
38844
38924
  if (url.endsWith(".stl")) {
38845
38925
  const loader = new STLLoader2();
38846
38926
  const geometry = await loader.loadAsync(url);
38847
- const material = new THREE58.MeshStandardMaterial({
38927
+ const material = new THREE59.MeshStandardMaterial({
38848
38928
  color: 8947848,
38849
38929
  metalness: 0.5,
38850
38930
  roughness: 0.5
38851
38931
  });
38852
- return new THREE58.Mesh(geometry, material);
38932
+ return new THREE59.Mesh(geometry, material);
38853
38933
  }
38854
38934
  if (url.endsWith(".obj")) {
38855
38935
  const loader = new OBJLoader2();
@@ -38882,9 +38962,9 @@ async function renderComponent(component, scene) {
38882
38962
  }
38883
38963
  if (component.rotation) {
38884
38964
  model.rotation.set(
38885
- THREE59.MathUtils.degToRad(component.rotation.x ?? 0),
38886
- THREE59.MathUtils.degToRad(component.rotation.y ?? 0),
38887
- THREE59.MathUtils.degToRad(component.rotation.z ?? 0)
38965
+ THREE60.MathUtils.degToRad(component.rotation.x ?? 0),
38966
+ THREE60.MathUtils.degToRad(component.rotation.y ?? 0),
38967
+ THREE60.MathUtils.degToRad(component.rotation.z ?? 0)
38888
38968
  );
38889
38969
  }
38890
38970
  scene.add(model);
@@ -38898,13 +38978,13 @@ async function renderComponent(component, scene) {
38898
38978
  );
38899
38979
  if (jscadObject && (jscadObject.polygons || jscadObject.sides)) {
38900
38980
  const threeGeom = convertCSGToThreeGeom3(jscadObject);
38901
- const material2 = new THREE59.MeshStandardMaterial({
38981
+ const material2 = new THREE60.MeshStandardMaterial({
38902
38982
  color: 8947848,
38903
38983
  metalness: 0.5,
38904
38984
  roughness: 0.5,
38905
- side: THREE59.DoubleSide
38985
+ side: THREE60.DoubleSide
38906
38986
  });
38907
- const mesh2 = new THREE59.Mesh(threeGeom, material2);
38987
+ const mesh2 = new THREE60.Mesh(threeGeom, material2);
38908
38988
  if (component.position) {
38909
38989
  mesh2.position.set(
38910
38990
  component.position.x ?? 0,
@@ -38914,9 +38994,9 @@ async function renderComponent(component, scene) {
38914
38994
  }
38915
38995
  if (component.rotation) {
38916
38996
  mesh2.rotation.set(
38917
- THREE59.MathUtils.degToRad(component.rotation.x ?? 0),
38918
- THREE59.MathUtils.degToRad(component.rotation.y ?? 0),
38919
- THREE59.MathUtils.degToRad(component.rotation.z ?? 0)
38997
+ THREE60.MathUtils.degToRad(component.rotation.x ?? 0),
38998
+ THREE60.MathUtils.degToRad(component.rotation.y ?? 0),
38999
+ THREE60.MathUtils.degToRad(component.rotation.z ?? 0)
38920
39000
  );
38921
39001
  }
38922
39002
  scene.add(mesh2);
@@ -38933,17 +39013,17 @@ async function renderComponent(component, scene) {
38933
39013
  if (!geom || !geom.polygons && !geom.sides) {
38934
39014
  continue;
38935
39015
  }
38936
- const color = new THREE59.Color(geomInfo.color);
39016
+ const color = new THREE60.Color(geomInfo.color);
38937
39017
  color.convertLinearToSRGB();
38938
39018
  const geomWithColor = { ...geom, color: [color.r, color.g, color.b] };
38939
39019
  const threeGeom = convertCSGToThreeGeom3(geomWithColor);
38940
- const material2 = new THREE59.MeshStandardMaterial({
39020
+ const material2 = new THREE60.MeshStandardMaterial({
38941
39021
  vertexColors: true,
38942
39022
  metalness: 0.2,
38943
39023
  roughness: 0.8,
38944
- side: THREE59.DoubleSide
39024
+ side: THREE60.DoubleSide
38945
39025
  });
38946
- const mesh2 = new THREE59.Mesh(threeGeom, material2);
39026
+ const mesh2 = new THREE60.Mesh(threeGeom, material2);
38947
39027
  if (component.position) {
38948
39028
  mesh2.position.set(
38949
39029
  component.position.x ?? 0,
@@ -38953,22 +39033,22 @@ async function renderComponent(component, scene) {
38953
39033
  }
38954
39034
  if (component.rotation) {
38955
39035
  mesh2.rotation.set(
38956
- THREE59.MathUtils.degToRad(component.rotation.x ?? 0),
38957
- THREE59.MathUtils.degToRad(component.rotation.y ?? 0),
38958
- THREE59.MathUtils.degToRad(component.rotation.z ?? 0)
39036
+ THREE60.MathUtils.degToRad(component.rotation.x ?? 0),
39037
+ THREE60.MathUtils.degToRad(component.rotation.y ?? 0),
39038
+ THREE60.MathUtils.degToRad(component.rotation.z ?? 0)
38959
39039
  );
38960
39040
  }
38961
39041
  scene.add(mesh2);
38962
39042
  }
38963
39043
  return;
38964
39044
  }
38965
- const geometry = new THREE59.BoxGeometry(0.5, 0.5, 0.5);
38966
- const material = new THREE59.MeshStandardMaterial({
39045
+ const geometry = new THREE60.BoxGeometry(0.5, 0.5, 0.5);
39046
+ const material = new THREE60.MeshStandardMaterial({
38967
39047
  color: 16711680,
38968
39048
  transparent: true,
38969
39049
  opacity: 0.25
38970
39050
  });
38971
- const mesh = new THREE59.Mesh(geometry, material);
39051
+ const mesh = new THREE60.Mesh(geometry, material);
38972
39052
  if (component.position) {
38973
39053
  mesh.position.set(
38974
39054
  component.position.x ?? 0,
@@ -38989,11 +39069,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
38989
39069
  padding = 20,
38990
39070
  zoom = 1.5
38991
39071
  } = options;
38992
- const scene = new THREE60.Scene();
39072
+ const scene = new THREE61.Scene();
38993
39073
  const renderer = new SVGRenderer();
38994
39074
  renderer.setSize(width, height);
38995
- renderer.setClearColor(new THREE60.Color(backgroundColor), 1);
38996
- const camera = new THREE60.OrthographicCamera();
39075
+ renderer.setClearColor(new THREE61.Color(backgroundColor), 1);
39076
+ const camera = new THREE61.OrthographicCamera();
38997
39077
  const aspect = width / height;
38998
39078
  const frustumSize = 100;
38999
39079
  const halfFrustumSize = frustumSize / 2 / zoom;
@@ -39007,11 +39087,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
39007
39087
  camera.position.set(position.x, position.y, position.z);
39008
39088
  camera.up.set(0, 1, 0);
39009
39089
  const lookAt2 = options.camera?.lookAt ?? { x: 0, y: 0, z: 0 };
39010
- camera.lookAt(new THREE60.Vector3(lookAt2.x, lookAt2.y, lookAt2.z));
39090
+ camera.lookAt(new THREE61.Vector3(lookAt2.x, lookAt2.y, lookAt2.z));
39011
39091
  camera.updateProjectionMatrix();
39012
- const ambientLight = new THREE60.AmbientLight(16777215, Math.PI / 2);
39092
+ const ambientLight = new THREE61.AmbientLight(16777215, Math.PI / 2);
39013
39093
  scene.add(ambientLight);
39014
- const pointLight = new THREE60.PointLight(16777215, Math.PI / 4);
39094
+ const pointLight = new THREE61.PointLight(16777215, Math.PI / 4);
39015
39095
  pointLight.position.set(-10, -10, 10);
39016
39096
  scene.add(pointLight);
39017
39097
  const components = su18(circuitJson).cad_component.list();
@@ -39043,28 +39123,28 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
39043
39123
  const material = createBoardMaterial({
39044
39124
  material: boardData?.material,
39045
39125
  color: edgeColor,
39046
- side: THREE60.DoubleSide
39126
+ side: THREE61.DoubleSide
39047
39127
  });
39048
- const mesh = new THREE60.Mesh(geometry, material);
39128
+ const mesh = new THREE61.Mesh(geometry, material);
39049
39129
  scene.add(mesh);
39050
39130
  }
39051
39131
  }
39052
- const gridColor = new THREE60.Color(8947848);
39053
- const gridHelper = new THREE60.GridHelper(100, 100, gridColor, gridColor);
39132
+ const gridColor = new THREE61.Color(8947848);
39133
+ const gridHelper = new THREE61.GridHelper(100, 100, gridColor, gridColor);
39054
39134
  gridHelper.rotation.x = Math.PI / 2;
39055
39135
  const materials = Array.isArray(gridHelper.material) ? gridHelper.material : [gridHelper.material];
39056
39136
  for (const mat of materials) {
39057
39137
  mat.transparent = true;
39058
39138
  mat.opacity = 0.3;
39059
- if (mat instanceof THREE60.LineBasicMaterial) {
39139
+ if (mat instanceof THREE61.LineBasicMaterial) {
39060
39140
  mat.color = gridColor;
39061
39141
  mat.vertexColors = false;
39062
39142
  }
39063
39143
  }
39064
39144
  scene.add(gridHelper);
39065
- const box = new THREE60.Box3().setFromObject(scene);
39066
- const center = box.getCenter(new THREE60.Vector3());
39067
- const size4 = box.getSize(new THREE60.Vector3());
39145
+ const box = new THREE61.Box3().setFromObject(scene);
39146
+ const center = box.getCenter(new THREE61.Vector3());
39147
+ const size4 = box.getSize(new THREE61.Vector3());
39068
39148
  scene.position.sub(center);
39069
39149
  const maxDim = Math.max(size4.x, size4.y, size4.z);
39070
39150
  if (maxDim > 0) {
@@ -39082,7 +39162,7 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
39082
39162
 
39083
39163
  // src/hooks/exporter/gltf.ts
39084
39164
  import { GLTFExporter as GLTFExporter3 } from "three-stdlib";
39085
- import { useEffect as useEffect51, useState as useState43, useMemo as useMemo37, useCallback as useCallback26 } from "react";
39165
+ import { useEffect as useEffect52, useState as useState43, useMemo as useMemo37, useCallback as useCallback26 } from "react";
39086
39166
  function useSaveGltfAs(options = {}) {
39087
39167
  const parse2 = useParser(options);
39088
39168
  const link = useMemo37(() => document.createElement("a"), []);
@@ -39095,7 +39175,7 @@ function useSaveGltfAs(options = {}) {
39095
39175
  link.dispatchEvent(new MouseEvent("click"));
39096
39176
  URL.revokeObjectURL(url);
39097
39177
  };
39098
- useEffect51(
39178
+ useEffect52(
39099
39179
  () => () => {
39100
39180
  link.remove();
39101
39181
  instance = null;
@@ -39116,7 +39196,7 @@ function useExportGltfUrl(options = {}) {
39116
39196
  (instance) => parse2(instance).then(setUrl).catch(setError),
39117
39197
  []
39118
39198
  );
39119
- useEffect51(() => () => URL.revokeObjectURL(url), [url]);
39199
+ useEffect52(() => () => URL.revokeObjectURL(url), [url]);
39120
39200
  return [ref, url, error];
39121
39201
  }
39122
39202
  function useParser(options = {}) {