@tscircuit/3d-viewer 0.0.432 → 0.0.434

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.d.ts CHANGED
@@ -48,6 +48,7 @@ interface ManifoldGeoms {
48
48
  geometry: THREE.BufferGeometry;
49
49
  color: THREE.Color;
50
50
  material: PcbBoard["material"];
51
+ isFaux?: boolean;
51
52
  };
52
53
  platedHoles?: Array<{
53
54
  key: string;
@@ -83,6 +84,7 @@ interface UseManifoldBoardBuilderResult {
83
84
  error: string | null;
84
85
  isLoading: boolean;
85
86
  boardData: PcbBoard | null;
87
+ isFauxBoard: boolean;
86
88
  }
87
89
  declare const useManifoldBoardBuilder: (manifoldJSModule: ManifoldToplevel | null, circuitJson: AnyCircuitElement[]) => UseManifoldBoardBuilderResult;
88
90
 
package/dist/index.js CHANGED
@@ -14231,7 +14231,7 @@ var require_browser = __commonJS({
14231
14231
  import { useState as useState33, useCallback as useCallback20, useRef as useRef23, useEffect as useEffect39 } from "react";
14232
14232
 
14233
14233
  // src/CadViewerJscad.tsx
14234
- import { su as su4 } from "@tscircuit/circuit-json-util";
14234
+ import { su as su6 } from "@tscircuit/circuit-json-util";
14235
14235
  import { forwardRef as forwardRef3, useMemo as useMemo19 } from "react";
14236
14236
 
14237
14237
  // src/AnyCadComponent.tsx
@@ -28174,7 +28174,7 @@ import * as THREE14 from "three";
28174
28174
  // package.json
28175
28175
  var package_default = {
28176
28176
  name: "@tscircuit/3d-viewer",
28177
- version: "0.0.431",
28177
+ version: "0.0.433",
28178
28178
  main: "./dist/index.js",
28179
28179
  module: "./dist/index.js",
28180
28180
  type: "module",
@@ -31449,6 +31449,93 @@ var ThreeErrorBoundary = class extends React11.Component {
31449
31449
  }
31450
31450
  };
31451
31451
 
31452
+ // src/utils/preprocess-circuit-json.ts
31453
+ import { su as su5 } from "@tscircuit/circuit-json-util";
31454
+
31455
+ // src/utils/create-faux-board.ts
31456
+ import { su as su4, getBoundsOfPcbElements } from "@tscircuit/circuit-json-util";
31457
+ function createFauxBoard(circuitJson) {
31458
+ const cadComponents = su4(circuitJson).cad_component.list();
31459
+ const pads = su4(circuitJson).pcb_smtpad.list();
31460
+ const holes = su4(circuitJson).pcb_hole.list();
31461
+ const platedHoles = su4(circuitJson).pcb_plated_hole.list();
31462
+ const vias = su4(circuitJson).pcb_via.list();
31463
+ if (cadComponents.length === 0 && pads.length === 0 && holes.length === 0 && platedHoles.length === 0 && vias.length === 0) {
31464
+ return null;
31465
+ }
31466
+ const pcbElements = [...holes, ...platedHoles, ...vias, ...pads];
31467
+ const bounds = getBoundsOfPcbElements(pcbElements);
31468
+ let minX = bounds.minX;
31469
+ let maxX = bounds.maxX;
31470
+ let minY = bounds.minY;
31471
+ let maxY = bounds.maxY;
31472
+ for (const component of cadComponents) {
31473
+ if (component.position) {
31474
+ const x = component.position.x;
31475
+ const y = component.position.y;
31476
+ minX = Math.min(minX, x);
31477
+ maxX = Math.max(maxX, x);
31478
+ minY = Math.min(minY, y);
31479
+ maxY = Math.max(maxY, y);
31480
+ }
31481
+ }
31482
+ if (minX === Infinity) {
31483
+ minX = -10;
31484
+ maxX = 10;
31485
+ minY = -10;
31486
+ maxY = 10;
31487
+ }
31488
+ const padding = 2;
31489
+ const halfWidth = Math.max(Math.abs(minX), Math.abs(maxX)) + padding;
31490
+ const halfHeight = Math.max(Math.abs(minY), Math.abs(maxY)) + padding;
31491
+ const width10 = Math.max(2 * halfWidth, 10);
31492
+ const height10 = Math.max(2 * halfHeight, 10);
31493
+ const fauxBoard = {
31494
+ type: "pcb_board",
31495
+ pcb_board_id: "faux-board",
31496
+ center: { x: 0, y: 0 },
31497
+ // Always center at origin like real boards
31498
+ width: width10,
31499
+ height: height10,
31500
+ thickness: 1.6,
31501
+ // standard thickness
31502
+ material: "fr4",
31503
+ num_layers: 2
31504
+ // No outline - will use rectangular shape
31505
+ };
31506
+ return fauxBoard;
31507
+ }
31508
+
31509
+ // src/utils/preprocess-circuit-json.ts
31510
+ function addFauxBoardIfNeeded(circuitJson) {
31511
+ const boards = su5(circuitJson).pcb_board.list();
31512
+ if (boards.length > 0) {
31513
+ return circuitJson;
31514
+ }
31515
+ const fauxBoard = createFauxBoard(circuitJson);
31516
+ if (!fauxBoard) {
31517
+ return circuitJson;
31518
+ }
31519
+ const boardThickness = fauxBoard.thickness;
31520
+ const componentZ = boardThickness / 2;
31521
+ const processedCircuitJson = circuitJson.map((element) => {
31522
+ if (element.type === "cad_component") {
31523
+ const cadComponent = element;
31524
+ if (cadComponent.position) {
31525
+ return {
31526
+ ...cadComponent,
31527
+ position: {
31528
+ ...cadComponent.position,
31529
+ z: componentZ
31530
+ }
31531
+ };
31532
+ }
31533
+ }
31534
+ return element;
31535
+ });
31536
+ return [...processedCircuitJson, fauxBoard];
31537
+ }
31538
+
31452
31539
  // src/CadViewerJscad.tsx
31453
31540
  import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
31454
31541
  var CadViewerJscad = forwardRef3(
@@ -31464,13 +31551,13 @@ var CadViewerJscad = forwardRef3(
31464
31551
  const childrenSoup = useConvertChildrenToCircuitJson(children);
31465
31552
  const internalCircuitJson = useMemo19(() => {
31466
31553
  const cj = soup ?? circuitJson;
31467
- return cj ?? childrenSoup;
31554
+ return addFauxBoardIfNeeded(cj ?? childrenSoup);
31468
31555
  }, [soup, circuitJson, childrenSoup]);
31469
31556
  const boardGeom = useBoardGeomBuilder(internalCircuitJson);
31470
31557
  const initialCameraPosition = useMemo19(() => {
31471
31558
  if (!internalCircuitJson) return [5, -5, 5];
31472
31559
  try {
31473
- const board = su4(internalCircuitJson).pcb_board.list()[0];
31560
+ const board = su6(internalCircuitJson).pcb_board.list()[0];
31474
31561
  if (!board) return [5, -5, 5];
31475
31562
  const { width: width10, height: height10 } = board;
31476
31563
  if (!width10 && !height10) {
@@ -31493,10 +31580,19 @@ var CadViewerJscad = forwardRef3(
31493
31580
  return [5, -5, 5];
31494
31581
  }
31495
31582
  }, [internalCircuitJson]);
31583
+ const isFauxBoard = useMemo19(() => {
31584
+ if (!internalCircuitJson) return false;
31585
+ try {
31586
+ const board = su6(internalCircuitJson).pcb_board.list()[0];
31587
+ return !!board && board.pcb_board_id === "faux-board";
31588
+ } catch (e) {
31589
+ return false;
31590
+ }
31591
+ }, [internalCircuitJson]);
31496
31592
  const boardDimensions = useMemo19(() => {
31497
31593
  if (!internalCircuitJson) return void 0;
31498
31594
  try {
31499
- const board = su4(internalCircuitJson).pcb_board.list()[0];
31595
+ const board = su6(internalCircuitJson).pcb_board.list()[0];
31500
31596
  if (!board) return void 0;
31501
31597
  return { width: board.width ?? 0, height: board.height ?? 0 };
31502
31598
  } catch (e) {
@@ -31507,7 +31603,7 @@ var CadViewerJscad = forwardRef3(
31507
31603
  const boardCenter = useMemo19(() => {
31508
31604
  if (!internalCircuitJson) return void 0;
31509
31605
  try {
31510
- const board = su4(internalCircuitJson).pcb_board.list()[0];
31606
+ const board = su6(internalCircuitJson).pcb_board.list()[0];
31511
31607
  if (!board || !board.center) return void 0;
31512
31608
  return { x: board.center.x, y: board.center.y };
31513
31609
  } catch (e) {
@@ -31516,7 +31612,7 @@ var CadViewerJscad = forwardRef3(
31516
31612
  }
31517
31613
  }, [internalCircuitJson]);
31518
31614
  const { stls: boardStls, loading } = useStlsFromGeom(boardGeom);
31519
- const cad_components = su4(internalCircuitJson).cad_component.list();
31615
+ const cad_components = su6(internalCircuitJson).cad_component.list();
31520
31616
  return /* @__PURE__ */ jsxs6(
31521
31617
  CadViewerContainer,
31522
31618
  {
@@ -31534,7 +31630,7 @@ var CadViewerJscad = forwardRef3(
31534
31630
  {
31535
31631
  stlData,
31536
31632
  color,
31537
- opacity: index2 === 0 ? 0.95 : 1,
31633
+ opacity: index2 === 0 ? isFauxBoard ? 0.8 : 0.95 : 1,
31538
31634
  layerType
31539
31635
  },
31540
31636
  `board-${index2}`
@@ -31561,12 +31657,12 @@ var CadViewerJscad = forwardRef3(
31561
31657
  );
31562
31658
 
31563
31659
  // src/CadViewerManifold.tsx
31564
- import { su as su13 } from "@tscircuit/circuit-json-util";
31660
+ import { su as su15 } from "@tscircuit/circuit-json-util";
31565
31661
  import { useEffect as useEffect22, useMemo as useMemo21, useState as useState15 } from "react";
31566
31662
 
31567
31663
  // src/hooks/useManifoldBoardBuilder.ts
31568
31664
  import { useState as useState14, useEffect as useEffect21, useMemo as useMemo20, useRef as useRef8 } from "react";
31569
- import { su as su12 } from "@tscircuit/circuit-json-util";
31665
+ import { su as su14 } from "@tscircuit/circuit-json-util";
31570
31666
  import * as THREE24 from "three";
31571
31667
 
31572
31668
  // src/utils/manifold-mesh-to-three-geometry.ts
@@ -31592,7 +31688,7 @@ function manifoldMeshToThreeGeometry(manifoldMesh) {
31592
31688
 
31593
31689
  // src/utils/trace-texture.ts
31594
31690
  import * as THREE18 from "three";
31595
- import { su as su5 } from "@tscircuit/circuit-json-util";
31691
+ import { su as su7 } from "@tscircuit/circuit-json-util";
31596
31692
  function isWireRoutePoint(point2) {
31597
31693
  return point2 && point2.route_type === "wire" && typeof point2.layer === "string" && typeof point2.width === "number";
31598
31694
  }
@@ -31603,9 +31699,9 @@ function createTraceTextureForLayer({
31603
31699
  traceColor,
31604
31700
  traceTextureResolution
31605
31701
  }) {
31606
- const pcbTraces = su5(circuitJson).pcb_trace.list();
31607
- const allPcbVias = su5(circuitJson).pcb_via.list();
31608
- const allPcbPlatedHoles = su5(
31702
+ const pcbTraces = su7(circuitJson).pcb_trace.list();
31703
+ const allPcbVias = su7(circuitJson).pcb_via.list();
31704
+ const allPcbPlatedHoles = su7(
31609
31705
  circuitJson
31610
31706
  ).pcb_plated_hole.list();
31611
31707
  const tracesOnLayer = pcbTraces.filter(
@@ -31686,7 +31782,7 @@ function createTraceTextureForLayer({
31686
31782
  // src/utils/silkscreen-texture.ts
31687
31783
  var import_text2 = __toESM(require_text(), 1);
31688
31784
  import * as THREE19 from "three";
31689
- import { su as su6 } from "@tscircuit/circuit-json-util";
31785
+ import { su as su8 } from "@tscircuit/circuit-json-util";
31690
31786
  function createSilkscreenTextureForLayer({
31691
31787
  layer,
31692
31788
  circuitJson,
@@ -31694,11 +31790,11 @@ function createSilkscreenTextureForLayer({
31694
31790
  silkscreenColor = "rgb(255,255,255)",
31695
31791
  traceTextureResolution
31696
31792
  }) {
31697
- const pcbSilkscreenTexts = su6(circuitJson).pcb_silkscreen_text.list();
31698
- const pcbSilkscreenPaths = su6(circuitJson).pcb_silkscreen_path.list();
31699
- const pcbSilkscreenLines = su6(circuitJson).pcb_silkscreen_line.list();
31700
- const pcbSilkscreenRects = su6(circuitJson).pcb_silkscreen_rect.list();
31701
- const pcbSilkscreenCircles = su6(circuitJson).pcb_silkscreen_circle.list();
31793
+ const pcbSilkscreenTexts = su8(circuitJson).pcb_silkscreen_text.list();
31794
+ const pcbSilkscreenPaths = su8(circuitJson).pcb_silkscreen_path.list();
31795
+ const pcbSilkscreenLines = su8(circuitJson).pcb_silkscreen_line.list();
31796
+ const pcbSilkscreenRects = su8(circuitJson).pcb_silkscreen_rect.list();
31797
+ const pcbSilkscreenCircles = su8(circuitJson).pcb_silkscreen_circle.list();
31702
31798
  const textsOnLayer = pcbSilkscreenTexts.filter((t) => t.layer === layer);
31703
31799
  const pathsOnLayer = pcbSilkscreenPaths.filter((p) => p.layer === layer);
31704
31800
  const linesOnLayer = pcbSilkscreenLines.filter((l) => l.layer === layer);
@@ -31953,7 +32049,7 @@ function createSilkscreenTextureForLayer({
31953
32049
  }
31954
32050
 
31955
32051
  // src/utils/manifold/process-non-plated-holes.ts
31956
- import { su as su7 } from "@tscircuit/circuit-json-util";
32052
+ import { su as su9 } from "@tscircuit/circuit-json-util";
31957
32053
 
31958
32054
  // src/utils/hole-geoms.ts
31959
32055
  function createCircleHoleDrill({
@@ -32080,7 +32176,7 @@ function isRotatedPillHole(hole) {
32080
32176
  }
32081
32177
  function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
32082
32178
  const nonPlatedHoleBoardDrills = [];
32083
- const pcbHoles = su7(circuitJson).pcb_hole.list();
32179
+ const pcbHoles = su9(circuitJson).pcb_hole.list();
32084
32180
  const createPillOp = (width10, height10, depth) => {
32085
32181
  const pillOp = createRoundedRectPrism({
32086
32182
  Manifold,
@@ -32129,13 +32225,13 @@ function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, m
32129
32225
  }
32130
32226
 
32131
32227
  // src/utils/manifold/process-plated-holes.ts
32132
- import { su as su8 } from "@tscircuit/circuit-json-util";
32228
+ import { su as su10 } from "@tscircuit/circuit-json-util";
32133
32229
  import * as THREE20 from "three";
32134
32230
  var COPPER_COLOR = new THREE20.Color(...colors.copper);
32135
32231
  var PLATED_HOLE_LIP_HEIGHT = 0.05;
32136
32232
  function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
32137
32233
  const platedHoleBoardDrills = [];
32138
- const pcbPlatedHoles = su8(circuitJson).pcb_plated_hole.list();
32234
+ const pcbPlatedHoles = su10(circuitJson).pcb_plated_hole.list();
32139
32235
  const platedHoleCopperGeoms = [];
32140
32236
  const platedHoleCopperOpsForSubtract = [];
32141
32237
  const createPillOp = (width10, height10, depth) => {
@@ -32448,7 +32544,7 @@ function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, mani
32448
32544
  }
32449
32545
 
32450
32546
  // src/utils/manifold/process-vias.ts
32451
- import { su as su9 } from "@tscircuit/circuit-json-util";
32547
+ import { su as su11 } from "@tscircuit/circuit-json-util";
32452
32548
  import * as THREE21 from "three";
32453
32549
 
32454
32550
  // src/utils/via-geoms.ts
@@ -32485,7 +32581,7 @@ function createViaCopper({
32485
32581
  var COPPER_COLOR2 = new THREE21.Color(...colors.copper);
32486
32582
  function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, boardClipVolume) {
32487
32583
  const viaBoardDrills = [];
32488
- const pcbVias = su9(circuitJson).pcb_via.list();
32584
+ const pcbVias = su11(circuitJson).pcb_via.list();
32489
32585
  const viaCopperGeoms = [];
32490
32586
  pcbVias.forEach((via, index2) => {
32491
32587
  if (typeof via.outer_diameter === "number") {
@@ -32534,12 +32630,12 @@ function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldIns
32534
32630
  }
32535
32631
 
32536
32632
  // src/utils/manifold/process-smt-pads.ts
32537
- import { su as su10 } from "@tscircuit/circuit-json-util";
32633
+ import { su as su12 } from "@tscircuit/circuit-json-util";
32538
32634
  import * as THREE22 from "three";
32539
32635
  var COPPER_COLOR3 = new THREE22.Color(...colors.copper);
32540
32636
  function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup, holeUnion, boardClipVolume) {
32541
32637
  const smtPadGeoms = [];
32542
- const smtPads = su10(circuitJson).pcb_smtpad.list();
32638
+ const smtPads = su12(circuitJson).pcb_smtpad.list();
32543
32639
  smtPads.forEach((pad2, index2) => {
32544
32640
  const padBaseThickness = DEFAULT_SMT_PAD_THICKNESS;
32545
32641
  const zPos = pad2.layer === "bottom" ? -pcbThickness / 2 - BOARD_SURFACE_OFFSET.copper : pcbThickness / 2 + BOARD_SURFACE_OFFSET.copper;
@@ -32805,7 +32901,7 @@ function processCopperPoursForManifold(Manifold, CrossSection, circuitJson, pcbT
32805
32901
  }
32806
32902
 
32807
32903
  // src/utils/manifold/process-cutouts.ts
32808
- import { su as su11 } from "@tscircuit/circuit-json-util";
32904
+ import { su as su13 } from "@tscircuit/circuit-json-util";
32809
32905
  var arePointsClockwise5 = (points) => {
32810
32906
  let area = 0;
32811
32907
  for (let i = 0; i < points.length; i++) {
@@ -32820,7 +32916,7 @@ var arePointsClockwise5 = (points) => {
32820
32916
  };
32821
32917
  function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
32822
32918
  const cutoutOps = [];
32823
- const pcbCutouts = su11(circuitJson).pcb_cutout.list();
32919
+ const pcbCutouts = su13(circuitJson).pcb_cutout.list();
32824
32920
  for (const cutout of pcbCutouts) {
32825
32921
  let cutoutOp;
32826
32922
  const cutoutHeight = pcbThickness * 1.5;
@@ -32912,11 +33008,12 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
32912
33008
  const [isLoading, setIsLoading] = useState14(true);
32913
33009
  const manifoldInstancesForCleanup = useRef8([]);
32914
33010
  const boardData = useMemo20(() => {
32915
- const boards = su12(circuitJson).pcb_board.list();
32916
- if (boards.length === 0) {
32917
- return null;
32918
- }
32919
- return boards[0];
33011
+ const boards = su14(circuitJson).pcb_board.list();
33012
+ return boards.length > 0 ? boards[0] : null;
33013
+ }, [circuitJson]);
33014
+ const isFauxBoard = useMemo20(() => {
33015
+ const boards = su14(circuitJson).pcb_board.list();
33016
+ return boards.length > 0 && boards[0].pcb_board_id === "faux-board";
32920
33017
  }, [circuitJson]);
32921
33018
  useEffect21(() => {
32922
33019
  if (!manifoldJSModule || !boardData) {
@@ -33084,7 +33181,8 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
33084
33181
  matColorArray[1],
33085
33182
  matColorArray[2]
33086
33183
  ),
33087
- material: boardData.material
33184
+ material: boardData.material,
33185
+ isFaux: isFauxBoard
33088
33186
  };
33089
33187
  }
33090
33188
  const { smtPadGeoms } = processSmtPadsForManifold(
@@ -33161,7 +33259,8 @@ var useManifoldBoardBuilder = (manifoldJSModule, circuitJson) => {
33161
33259
  pcbThickness,
33162
33260
  error,
33163
33261
  isLoading,
33164
- boardData
33262
+ boardData,
33263
+ isFauxBoard
33165
33264
  };
33166
33265
  };
33167
33266
 
@@ -33174,7 +33273,8 @@ var DEFAULT_SIDE = THREE25.DoubleSide;
33174
33273
  var createBoardMaterial = ({
33175
33274
  material,
33176
33275
  color,
33177
- side = DEFAULT_SIDE
33276
+ side = DEFAULT_SIDE,
33277
+ isFaux = false
33178
33278
  }) => {
33179
33279
  if (material === "fr4") {
33180
33280
  return new THREE25.MeshPhysicalMaterial({
@@ -33186,8 +33286,8 @@ var createBoardMaterial = ({
33186
33286
  ior: 1.45,
33187
33287
  sheen: 0,
33188
33288
  clearcoat: 0,
33189
- transparent: false,
33190
- opacity: 1,
33289
+ transparent: isFaux,
33290
+ opacity: isFaux ? 0.6 : 1,
33191
33291
  flatShading: true
33192
33292
  });
33193
33293
  }
@@ -33198,7 +33298,7 @@ var createBoardMaterial = ({
33198
33298
  metalness: 0.1,
33199
33299
  roughness: 0.8,
33200
33300
  transparent: true,
33201
- opacity: 0.9
33301
+ opacity: isFaux ? 0.6 : 0.9
33202
33302
  });
33203
33303
  };
33204
33304
 
@@ -33212,7 +33312,8 @@ function createGeometryMeshes(geoms) {
33212
33312
  createBoardMaterial({
33213
33313
  material: geoms.board.material,
33214
33314
  color: geoms.board.color,
33215
- side: THREE26.DoubleSide
33315
+ side: THREE26.DoubleSide,
33316
+ isFaux: geoms.board.isFaux
33216
33317
  })
33217
33318
  );
33218
33319
  mesh.name = "board-geom";
@@ -33371,7 +33472,8 @@ var CadViewerManifold = ({
33371
33472
  }) => {
33372
33473
  const childrenCircuitJson = useConvertChildrenToCircuitJson(children);
33373
33474
  const circuitJson = useMemo21(() => {
33374
- return circuitJsonProp ?? childrenCircuitJson;
33475
+ const rawCircuitJson = circuitJsonProp ?? childrenCircuitJson;
33476
+ return addFauxBoardIfNeeded(rawCircuitJson);
33375
33477
  }, [circuitJsonProp, childrenCircuitJson]);
33376
33478
  const [manifoldJSModule, setManifoldJSModule] = useState15(null);
33377
33479
  const [manifoldLoadingError, setManifoldLoadingError] = useState15(null);
@@ -33451,7 +33553,7 @@ try {
33451
33553
  [textures, boardData, pcbThickness]
33452
33554
  );
33453
33555
  const cadComponents = useMemo21(
33454
- () => su13(circuitJson).cad_component.list(),
33556
+ () => su15(circuitJson).cad_component.list(),
33455
33557
  [circuitJson]
33456
33558
  );
33457
33559
  const boardDimensions = useMemo21(() => {
@@ -33663,9 +33765,16 @@ var useContextMenu = ({ containerRef }) => {
33663
33765
  }, []);
33664
33766
  const handleClickAway = useCallback8((e) => {
33665
33767
  const target = e.target;
33666
- if (menuRef.current && !menuRef.current.contains(target)) {
33667
- setMenuVisible(false);
33768
+ if (menuRef.current && menuRef.current.contains(target)) {
33769
+ return;
33770
+ }
33771
+ const isInRadixPortal = target.closest?.(
33772
+ "[data-radix-popper-content-wrapper], [data-radix-dropdown-menu-content], [data-radix-dropdown-menu-sub-content]"
33773
+ );
33774
+ if (isInRadixPortal) {
33775
+ return;
33668
33776
  }
33777
+ setMenuVisible(false);
33669
33778
  }, []);
33670
33779
  useEffect23(() => {
33671
33780
  if (menuVisible) {
@@ -39245,6 +39354,7 @@ var AppearanceMenu = () => {
39245
39354
  },
39246
39355
  onMouseEnter: () => setHoveredItem("appearance"),
39247
39356
  onMouseLeave: () => setHoveredItem(null),
39357
+ onTouchStart: () => setHoveredItem("appearance"),
39248
39358
  children: [
39249
39359
  /* @__PURE__ */ jsx32("span", { style: { flex: 1, display: "flex", alignItems: "center" }, children: "Appearance" }),
39250
39360
  /* @__PURE__ */ jsx32(
@@ -39283,6 +39393,7 @@ var AppearanceMenu = () => {
39283
39393
  },
39284
39394
  onMouseEnter: () => setHoveredItem("boardBody"),
39285
39395
  onMouseLeave: () => setHoveredItem(null),
39396
+ onTouchStart: () => setHoveredItem("boardBody"),
39286
39397
  children: [
39287
39398
  /* @__PURE__ */ jsx32("span", { style: iconContainerStyles, children: visibility.boardBody && /* @__PURE__ */ jsx32(CheckIcon, {}) }),
39288
39399
  /* @__PURE__ */ jsx32("span", { style: { display: "flex", alignItems: "center" }, children: "Board Body" })
@@ -39303,6 +39414,7 @@ var AppearanceMenu = () => {
39303
39414
  },
39304
39415
  onMouseEnter: () => setHoveredItem("topCopper"),
39305
39416
  onMouseLeave: () => setHoveredItem(null),
39417
+ onTouchStart: () => setHoveredItem("topCopper"),
39306
39418
  children: [
39307
39419
  /* @__PURE__ */ jsx32("span", { style: iconContainerStyles, children: visibility.topCopper && /* @__PURE__ */ jsx32(CheckIcon, {}) }),
39308
39420
  /* @__PURE__ */ jsx32("span", { style: { display: "flex", alignItems: "center" }, children: "Top Copper" })
@@ -39323,6 +39435,7 @@ var AppearanceMenu = () => {
39323
39435
  },
39324
39436
  onMouseEnter: () => setHoveredItem("bottomCopper"),
39325
39437
  onMouseLeave: () => setHoveredItem(null),
39438
+ onTouchStart: () => setHoveredItem("bottomCopper"),
39326
39439
  children: [
39327
39440
  /* @__PURE__ */ jsx32("span", { style: iconContainerStyles, children: visibility.bottomCopper && /* @__PURE__ */ jsx32(CheckIcon, {}) }),
39328
39441
  /* @__PURE__ */ jsx32("span", { style: { display: "flex", alignItems: "center" }, children: "Bottom Copper" })
@@ -39343,6 +39456,7 @@ var AppearanceMenu = () => {
39343
39456
  },
39344
39457
  onMouseEnter: () => setHoveredItem("topSilkscreen"),
39345
39458
  onMouseLeave: () => setHoveredItem(null),
39459
+ onTouchStart: () => setHoveredItem("topSilkscreen"),
39346
39460
  children: [
39347
39461
  /* @__PURE__ */ jsx32("span", { style: iconContainerStyles, children: visibility.topSilkscreen && /* @__PURE__ */ jsx32(CheckIcon, {}) }),
39348
39462
  /* @__PURE__ */ jsx32("span", { style: { display: "flex", alignItems: "center" }, children: "Top Silkscreen" })
@@ -39363,6 +39477,7 @@ var AppearanceMenu = () => {
39363
39477
  },
39364
39478
  onMouseEnter: () => setHoveredItem("bottomSilkscreen"),
39365
39479
  onMouseLeave: () => setHoveredItem(null),
39480
+ onTouchStart: () => setHoveredItem("bottomSilkscreen"),
39366
39481
  children: [
39367
39482
  /* @__PURE__ */ jsx32("span", { style: iconContainerStyles, children: visibility.bottomSilkscreen && /* @__PURE__ */ jsx32(CheckIcon, {}) }),
39368
39483
  /* @__PURE__ */ jsx32("span", { style: { display: "flex", alignItems: "center" }, children: "Bottom Silkscreen" })
@@ -39383,6 +39498,7 @@ var AppearanceMenu = () => {
39383
39498
  },
39384
39499
  onMouseEnter: () => setHoveredItem("smtModels"),
39385
39500
  onMouseLeave: () => setHoveredItem(null),
39501
+ onTouchStart: () => setHoveredItem("smtModels"),
39386
39502
  children: [
39387
39503
  /* @__PURE__ */ jsx32("span", { style: iconContainerStyles, children: visibility.smtModels && /* @__PURE__ */ jsx32(CheckIcon, {}) }),
39388
39504
  /* @__PURE__ */ jsx32("span", { style: { display: "flex", alignItems: "center" }, children: "CAD Models" })
@@ -39512,6 +39628,7 @@ var ContextMenu = ({
39512
39628
  },
39513
39629
  onMouseEnter: () => setHoveredItem("camera"),
39514
39630
  onMouseLeave: () => setHoveredItem(null),
39631
+ onTouchStart: () => setHoveredItem("camera"),
39515
39632
  children: [
39516
39633
  /* @__PURE__ */ jsx33(
39517
39634
  "span",
@@ -39558,6 +39675,7 @@ var ContextMenu = ({
39558
39675
  },
39559
39676
  onMouseEnter: () => setHoveredItem(option),
39560
39677
  onMouseLeave: () => setHoveredItem(null),
39678
+ onTouchStart: () => setHoveredItem(option),
39561
39679
  children: [
39562
39680
  /* @__PURE__ */ jsx33("span", { style: iconContainerStyles2, children: cameraPreset === option && /* @__PURE__ */ jsx33(DotIcon, {}) }),
39563
39681
  /* @__PURE__ */ jsx33("span", { style: { display: "flex", alignItems: "center" }, children: option })
@@ -39583,6 +39701,7 @@ var ContextMenu = ({
39583
39701
  },
39584
39702
  onMouseEnter: () => setHoveredItem("autorotate"),
39585
39703
  onMouseLeave: () => setHoveredItem(null),
39704
+ onTouchStart: () => setHoveredItem("autorotate"),
39586
39705
  children: [
39587
39706
  /* @__PURE__ */ jsx33("span", { style: iconContainerStyles2, children: autoRotate && /* @__PURE__ */ jsx33(CheckIcon, {}) }),
39588
39707
  /* @__PURE__ */ jsx33("span", { style: { display: "flex", alignItems: "center" }, children: "Auto rotate" })
@@ -39602,6 +39721,7 @@ var ContextMenu = ({
39602
39721
  onSelect: onDownloadGltf,
39603
39722
  onMouseEnter: () => setHoveredItem("download"),
39604
39723
  onMouseLeave: () => setHoveredItem(null),
39724
+ onTouchStart: () => setHoveredItem("download"),
39605
39725
  children: /* @__PURE__ */ jsx33("span", { style: { display: "flex", alignItems: "center" }, children: "Download GLTF" })
39606
39726
  }
39607
39727
  ),
@@ -39621,6 +39741,7 @@ var ContextMenu = ({
39621
39741
  },
39622
39742
  onMouseEnter: () => setHoveredItem("engine"),
39623
39743
  onMouseLeave: () => setHoveredItem(null),
39744
+ onTouchStart: () => setHoveredItem("engine"),
39624
39745
  children: [
39625
39746
  /* @__PURE__ */ jsxs9("span", { style: { flex: 1, display: "flex", alignItems: "center" }, children: [
39626
39747
  "Switch to ",
@@ -39853,7 +39974,7 @@ var CadViewer = (props) => {
39853
39974
 
39854
39975
  // src/convert-circuit-json-to-3d-svg.ts
39855
39976
  var import_debug = __toESM(require_browser(), 1);
39856
- import { su as su14 } from "@tscircuit/circuit-json-util";
39977
+ import { su as su16 } from "@tscircuit/circuit-json-util";
39857
39978
  import * as THREE31 from "three";
39858
39979
  import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
39859
39980
 
@@ -40082,11 +40203,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
40082
40203
  const pointLight = new THREE31.PointLight(16777215, Math.PI / 4);
40083
40204
  pointLight.position.set(-10, -10, 10);
40084
40205
  scene.add(pointLight);
40085
- const components = su14(circuitJson).cad_component.list();
40206
+ const components = su16(circuitJson).cad_component.list();
40086
40207
  for (const component of components) {
40087
40208
  await renderComponent(component, scene);
40088
40209
  }
40089
- const boardData = su14(circuitJson).pcb_board.list()[0];
40210
+ const boardData = su16(circuitJson).pcb_board.list()[0];
40090
40211
  const boardGeom = createBoardGeomFromCircuitJson(circuitJson);
40091
40212
  if (boardGeom) {
40092
40213
  for (const geom of boardGeom) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.432",
3
+ "version": "0.0.434",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",