@tscircuit/3d-viewer 0.0.633 → 0.0.635
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 +115 -60
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -20161,7 +20161,7 @@ var JscadModel = ({
|
|
|
20161
20161
|
);
|
|
20162
20162
|
};
|
|
20163
20163
|
|
|
20164
|
-
// src/hooks/use-global-
|
|
20164
|
+
// src/hooks/use-global-model-loader.ts
|
|
20165
20165
|
import { useEffect as useEffect10, useState as useState8 } from "react";
|
|
20166
20166
|
|
|
20167
20167
|
// src/utils/vrml.ts
|
|
@@ -20183,21 +20183,24 @@ async function loadVrml(url) {
|
|
|
20183
20183
|
return loader.parse(sanitizeVrmlIdentifiers(text), url);
|
|
20184
20184
|
}
|
|
20185
20185
|
|
|
20186
|
-
// src/hooks/use-global-
|
|
20187
|
-
import {
|
|
20188
|
-
|
|
20189
|
-
|
|
20186
|
+
// src/hooks/use-global-model-loader.ts
|
|
20187
|
+
import { Mesh as Mesh8, MeshStandardMaterial as MeshStandardMaterial4 } from "three";
|
|
20188
|
+
import { MTLLoader, OBJLoader, STLLoader } from "three-stdlib";
|
|
20189
|
+
if (typeof window !== "undefined" && !window.TSCIRCUIT_MODEL_LOADER_CACHE) {
|
|
20190
|
+
window.TSCIRCUIT_MODEL_LOADER_CACHE = /* @__PURE__ */ new Map();
|
|
20190
20191
|
}
|
|
20191
|
-
function
|
|
20192
|
+
function useGlobalModelLoader(url, modelType) {
|
|
20192
20193
|
const [obj, setObj] = useState8(null);
|
|
20193
20194
|
useEffect10(() => {
|
|
20194
20195
|
if (!url) return;
|
|
20195
20196
|
const cleanUrl = url.replace(/&cachebust_origin=$/, "");
|
|
20196
|
-
const
|
|
20197
|
+
const cachesByModelType = window.TSCIRCUIT_MODEL_LOADER_CACHE;
|
|
20198
|
+
const cache = cachesByModelType.get(modelType) ?? /* @__PURE__ */ new Map();
|
|
20199
|
+
cachesByModelType.set(modelType, cache);
|
|
20197
20200
|
let hasUrlChanged = false;
|
|
20198
|
-
async function
|
|
20201
|
+
async function loadAndParseModel() {
|
|
20199
20202
|
try {
|
|
20200
|
-
if (
|
|
20203
|
+
if (modelType === "wrl") {
|
|
20201
20204
|
return await loadVrml(cleanUrl);
|
|
20202
20205
|
}
|
|
20203
20206
|
const response = await fetch(cleanUrl);
|
|
@@ -20206,6 +20209,11 @@ function useGlobalObjLoader(url) {
|
|
|
20206
20209
|
`Failed to fetch "${cleanUrl}": ${response.status} ${response.statusText}`
|
|
20207
20210
|
);
|
|
20208
20211
|
}
|
|
20212
|
+
if (modelType === "stl") {
|
|
20213
|
+
const geometry = new STLLoader().parse(await response.arrayBuffer());
|
|
20214
|
+
const material = new MeshStandardMaterial4({ color: 8947848 });
|
|
20215
|
+
return new Mesh8(geometry, material);
|
|
20216
|
+
}
|
|
20209
20217
|
const text = await response.text();
|
|
20210
20218
|
const mtlContentArr = text.match(/newmtl[\s\S]*?endmtl/g);
|
|
20211
20219
|
const objLoader = new OBJLoader();
|
|
@@ -20236,7 +20244,7 @@ function useGlobalObjLoader(url) {
|
|
|
20236
20244
|
return result.clone();
|
|
20237
20245
|
});
|
|
20238
20246
|
}
|
|
20239
|
-
const promise =
|
|
20247
|
+
const promise = loadAndParseModel().then((result) => {
|
|
20240
20248
|
if (result instanceof Error) {
|
|
20241
20249
|
return result;
|
|
20242
20250
|
}
|
|
@@ -20255,7 +20263,7 @@ function useGlobalObjLoader(url) {
|
|
|
20255
20263
|
return () => {
|
|
20256
20264
|
hasUrlChanged = true;
|
|
20257
20265
|
};
|
|
20258
|
-
}, [url]);
|
|
20266
|
+
}, [url, modelType]);
|
|
20259
20267
|
return obj;
|
|
20260
20268
|
}
|
|
20261
20269
|
|
|
@@ -20265,6 +20273,7 @@ import * as THREE13 from "three";
|
|
|
20265
20273
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
20266
20274
|
function MixedStlModel({
|
|
20267
20275
|
url,
|
|
20276
|
+
modelType,
|
|
20268
20277
|
position,
|
|
20269
20278
|
rotation,
|
|
20270
20279
|
modelOffset = [0, 0, 0],
|
|
@@ -20278,7 +20287,7 @@ function MixedStlModel({
|
|
|
20278
20287
|
scale: scale11,
|
|
20279
20288
|
isTranslucent = false
|
|
20280
20289
|
}) {
|
|
20281
|
-
const obj =
|
|
20290
|
+
const obj = useGlobalModelLoader(url, modelType);
|
|
20282
20291
|
const model = useMemo8(() => {
|
|
20283
20292
|
if (obj && !(obj instanceof Error)) {
|
|
20284
20293
|
obj.traverse((child) => {
|
|
@@ -20352,11 +20361,11 @@ import {
|
|
|
20352
20361
|
Color as Color3,
|
|
20353
20362
|
Float32BufferAttribute,
|
|
20354
20363
|
Group as Group5,
|
|
20355
|
-
Mesh as
|
|
20356
|
-
MeshStandardMaterial as
|
|
20364
|
+
Mesh as Mesh10,
|
|
20365
|
+
MeshStandardMaterial as MeshStandardMaterial6
|
|
20357
20366
|
} from "three";
|
|
20358
20367
|
var DEFAULT_COLOR = new Color3(0.82, 0.82, 0.82);
|
|
20359
|
-
var createMaterial = (color) => new
|
|
20368
|
+
var createMaterial = (color) => new MeshStandardMaterial6({
|
|
20360
20369
|
color: color ? new Color3(color[0], color[1], color[2]) : DEFAULT_COLOR
|
|
20361
20370
|
});
|
|
20362
20371
|
function applyFaceColorGroups(geometry, mesh) {
|
|
@@ -20426,7 +20435,7 @@ function occtMeshesToGroup(meshes) {
|
|
|
20426
20435
|
geometry.setIndex(indices);
|
|
20427
20436
|
geometry.clearGroups();
|
|
20428
20437
|
const materials2 = applyFaceColorGroups(geometry, mesh);
|
|
20429
|
-
const threeMesh = new
|
|
20438
|
+
const threeMesh = new Mesh10(
|
|
20430
20439
|
geometry,
|
|
20431
20440
|
materials2.length > 1 ? materials2 : materials2[0]
|
|
20432
20441
|
);
|
|
@@ -21096,6 +21105,7 @@ var AnyCadComponent = ({
|
|
|
21096
21105
|
MixedStlModel,
|
|
21097
21106
|
{
|
|
21098
21107
|
url,
|
|
21108
|
+
modelType: meshModelType,
|
|
21099
21109
|
position: adjustedPosition,
|
|
21100
21110
|
rotation: rotationOffset,
|
|
21101
21111
|
modelOffset: modelTransform.modelPosition,
|
|
@@ -21111,7 +21121,7 @@ var AnyCadComponent = ({
|
|
|
21111
21121
|
isHovered,
|
|
21112
21122
|
isTranslucent
|
|
21113
21123
|
},
|
|
21114
|
-
`${cad_component.cad_component_id}-mixed-${url}`
|
|
21124
|
+
`${cad_component.cad_component_id}-mixed-${meshModelType}-${url}`
|
|
21115
21125
|
)
|
|
21116
21126
|
);
|
|
21117
21127
|
}
|
|
@@ -22051,7 +22061,7 @@ import * as THREE30 from "three";
|
|
|
22051
22061
|
// package.json
|
|
22052
22062
|
var package_default = {
|
|
22053
22063
|
name: "@tscircuit/3d-viewer",
|
|
22054
|
-
version: "0.0.
|
|
22064
|
+
version: "0.0.634",
|
|
22055
22065
|
repository: {
|
|
22056
22066
|
type: "git",
|
|
22057
22067
|
url: "https://github.com/tscircuit/3d-viewer"
|
|
@@ -22119,8 +22129,8 @@ var package_default = {
|
|
|
22119
22129
|
"@tscircuit/alphabet": "^0.0.25",
|
|
22120
22130
|
"@tscircuit/circuit-json-util": "^0.0.119",
|
|
22121
22131
|
"@tscircuit/copper-pour-solver": "^0.0.29",
|
|
22122
|
-
"@tscircuit/core": "^0.0.
|
|
22123
|
-
"@tscircuit/props": "^0.0.
|
|
22132
|
+
"@tscircuit/core": "^0.0.2195",
|
|
22133
|
+
"@tscircuit/props": "^0.0.707",
|
|
22124
22134
|
"@tscircuit/solver-utils": "^0.0.4",
|
|
22125
22135
|
"@types/debug": "^4.1.13",
|
|
22126
22136
|
"@types/jsdom": "^21.1.7",
|
|
@@ -24039,46 +24049,45 @@ var createPillPrism = (width, height, depth) => {
|
|
|
24039
24049
|
});
|
|
24040
24050
|
return (0, import_booleans.union)(rect, firstCap, secondCap);
|
|
24041
24051
|
};
|
|
24042
|
-
var createHoleWithPolygonPadHoleGeom = (hole,
|
|
24052
|
+
var createHoleWithPolygonPadHoleGeom = (hole, depth, options = {}) => {
|
|
24043
24053
|
const holeShape = hole.hole_shape || "circle";
|
|
24044
24054
|
const sizeDelta = options.sizeDelta ?? 0;
|
|
24045
24055
|
const offsetX = hole.hole_offset_x || 0;
|
|
24046
24056
|
const offsetY = hole.hole_offset_y || 0;
|
|
24047
|
-
|
|
24048
|
-
hole.x + offsetX,
|
|
24049
|
-
hole.y + offsetY,
|
|
24050
|
-
0
|
|
24051
|
-
];
|
|
24057
|
+
let drill;
|
|
24052
24058
|
if (holeShape === "circle") {
|
|
24053
24059
|
const diameter = Math.max((hole.hole_diameter ?? 0) + sizeDelta, M);
|
|
24054
|
-
const radius =
|
|
24055
|
-
|
|
24056
|
-
}
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24068
|
-
|
|
24069
|
-
|
|
24070
|
-
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
|
|
24060
|
+
const radius = diameter / 2;
|
|
24061
|
+
drill = (0, import_primitives3.cylinder)({ center: [0, 0, 0], radius, height: depth });
|
|
24062
|
+
} else {
|
|
24063
|
+
const baseWidth = hole.hole_width ?? hole.hole_diameter;
|
|
24064
|
+
const baseHeight = hole.hole_height ?? hole.hole_diameter;
|
|
24065
|
+
if (!baseWidth || !baseHeight) {
|
|
24066
|
+
return null;
|
|
24067
|
+
}
|
|
24068
|
+
const shapeWidth = Math.max(baseWidth + sizeDelta, M);
|
|
24069
|
+
const shapeHeight = Math.max(baseHeight + sizeDelta, M);
|
|
24070
|
+
if (holeShape === "oval") {
|
|
24071
|
+
drill = createEllipsePrism(shapeWidth, shapeHeight, depth);
|
|
24072
|
+
} else if (holeShape === "pill" || holeShape === "rotated_pill") {
|
|
24073
|
+
drill = createPillPrism(shapeWidth, shapeHeight, depth);
|
|
24074
|
+
} else {
|
|
24075
|
+
const fallbackDiameter = Math.max(
|
|
24076
|
+
(hole.hole_diameter ?? baseWidth) + sizeDelta,
|
|
24077
|
+
M
|
|
24078
|
+
);
|
|
24079
|
+
drill = (0, import_primitives3.cylinder)({
|
|
24080
|
+
center: [0, 0, 0],
|
|
24081
|
+
radius: fallbackDiameter / 2,
|
|
24082
|
+
height: depth
|
|
24083
|
+
});
|
|
24074
24084
|
}
|
|
24075
|
-
return (0, import_transforms2.translate)(center, pill);
|
|
24076
24085
|
}
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
|
|
24080
|
-
|
|
24081
|
-
return (0,
|
|
24086
|
+
drill = (0, import_transforms2.translate)([offsetX, offsetY, 0], drill);
|
|
24087
|
+
if (hole.ccw_rotation) {
|
|
24088
|
+
drill = (0, import_transforms2.rotateZ)(hole.ccw_rotation * Math.PI / 180, drill);
|
|
24089
|
+
}
|
|
24090
|
+
return (0, import_transforms2.translate)([hole.x, hole.y, 0], drill);
|
|
24082
24091
|
};
|
|
24083
24092
|
|
|
24084
24093
|
// src/geoms/plated-hole.ts
|
|
@@ -24567,7 +24576,13 @@ var platedHole = (plated_hole, ctx, options = {}) => {
|
|
|
24567
24576
|
const centerZ = 0;
|
|
24568
24577
|
const createPolygonPad = (thickness, zCenter) => {
|
|
24569
24578
|
const safeThickness = Math.max(thickness, M);
|
|
24570
|
-
|
|
24579
|
+
let extruded = (0, import_extrusions3.extrudeLinear)({ height: safeThickness }, polygon2d);
|
|
24580
|
+
if (plated_hole.ccw_rotation) {
|
|
24581
|
+
extruded = (0, import_transforms4.rotate)(
|
|
24582
|
+
[0, 0, plated_hole.ccw_rotation * Math.PI / 180],
|
|
24583
|
+
extruded
|
|
24584
|
+
);
|
|
24585
|
+
}
|
|
24571
24586
|
return (0, import_transforms4.translate)(
|
|
24572
24587
|
[plated_hole.x, plated_hole.y, zCenter - safeThickness / 2],
|
|
24573
24588
|
extruded
|
|
@@ -25655,6 +25670,7 @@ function clearViaHolesFromSurface({
|
|
|
25655
25670
|
}
|
|
25656
25671
|
|
|
25657
25672
|
// src/textures/create-combined-board-textures.ts
|
|
25673
|
+
import { CircuitToCanvasDrawer as CircuitToCanvasDrawer12 } from "circuit-to-canvas";
|
|
25658
25674
|
import * as THREE46 from "three";
|
|
25659
25675
|
|
|
25660
25676
|
// src/utils/pad-texture.ts
|
|
@@ -27163,7 +27179,8 @@ var createCombinedTexture = ({
|
|
|
27163
27179
|
boardData,
|
|
27164
27180
|
traceTextureResolution,
|
|
27165
27181
|
layer,
|
|
27166
|
-
openViaHoles
|
|
27182
|
+
openViaHoles,
|
|
27183
|
+
drillElements
|
|
27167
27184
|
}) => {
|
|
27168
27185
|
const hasImage = textures.some((texture) => texture?.image);
|
|
27169
27186
|
if (!hasImage) return null;
|
|
@@ -27185,6 +27202,30 @@ var createCombinedTexture = ({
|
|
|
27185
27202
|
const image = texture.image;
|
|
27186
27203
|
ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);
|
|
27187
27204
|
}
|
|
27205
|
+
if (drillElements.length > 0) {
|
|
27206
|
+
const apertureMask = document.createElement("canvas");
|
|
27207
|
+
apertureMask.width = canvasWidth;
|
|
27208
|
+
apertureMask.height = canvasHeight;
|
|
27209
|
+
const maskCtx = apertureMask.getContext("2d");
|
|
27210
|
+
if (maskCtx) {
|
|
27211
|
+
maskCtx.fillStyle = "black";
|
|
27212
|
+
maskCtx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
27213
|
+
if (layer === "bottom") {
|
|
27214
|
+
maskCtx.translate(0, canvasHeight);
|
|
27215
|
+
maskCtx.scale(1, -1);
|
|
27216
|
+
}
|
|
27217
|
+
const drawer = new CircuitToCanvasDrawer12(maskCtx);
|
|
27218
|
+
drawer.setCameraBounds(boardOutlineBounds);
|
|
27219
|
+
drawer.drawElements(drillElements, {
|
|
27220
|
+
layers: [layer === "top" ? "top_copper" : "bottom_copper"],
|
|
27221
|
+
clearDrillHoles: true
|
|
27222
|
+
});
|
|
27223
|
+
ctx.save();
|
|
27224
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
27225
|
+
ctx.drawImage(apertureMask, 0, 0, canvasWidth, canvasHeight);
|
|
27226
|
+
ctx.restore();
|
|
27227
|
+
}
|
|
27228
|
+
}
|
|
27188
27229
|
clearViaHolesFromSurface({
|
|
27189
27230
|
ctx,
|
|
27190
27231
|
vias: openViaHoles,
|
|
@@ -27254,6 +27295,9 @@ function createCombinedBoardTextures({
|
|
|
27254
27295
|
const silkscreenColor = "rgb(255,255,255)";
|
|
27255
27296
|
const copperColor = toRgb3(colors.copper);
|
|
27256
27297
|
const showBoardBody = visibility?.boardBody ?? true;
|
|
27298
|
+
const drillElements = circuitJson.filter(
|
|
27299
|
+
(element) => element.type === "pcb_hole" || element.type === "pcb_plated_hole" || element.type === "pcb_cutout"
|
|
27300
|
+
);
|
|
27257
27301
|
const openViaHoles = getViaHolesAtExposedPads(
|
|
27258
27302
|
circuitJson,
|
|
27259
27303
|
boardData,
|
|
@@ -27348,6 +27392,7 @@ function createCombinedBoardTextures({
|
|
|
27348
27392
|
}) : null;
|
|
27349
27393
|
const boardTexture = createCombinedTexture({
|
|
27350
27394
|
openViaHoles,
|
|
27395
|
+
drillElements,
|
|
27351
27396
|
textures: [
|
|
27352
27397
|
copperPourTexture,
|
|
27353
27398
|
traceTexture,
|
|
@@ -27976,7 +28021,7 @@ function JscadBoardTextures({
|
|
|
27976
28021
|
// src/three-components/STLModel.tsx
|
|
27977
28022
|
import { useState as useState18, useEffect as useEffect29, useMemo as useMemo22 } from "react";
|
|
27978
28023
|
import * as THREE50 from "three";
|
|
27979
|
-
import { STLLoader } from "three-stdlib";
|
|
28024
|
+
import { STLLoader as STLLoader2 } from "three-stdlib";
|
|
27980
28025
|
function STLModel({
|
|
27981
28026
|
stlUrl,
|
|
27982
28027
|
stlData,
|
|
@@ -27988,7 +28033,7 @@ function STLModel({
|
|
|
27988
28033
|
const { rootObject } = useThree();
|
|
27989
28034
|
const [geom, setGeom] = useState18(null);
|
|
27990
28035
|
useEffect29(() => {
|
|
27991
|
-
const loader = new
|
|
28036
|
+
const loader = new STLLoader2();
|
|
27992
28037
|
if (stlData) {
|
|
27993
28038
|
try {
|
|
27994
28039
|
const geometry = loader.parse(stlData);
|
|
@@ -30463,12 +30508,17 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
30463
30508
|
if (!Array.isArray(padOutline) || padOutline.length < 3) {
|
|
30464
30509
|
return;
|
|
30465
30510
|
}
|
|
30466
|
-
|
|
30511
|
+
let boardHoleOp = createHoleOpForPolygonPad({
|
|
30467
30512
|
ph,
|
|
30468
30513
|
depth: pcbThickness * 1.2,
|
|
30469
30514
|
sizeDelta: 2 * M
|
|
30470
30515
|
});
|
|
30471
30516
|
if (!boardHoleOp) return;
|
|
30517
|
+
if (ph.ccw_rotation) {
|
|
30518
|
+
const rotatedOp = boardHoleOp.rotate([0, 0, ph.ccw_rotation]);
|
|
30519
|
+
manifoldInstancesForCleanup.push(rotatedOp);
|
|
30520
|
+
boardHoleOp = rotatedOp;
|
|
30521
|
+
}
|
|
30472
30522
|
const translatedBoardHole = boardHoleOp.translate([ph.x, ph.y, 0]);
|
|
30473
30523
|
manifoldInstancesForCleanup.push(translatedBoardHole);
|
|
30474
30524
|
platedHoleBoardDrills.push(translatedBoardHole);
|
|
@@ -30494,8 +30544,13 @@ function processPlatedHolesForManifold(Manifold, CrossSection, circuitJson, pcbT
|
|
|
30494
30544
|
}) || barrelOp;
|
|
30495
30545
|
const copperUnion = Manifold.union([mainFill, barrelOp]);
|
|
30496
30546
|
manifoldInstancesForCleanup.push(copperUnion);
|
|
30497
|
-
|
|
30547
|
+
let finalCopper = copperUnion.subtract(holeCutOp);
|
|
30498
30548
|
manifoldInstancesForCleanup.push(finalCopper);
|
|
30549
|
+
if (ph.ccw_rotation) {
|
|
30550
|
+
const rotatedOp = finalCopper.rotate([0, 0, ph.ccw_rotation]);
|
|
30551
|
+
manifoldInstancesForCleanup.push(rotatedOp);
|
|
30552
|
+
finalCopper = rotatedOp;
|
|
30553
|
+
}
|
|
30499
30554
|
const translatedCopper = finalCopper.translate([ph.x, ph.y, 0]);
|
|
30500
30555
|
manifoldInstancesForCleanup.push(translatedCopper);
|
|
30501
30556
|
let finalCopperOp = translatedCopper;
|
|
@@ -40411,10 +40466,10 @@ import * as THREE65 from "three";
|
|
|
40411
40466
|
import * as THREE64 from "three";
|
|
40412
40467
|
import { GLTFLoader as GLTFLoader3 } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
40413
40468
|
import { OBJLoader as OBJLoader2 } from "three/examples/jsm/loaders/OBJLoader.js";
|
|
40414
|
-
import { STLLoader as
|
|
40469
|
+
import { STLLoader as STLLoader3 } from "three/examples/jsm/loaders/STLLoader.js";
|
|
40415
40470
|
async function load3DModel(url) {
|
|
40416
40471
|
if (url.endsWith(".stl")) {
|
|
40417
|
-
const loader = new
|
|
40472
|
+
const loader = new STLLoader3();
|
|
40418
40473
|
const geometry = await loader.loadAsync(url);
|
|
40419
40474
|
const material = new THREE64.MeshStandardMaterial({
|
|
40420
40475
|
color: 8947848,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/3d-viewer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.635",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/tscircuit/3d-viewer"
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"@tscircuit/alphabet": "^0.0.25",
|
|
69
69
|
"@tscircuit/circuit-json-util": "^0.0.119",
|
|
70
70
|
"@tscircuit/copper-pour-solver": "^0.0.29",
|
|
71
|
-
"@tscircuit/core": "^0.0.
|
|
72
|
-
"@tscircuit/props": "^0.0.
|
|
71
|
+
"@tscircuit/core": "^0.0.2195",
|
|
72
|
+
"@tscircuit/props": "^0.0.707",
|
|
73
73
|
"@tscircuit/solver-utils": "^0.0.4",
|
|
74
74
|
"@types/debug": "^4.1.13",
|
|
75
75
|
"@types/jsdom": "^21.1.7",
|