@tscircuit/3d-viewer 0.0.438 → 0.0.440
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 +439 -57
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -14228,7 +14228,7 @@ var require_browser = __commonJS({
|
|
|
14228
14228
|
});
|
|
14229
14229
|
|
|
14230
14230
|
// src/CadViewer.tsx
|
|
14231
|
-
import { useState as
|
|
14231
|
+
import { useState as useState36, useCallback as useCallback22, useRef as useRef26, useEffect as useEffect42, useMemo as useMemo28 } from "react";
|
|
14232
14232
|
import * as THREE29 from "three";
|
|
14233
14233
|
|
|
14234
14234
|
// src/CadViewerJscad.tsx
|
|
@@ -16957,7 +16957,8 @@ var pcb_cutout_rect = pcb_cutout_base.extend({
|
|
|
16957
16957
|
center: point,
|
|
16958
16958
|
width: length,
|
|
16959
16959
|
height: length,
|
|
16960
|
-
rotation: rotation.optional()
|
|
16960
|
+
rotation: rotation.optional(),
|
|
16961
|
+
corner_radius: length.optional()
|
|
16961
16962
|
});
|
|
16962
16963
|
expectTypesMatch(true);
|
|
16963
16964
|
var pcb_cutout_circle = pcb_cutout_base.extend({
|
|
@@ -28287,7 +28288,7 @@ import * as THREE15 from "three";
|
|
|
28287
28288
|
// package.json
|
|
28288
28289
|
var package_default = {
|
|
28289
28290
|
name: "@tscircuit/3d-viewer",
|
|
28290
|
-
version: "0.0.
|
|
28291
|
+
version: "0.0.439",
|
|
28291
28292
|
main: "./dist/index.js",
|
|
28292
28293
|
module: "./dist/index.js",
|
|
28293
28294
|
type: "module",
|
|
@@ -28348,7 +28349,7 @@ var package_default = {
|
|
|
28348
28349
|
"@vitejs/plugin-react": "^4.3.4",
|
|
28349
28350
|
"bun-match-svg": "^0.0.9",
|
|
28350
28351
|
"bun-types": "1.2.1",
|
|
28351
|
-
"circuit-json": "0.0.
|
|
28352
|
+
"circuit-json": "0.0.317",
|
|
28352
28353
|
"circuit-to-svg": "^0.0.179",
|
|
28353
28354
|
debug: "^4.4.0",
|
|
28354
28355
|
"jscad-electronics": "^0.0.89",
|
|
@@ -30986,10 +30987,33 @@ var BoardGeomBuilder = class {
|
|
|
30986
30987
|
const cutoutHeight = this.ctx.pcbThickness * 1.5;
|
|
30987
30988
|
switch (cutout.shape) {
|
|
30988
30989
|
case "rect":
|
|
30989
|
-
|
|
30990
|
-
|
|
30991
|
-
|
|
30992
|
-
|
|
30990
|
+
const rectCornerRadius = clampRectBorderRadius(
|
|
30991
|
+
cutout.width,
|
|
30992
|
+
cutout.height,
|
|
30993
|
+
extractRectBorderRadius(cutout)
|
|
30994
|
+
);
|
|
30995
|
+
if (rectCornerRadius > 0) {
|
|
30996
|
+
const rect2d = (0, import_primitives10.roundedRectangle)({
|
|
30997
|
+
size: [cutout.width, cutout.height],
|
|
30998
|
+
roundRadius: rectCornerRadius,
|
|
30999
|
+
segments: PAD_ROUNDED_SEGMENTS
|
|
31000
|
+
});
|
|
31001
|
+
cutoutGeom = (0, import_extrusions8.extrudeLinear)({ height: cutoutHeight }, rect2d);
|
|
31002
|
+
cutoutGeom = (0, import_transforms8.translate)([0, 0, -cutoutHeight / 2], cutoutGeom);
|
|
31003
|
+
cutoutGeom = (0, import_transforms8.translate)(
|
|
31004
|
+
[cutout.center.x, cutout.center.y, 0],
|
|
31005
|
+
cutoutGeom
|
|
31006
|
+
);
|
|
31007
|
+
} else {
|
|
31008
|
+
const baseCutoutGeom = (0, import_primitives10.cuboid)({
|
|
31009
|
+
center: [0, 0, 0],
|
|
31010
|
+
size: [cutout.width, cutout.height, cutoutHeight]
|
|
31011
|
+
});
|
|
31012
|
+
cutoutGeom = (0, import_transforms8.translate)(
|
|
31013
|
+
[cutout.center.x, cutout.center.y, 0],
|
|
31014
|
+
baseCutoutGeom
|
|
31015
|
+
);
|
|
31016
|
+
}
|
|
30993
31017
|
if (cutout.rotation) {
|
|
30994
31018
|
const rotationRadians = cutout.rotation * Math.PI / 180;
|
|
30995
31019
|
cutoutGeom = (0, import_transforms8.rotateZ)(rotationRadians, cutoutGeom);
|
|
@@ -33492,15 +33516,25 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
|
|
|
33492
33516
|
let cutoutOp;
|
|
33493
33517
|
const cutoutHeight = pcbThickness * 1.5;
|
|
33494
33518
|
switch (cutout.shape) {
|
|
33495
|
-
case "rect":
|
|
33496
|
-
|
|
33497
|
-
|
|
33498
|
-
|
|
33499
|
-
|
|
33500
|
-
|
|
33519
|
+
case "rect": {
|
|
33520
|
+
const rectCornerRadius = extractRectBorderRadius(cutout);
|
|
33521
|
+
if (typeof rectCornerRadius === "number" && rectCornerRadius > 0) {
|
|
33522
|
+
cutoutOp = createRoundedRectPrism({
|
|
33523
|
+
Manifold,
|
|
33524
|
+
width: cutout.width,
|
|
33525
|
+
height: cutout.height,
|
|
33526
|
+
thickness: cutoutHeight,
|
|
33527
|
+
borderRadius: rectCornerRadius
|
|
33528
|
+
});
|
|
33529
|
+
} else {
|
|
33530
|
+
cutoutOp = Manifold.cube(
|
|
33531
|
+
[cutout.width, cutout.height, cutoutHeight],
|
|
33532
|
+
true
|
|
33533
|
+
// centered
|
|
33534
|
+
);
|
|
33535
|
+
}
|
|
33501
33536
|
manifoldInstancesForCleanup.push(cutoutOp);
|
|
33502
33537
|
if (cutout.rotation) {
|
|
33503
|
-
const rotationRadians = cutout.rotation * Math.PI / 180;
|
|
33504
33538
|
const rotatedOp = cutoutOp.rotate([0, 0, cutout.rotation]);
|
|
33505
33539
|
manifoldInstancesForCleanup.push(rotatedOp);
|
|
33506
33540
|
cutoutOp = rotatedOp;
|
|
@@ -33513,6 +33547,7 @@ function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThick
|
|
|
33513
33547
|
]);
|
|
33514
33548
|
manifoldInstancesForCleanup.push(cutoutOp);
|
|
33515
33549
|
break;
|
|
33550
|
+
}
|
|
33516
33551
|
case "circle":
|
|
33517
33552
|
cutoutOp = Manifold.cylinder(
|
|
33518
33553
|
cutoutHeight,
|
|
@@ -34468,8 +34503,110 @@ var useGlobalDownloadGltf = () => {
|
|
|
34468
34503
|
}, []);
|
|
34469
34504
|
};
|
|
34470
34505
|
|
|
34506
|
+
// src/hooks/useRegisteredHotkey.ts
|
|
34507
|
+
import { useEffect as useEffect25, useMemo as useMemo21, useRef as useRef11, useState as useState18 } from "react";
|
|
34508
|
+
var hotkeyRegistry = /* @__PURE__ */ new Map();
|
|
34509
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
34510
|
+
var isListenerAttached = false;
|
|
34511
|
+
var matchesKey = (eventKey, targetKey) => {
|
|
34512
|
+
if (!eventKey || !targetKey) return false;
|
|
34513
|
+
return eventKey.toLowerCase() === targetKey.toLowerCase();
|
|
34514
|
+
};
|
|
34515
|
+
var matchesModifiers = (event, modifiers) => {
|
|
34516
|
+
if (!modifiers || modifiers.length === 0) {
|
|
34517
|
+
return !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey;
|
|
34518
|
+
}
|
|
34519
|
+
const hasCtrl = modifiers.includes("Ctrl");
|
|
34520
|
+
const hasCmd = modifiers.includes("Cmd");
|
|
34521
|
+
const hasShift = modifiers.includes("Shift");
|
|
34522
|
+
const hasAlt = modifiers.includes("Alt");
|
|
34523
|
+
if (hasCtrl && !event.ctrlKey) return false;
|
|
34524
|
+
if (hasCmd && !event.metaKey) return false;
|
|
34525
|
+
if (hasShift && !event.shiftKey) return false;
|
|
34526
|
+
if (hasAlt && !event.altKey) return false;
|
|
34527
|
+
return true;
|
|
34528
|
+
};
|
|
34529
|
+
var isEditableTarget = (target) => {
|
|
34530
|
+
if (!target || typeof target !== "object") return false;
|
|
34531
|
+
const element = target;
|
|
34532
|
+
const tagName = element.tagName;
|
|
34533
|
+
const editableTags = ["INPUT", "TEXTAREA", "SELECT"];
|
|
34534
|
+
if (editableTags.includes(tagName)) {
|
|
34535
|
+
return true;
|
|
34536
|
+
}
|
|
34537
|
+
return Boolean(element.getAttribute?.("contenteditable"));
|
|
34538
|
+
};
|
|
34539
|
+
var handleKeydown = (event) => {
|
|
34540
|
+
if (isEditableTarget(event.target)) {
|
|
34541
|
+
return;
|
|
34542
|
+
}
|
|
34543
|
+
hotkeyRegistry.forEach((entry) => {
|
|
34544
|
+
if (matchesKey(event.key, entry.key) && matchesModifiers(event, entry.modifiers)) {
|
|
34545
|
+
event.preventDefault();
|
|
34546
|
+
entry.invoke();
|
|
34547
|
+
}
|
|
34548
|
+
});
|
|
34549
|
+
};
|
|
34550
|
+
var notifySubscribers = () => {
|
|
34551
|
+
const entries = Array.from(hotkeyRegistry.values());
|
|
34552
|
+
subscribers.forEach((subscriber) => subscriber(entries));
|
|
34553
|
+
};
|
|
34554
|
+
var ensureListener = () => {
|
|
34555
|
+
if (isListenerAttached) return;
|
|
34556
|
+
if (typeof window === "undefined") return;
|
|
34557
|
+
window.addEventListener("keydown", handleKeydown);
|
|
34558
|
+
isListenerAttached = true;
|
|
34559
|
+
};
|
|
34560
|
+
var registerHotkey = (registration) => {
|
|
34561
|
+
hotkeyRegistry.set(registration.id, registration);
|
|
34562
|
+
notifySubscribers();
|
|
34563
|
+
ensureListener();
|
|
34564
|
+
};
|
|
34565
|
+
var unregisterHotkey = (id) => {
|
|
34566
|
+
if (hotkeyRegistry.delete(id)) {
|
|
34567
|
+
notifySubscribers();
|
|
34568
|
+
}
|
|
34569
|
+
};
|
|
34570
|
+
var subscribeToRegistry = (subscriber) => {
|
|
34571
|
+
subscribers.add(subscriber);
|
|
34572
|
+
subscriber(Array.from(hotkeyRegistry.values()));
|
|
34573
|
+
return () => {
|
|
34574
|
+
subscribers.delete(subscriber);
|
|
34575
|
+
};
|
|
34576
|
+
};
|
|
34577
|
+
var useRegisteredHotkey = (id, handler, metadata) => {
|
|
34578
|
+
const handlerRef = useRef11(handler);
|
|
34579
|
+
handlerRef.current = handler;
|
|
34580
|
+
const normalizedMetadata = useMemo21(
|
|
34581
|
+
() => ({
|
|
34582
|
+
key: metadata.key,
|
|
34583
|
+
description: metadata.description,
|
|
34584
|
+
modifiers: metadata.modifiers
|
|
34585
|
+
}),
|
|
34586
|
+
[metadata.key, metadata.description, metadata.modifiers]
|
|
34587
|
+
);
|
|
34588
|
+
useEffect25(() => {
|
|
34589
|
+
const registration = {
|
|
34590
|
+
id,
|
|
34591
|
+
...normalizedMetadata,
|
|
34592
|
+
invoke: () => handlerRef.current()
|
|
34593
|
+
};
|
|
34594
|
+
registerHotkey(registration);
|
|
34595
|
+
return () => {
|
|
34596
|
+
unregisterHotkey(id);
|
|
34597
|
+
};
|
|
34598
|
+
}, [id, normalizedMetadata]);
|
|
34599
|
+
};
|
|
34600
|
+
var useHotkeyRegistry = () => {
|
|
34601
|
+
const [entries, setEntries] = useState18(
|
|
34602
|
+
() => Array.from(hotkeyRegistry.values())
|
|
34603
|
+
);
|
|
34604
|
+
useEffect25(() => subscribeToRegistry(setEntries), []);
|
|
34605
|
+
return entries;
|
|
34606
|
+
};
|
|
34607
|
+
|
|
34471
34608
|
// src/components/ContextMenu.tsx
|
|
34472
|
-
import { useState as
|
|
34609
|
+
import { useState as useState34 } from "react";
|
|
34473
34610
|
|
|
34474
34611
|
// node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs
|
|
34475
34612
|
import * as React43 from "react";
|
|
@@ -38097,9 +38234,9 @@ function assignRef(ref, value) {
|
|
|
38097
38234
|
}
|
|
38098
38235
|
|
|
38099
38236
|
// node_modules/use-callback-ref/dist/es2015/useRef.js
|
|
38100
|
-
import { useState as
|
|
38237
|
+
import { useState as useState29 } from "react";
|
|
38101
38238
|
function useCallbackRef2(initialValue, callback) {
|
|
38102
|
-
var ref =
|
|
38239
|
+
var ref = useState29(function() {
|
|
38103
38240
|
return {
|
|
38104
38241
|
// value
|
|
38105
38242
|
value: initialValue,
|
|
@@ -39823,7 +39960,7 @@ var SubTrigger2 = DropdownMenuSubTrigger;
|
|
|
39823
39960
|
var SubContent2 = DropdownMenuSubContent;
|
|
39824
39961
|
|
|
39825
39962
|
// src/components/AppearanceMenu.tsx
|
|
39826
|
-
import { useState as
|
|
39963
|
+
import { useState as useState33 } from "react";
|
|
39827
39964
|
|
|
39828
39965
|
// src/components/Icons.tsx
|
|
39829
39966
|
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
@@ -39929,8 +40066,8 @@ var iconContainerStyles = {
|
|
|
39929
40066
|
};
|
|
39930
40067
|
var AppearanceMenu = () => {
|
|
39931
40068
|
const { visibility, toggleLayer } = useLayerVisibility();
|
|
39932
|
-
const [appearanceSubOpen, setAppearanceSubOpen] =
|
|
39933
|
-
const [hoveredItem, setHoveredItem] =
|
|
40069
|
+
const [appearanceSubOpen, setAppearanceSubOpen] = useState33(false);
|
|
40070
|
+
const [hoveredItem, setHoveredItem] = useState33(null);
|
|
39934
40071
|
return /* @__PURE__ */ jsxs7(Fragment9, { children: [
|
|
39935
40072
|
/* @__PURE__ */ jsx33(Separator2, { style: separatorStyles }),
|
|
39936
40073
|
/* @__PURE__ */ jsxs7(Sub2, { onOpenChange: setAppearanceSubOpen, children: [
|
|
@@ -40181,11 +40318,12 @@ var ContextMenu = ({
|
|
|
40181
40318
|
onEngineSwitch,
|
|
40182
40319
|
onCameraPresetSelect,
|
|
40183
40320
|
onAutoRotateToggle,
|
|
40184
|
-
onDownloadGltf
|
|
40321
|
+
onDownloadGltf,
|
|
40322
|
+
onOpenKeyboardShortcuts
|
|
40185
40323
|
}) => {
|
|
40186
40324
|
const { cameraType, setCameraType } = useCameraController();
|
|
40187
|
-
const [cameraSubOpen, setCameraSubOpen] =
|
|
40188
|
-
const [hoveredItem, setHoveredItem] =
|
|
40325
|
+
const [cameraSubOpen, setCameraSubOpen] = useState34(false);
|
|
40326
|
+
const [hoveredItem, setHoveredItem] = useState34(null);
|
|
40189
40327
|
return /* @__PURE__ */ jsx34(
|
|
40190
40328
|
"div",
|
|
40191
40329
|
{
|
|
@@ -40378,6 +40516,35 @@ var ContextMenu = ({
|
|
|
40378
40516
|
}
|
|
40379
40517
|
),
|
|
40380
40518
|
/* @__PURE__ */ jsx34(Separator2, { style: separatorStyles2 }),
|
|
40519
|
+
/* @__PURE__ */ jsxs8(
|
|
40520
|
+
Item22,
|
|
40521
|
+
{
|
|
40522
|
+
style: {
|
|
40523
|
+
...itemStyles2,
|
|
40524
|
+
...itemPaddingStyles2,
|
|
40525
|
+
backgroundColor: hoveredItem === "shortcuts" ? "#404040" : "transparent"
|
|
40526
|
+
},
|
|
40527
|
+
onSelect: onOpenKeyboardShortcuts,
|
|
40528
|
+
onMouseEnter: () => setHoveredItem("shortcuts"),
|
|
40529
|
+
onMouseLeave: () => setHoveredItem(null),
|
|
40530
|
+
onTouchStart: () => setHoveredItem("shortcuts"),
|
|
40531
|
+
children: [
|
|
40532
|
+
/* @__PURE__ */ jsx34("span", { style: { flex: 1, display: "flex", alignItems: "center" }, children: "Keyboard Shortcuts" }),
|
|
40533
|
+
/* @__PURE__ */ jsx34(
|
|
40534
|
+
"div",
|
|
40535
|
+
{
|
|
40536
|
+
style: {
|
|
40537
|
+
...badgeStyles,
|
|
40538
|
+
display: "flex",
|
|
40539
|
+
alignItems: "center"
|
|
40540
|
+
},
|
|
40541
|
+
children: "Shift+?"
|
|
40542
|
+
}
|
|
40543
|
+
)
|
|
40544
|
+
]
|
|
40545
|
+
}
|
|
40546
|
+
),
|
|
40547
|
+
/* @__PURE__ */ jsx34(Separator2, { style: separatorStyles2 }),
|
|
40381
40548
|
/* @__PURE__ */ jsx34(
|
|
40382
40549
|
"div",
|
|
40383
40550
|
{
|
|
@@ -40415,23 +40582,216 @@ var ContextMenu = ({
|
|
|
40415
40582
|
);
|
|
40416
40583
|
};
|
|
40417
40584
|
|
|
40585
|
+
// src/components/KeyboardShortcutsDialog.tsx
|
|
40586
|
+
import { useEffect as useEffect41, useMemo as useMemo27, useRef as useRef25, useState as useState35 } from "react";
|
|
40587
|
+
import { Fragment as Fragment10, jsx as jsx35, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
40588
|
+
var KeyboardShortcutsDialog = ({
|
|
40589
|
+
open,
|
|
40590
|
+
onClose
|
|
40591
|
+
}) => {
|
|
40592
|
+
const [query, setQuery] = useState35("");
|
|
40593
|
+
const inputRef = useRef25(null);
|
|
40594
|
+
const hotkeys = useHotkeyRegistry();
|
|
40595
|
+
useEffect41(() => {
|
|
40596
|
+
if (!open) return void 0;
|
|
40597
|
+
const handleKeyDown = (event) => {
|
|
40598
|
+
if (event.key === "Escape") {
|
|
40599
|
+
event.preventDefault();
|
|
40600
|
+
onClose();
|
|
40601
|
+
}
|
|
40602
|
+
};
|
|
40603
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
40604
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
40605
|
+
}, [open, onClose]);
|
|
40606
|
+
useEffect41(() => {
|
|
40607
|
+
if (open) {
|
|
40608
|
+
setTimeout(() => {
|
|
40609
|
+
inputRef.current?.focus();
|
|
40610
|
+
}, 0);
|
|
40611
|
+
}
|
|
40612
|
+
}, [open]);
|
|
40613
|
+
const filteredHotkeys = useMemo27(() => {
|
|
40614
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
40615
|
+
if (!normalizedQuery) {
|
|
40616
|
+
return hotkeys;
|
|
40617
|
+
}
|
|
40618
|
+
return hotkeys.filter((hotkey) => {
|
|
40619
|
+
const haystack = `${hotkey.key} ${hotkey.description}`;
|
|
40620
|
+
return haystack.toLowerCase().includes(normalizedQuery);
|
|
40621
|
+
});
|
|
40622
|
+
}, [hotkeys, query]);
|
|
40623
|
+
if (!open) {
|
|
40624
|
+
return null;
|
|
40625
|
+
}
|
|
40626
|
+
return /* @__PURE__ */ jsx35(
|
|
40627
|
+
"div",
|
|
40628
|
+
{
|
|
40629
|
+
role: "dialog",
|
|
40630
|
+
"aria-modal": "true",
|
|
40631
|
+
style: {
|
|
40632
|
+
position: "fixed",
|
|
40633
|
+
inset: 0,
|
|
40634
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
40635
|
+
display: "flex",
|
|
40636
|
+
alignItems: "center",
|
|
40637
|
+
justifyContent: "center",
|
|
40638
|
+
zIndex: 9999
|
|
40639
|
+
},
|
|
40640
|
+
onClick: onClose,
|
|
40641
|
+
children: /* @__PURE__ */ jsxs9(
|
|
40642
|
+
"div",
|
|
40643
|
+
{
|
|
40644
|
+
style: {
|
|
40645
|
+
backgroundColor: "#1f1f23",
|
|
40646
|
+
color: "#f8f8ff",
|
|
40647
|
+
borderRadius: 12,
|
|
40648
|
+
width: "min(640px, 90vw)",
|
|
40649
|
+
maxHeight: "80vh",
|
|
40650
|
+
boxShadow: "0 20px 60px rgba(0, 0, 0, 0.45), 0 8px 20px rgba(0, 0, 0, 0.35)",
|
|
40651
|
+
display: "flex",
|
|
40652
|
+
flexDirection: "column",
|
|
40653
|
+
overflow: "hidden"
|
|
40654
|
+
},
|
|
40655
|
+
onClick: (event) => event.stopPropagation(),
|
|
40656
|
+
children: [
|
|
40657
|
+
/* @__PURE__ */ jsxs9(
|
|
40658
|
+
"header",
|
|
40659
|
+
{
|
|
40660
|
+
style: {
|
|
40661
|
+
padding: "20px 24px 12px",
|
|
40662
|
+
borderBottom: "1px solid rgba(255, 255, 255, 0.08)"
|
|
40663
|
+
},
|
|
40664
|
+
children: [
|
|
40665
|
+
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
|
|
40666
|
+
/* @__PURE__ */ jsx35(
|
|
40667
|
+
"h2",
|
|
40668
|
+
{
|
|
40669
|
+
style: {
|
|
40670
|
+
margin: 0,
|
|
40671
|
+
fontSize: "1.1rem",
|
|
40672
|
+
fontWeight: 600,
|
|
40673
|
+
letterSpacing: "0.02em"
|
|
40674
|
+
},
|
|
40675
|
+
children: "Keyboard Shortcuts"
|
|
40676
|
+
}
|
|
40677
|
+
),
|
|
40678
|
+
/* @__PURE__ */ jsx35(
|
|
40679
|
+
"button",
|
|
40680
|
+
{
|
|
40681
|
+
type: "button",
|
|
40682
|
+
onClick: onClose,
|
|
40683
|
+
style: {
|
|
40684
|
+
background: "transparent",
|
|
40685
|
+
border: "none",
|
|
40686
|
+
color: "rgba(255, 255, 255, 0.8)",
|
|
40687
|
+
fontSize: "1rem",
|
|
40688
|
+
cursor: "pointer"
|
|
40689
|
+
},
|
|
40690
|
+
children: "\u2715"
|
|
40691
|
+
}
|
|
40692
|
+
)
|
|
40693
|
+
] }),
|
|
40694
|
+
/* @__PURE__ */ jsx35(
|
|
40695
|
+
"input",
|
|
40696
|
+
{
|
|
40697
|
+
ref: inputRef,
|
|
40698
|
+
type: "text",
|
|
40699
|
+
placeholder: "Search shortcuts...",
|
|
40700
|
+
value: query,
|
|
40701
|
+
onChange: (event) => setQuery(event.target.value),
|
|
40702
|
+
style: {
|
|
40703
|
+
marginTop: 12,
|
|
40704
|
+
width: "calc(100% - 24px)",
|
|
40705
|
+
padding: "10px 12px",
|
|
40706
|
+
borderRadius: 8,
|
|
40707
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
40708
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)",
|
|
40709
|
+
color: "white",
|
|
40710
|
+
fontSize: "0.95rem"
|
|
40711
|
+
}
|
|
40712
|
+
}
|
|
40713
|
+
)
|
|
40714
|
+
]
|
|
40715
|
+
}
|
|
40716
|
+
),
|
|
40717
|
+
/* @__PURE__ */ jsx35("div", { style: { overflowY: "auto" }, children: /* @__PURE__ */ jsxs9(
|
|
40718
|
+
"table",
|
|
40719
|
+
{
|
|
40720
|
+
style: {
|
|
40721
|
+
width: "100%",
|
|
40722
|
+
borderCollapse: "collapse",
|
|
40723
|
+
fontSize: "0.95rem"
|
|
40724
|
+
},
|
|
40725
|
+
children: [
|
|
40726
|
+
/* @__PURE__ */ jsx35("thead", { children: /* @__PURE__ */ jsxs9("tr", { style: { textAlign: "left", color: "#a1a1b5" }, children: [
|
|
40727
|
+
/* @__PURE__ */ jsx35("th", { style: { padding: "12px 24px", width: "25%" }, children: "Key" }),
|
|
40728
|
+
/* @__PURE__ */ jsx35("th", { style: { padding: "12px 24px" }, children: "Description" })
|
|
40729
|
+
] }) }),
|
|
40730
|
+
/* @__PURE__ */ jsx35("tbody", { children: filteredHotkeys.length === 0 ? /* @__PURE__ */ jsx35("tr", { children: /* @__PURE__ */ jsx35(
|
|
40731
|
+
"td",
|
|
40732
|
+
{
|
|
40733
|
+
colSpan: 2,
|
|
40734
|
+
style: { padding: "24px", textAlign: "center" },
|
|
40735
|
+
children: "No shortcuts found"
|
|
40736
|
+
}
|
|
40737
|
+
) }) : filteredHotkeys.map((hotkey) => /* @__PURE__ */ jsxs9(
|
|
40738
|
+
"tr",
|
|
40739
|
+
{
|
|
40740
|
+
style: { borderTop: "1px solid rgba(255, 255, 255, 0.05)" },
|
|
40741
|
+
children: [
|
|
40742
|
+
/* @__PURE__ */ jsx35("td", { style: { padding: "12px 24px" }, children: /* @__PURE__ */ jsx35(
|
|
40743
|
+
"span",
|
|
40744
|
+
{
|
|
40745
|
+
style: {
|
|
40746
|
+
display: "inline-flex",
|
|
40747
|
+
alignItems: "center",
|
|
40748
|
+
justifyContent: "center",
|
|
40749
|
+
border: "1px solid rgba(255, 255, 255, 0.3)",
|
|
40750
|
+
borderRadius: 6,
|
|
40751
|
+
minWidth: 36,
|
|
40752
|
+
padding: "4px 8px",
|
|
40753
|
+
fontFamily: "monospace",
|
|
40754
|
+
fontSize: "0.95rem"
|
|
40755
|
+
},
|
|
40756
|
+
children: hotkey.modifiers?.length ? /* @__PURE__ */ jsxs9(Fragment10, { children: [
|
|
40757
|
+
hotkey.modifiers.map((mod) => `${mod}+`).join(""),
|
|
40758
|
+
hotkey.key.toUpperCase()
|
|
40759
|
+
] }) : hotkey.key.toUpperCase()
|
|
40760
|
+
}
|
|
40761
|
+
) }),
|
|
40762
|
+
/* @__PURE__ */ jsx35("td", { style: { padding: "12px 24px" }, children: hotkey.description })
|
|
40763
|
+
]
|
|
40764
|
+
},
|
|
40765
|
+
hotkey.id
|
|
40766
|
+
)) })
|
|
40767
|
+
]
|
|
40768
|
+
}
|
|
40769
|
+
) })
|
|
40770
|
+
]
|
|
40771
|
+
}
|
|
40772
|
+
)
|
|
40773
|
+
}
|
|
40774
|
+
);
|
|
40775
|
+
};
|
|
40776
|
+
|
|
40418
40777
|
// src/CadViewer.tsx
|
|
40419
|
-
import { jsx as
|
|
40778
|
+
import { jsx as jsx36, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
40420
40779
|
var CadViewerInner = (props) => {
|
|
40421
|
-
const [engine, setEngine] =
|
|
40422
|
-
const containerRef =
|
|
40423
|
-
const [
|
|
40780
|
+
const [engine, setEngine] = useState36("manifold");
|
|
40781
|
+
const containerRef = useRef26(null);
|
|
40782
|
+
const [isKeyboardShortcutsDialogOpen, setIsKeyboardShortcutsDialogOpen] = useState36(false);
|
|
40783
|
+
const [autoRotate, setAutoRotate] = useState36(() => {
|
|
40424
40784
|
const stored = window.localStorage.getItem("cadViewerAutoRotate");
|
|
40425
40785
|
return stored === "false" ? false : true;
|
|
40426
40786
|
});
|
|
40427
|
-
const [autoRotateUserToggled, setAutoRotateUserToggled] =
|
|
40787
|
+
const [autoRotateUserToggled, setAutoRotateUserToggled] = useState36(() => {
|
|
40428
40788
|
const stored = window.localStorage.getItem("cadViewerAutoRotateUserToggled");
|
|
40429
40789
|
return stored === "true";
|
|
40430
40790
|
});
|
|
40431
|
-
const [cameraPreset, setCameraPreset] =
|
|
40791
|
+
const [cameraPreset, setCameraPreset] = useState36("Custom");
|
|
40432
40792
|
const { cameraType, setCameraType } = useCameraController();
|
|
40433
40793
|
const { visibility, toggleLayer } = useLayerVisibility();
|
|
40434
|
-
const cameraControllerRef =
|
|
40794
|
+
const cameraControllerRef = useRef26(null);
|
|
40435
40795
|
const externalCameraControllerReady = props.onCameraControllerReady;
|
|
40436
40796
|
const {
|
|
40437
40797
|
menuVisible,
|
|
@@ -40440,10 +40800,10 @@ var CadViewerInner = (props) => {
|
|
|
40440
40800
|
contextMenuEventHandlers,
|
|
40441
40801
|
setMenuVisible
|
|
40442
40802
|
} = useContextMenu({ containerRef });
|
|
40443
|
-
const autoRotateUserToggledRef =
|
|
40803
|
+
const autoRotateUserToggledRef = useRef26(autoRotateUserToggled);
|
|
40444
40804
|
autoRotateUserToggledRef.current = autoRotateUserToggled;
|
|
40445
|
-
const isAnimatingRef =
|
|
40446
|
-
const lastPresetSelectTime =
|
|
40805
|
+
const isAnimatingRef = useRef26(false);
|
|
40806
|
+
const lastPresetSelectTime = useRef26(0);
|
|
40447
40807
|
const PRESET_COOLDOWN = 1e3;
|
|
40448
40808
|
const handleUserInteraction = useCallback22(() => {
|
|
40449
40809
|
if (isAnimatingRef.current || Date.now() - lastPresetSelectTime.current < PRESET_COOLDOWN) {
|
|
@@ -40483,35 +40843,46 @@ var CadViewerInner = (props) => {
|
|
|
40483
40843
|
isAnimatingRef,
|
|
40484
40844
|
lastPresetSelectTime
|
|
40485
40845
|
});
|
|
40486
|
-
|
|
40846
|
+
useRegisteredHotkey(
|
|
40847
|
+
"open_keyboard_shortcuts_dialog",
|
|
40848
|
+
() => {
|
|
40849
|
+
setIsKeyboardShortcutsDialogOpen(true);
|
|
40850
|
+
},
|
|
40851
|
+
{
|
|
40852
|
+
key: "?",
|
|
40853
|
+
description: "Open keyboard shortcuts",
|
|
40854
|
+
modifiers: ["Shift"]
|
|
40855
|
+
}
|
|
40856
|
+
);
|
|
40857
|
+
useEffect42(() => {
|
|
40487
40858
|
const stored = window.localStorage.getItem("cadViewerEngine");
|
|
40488
40859
|
if (stored === "jscad" || stored === "manifold") {
|
|
40489
40860
|
setEngine(stored);
|
|
40490
40861
|
}
|
|
40491
40862
|
}, []);
|
|
40492
|
-
|
|
40863
|
+
useEffect42(() => {
|
|
40493
40864
|
window.localStorage.setItem("cadViewerEngine", engine);
|
|
40494
40865
|
}, [engine]);
|
|
40495
|
-
|
|
40866
|
+
useEffect42(() => {
|
|
40496
40867
|
window.localStorage.setItem("cadViewerAutoRotate", String(autoRotate));
|
|
40497
40868
|
}, [autoRotate]);
|
|
40498
|
-
|
|
40869
|
+
useEffect42(() => {
|
|
40499
40870
|
window.localStorage.setItem(
|
|
40500
40871
|
"cadViewerAutoRotateUserToggled",
|
|
40501
40872
|
String(autoRotateUserToggled)
|
|
40502
40873
|
);
|
|
40503
40874
|
}, [autoRotateUserToggled]);
|
|
40504
|
-
|
|
40875
|
+
useEffect42(() => {
|
|
40505
40876
|
const stored = window.localStorage.getItem("cadViewerCameraType");
|
|
40506
40877
|
if (stored === "orthographic" || stored === "perspective") {
|
|
40507
40878
|
setCameraType(stored);
|
|
40508
40879
|
}
|
|
40509
40880
|
}, [setCameraType]);
|
|
40510
|
-
|
|
40881
|
+
useEffect42(() => {
|
|
40511
40882
|
window.localStorage.setItem("cadViewerCameraType", cameraType);
|
|
40512
40883
|
}, [cameraType]);
|
|
40513
40884
|
const viewerKey = props.circuitJson ? JSON.stringify(props.circuitJson) : void 0;
|
|
40514
|
-
return /* @__PURE__ */
|
|
40885
|
+
return /* @__PURE__ */ jsxs10(
|
|
40515
40886
|
"div",
|
|
40516
40887
|
{
|
|
40517
40888
|
ref: containerRef,
|
|
@@ -40527,7 +40898,7 @@ var CadViewerInner = (props) => {
|
|
|
40527
40898
|
},
|
|
40528
40899
|
...contextMenuEventHandlers,
|
|
40529
40900
|
children: [
|
|
40530
|
-
engine === "jscad" ? /* @__PURE__ */
|
|
40901
|
+
engine === "jscad" ? /* @__PURE__ */ jsx36(
|
|
40531
40902
|
CadViewerJscad,
|
|
40532
40903
|
{
|
|
40533
40904
|
...props,
|
|
@@ -40536,7 +40907,7 @@ var CadViewerInner = (props) => {
|
|
|
40536
40907
|
onUserInteraction: handleUserInteraction,
|
|
40537
40908
|
onCameraControllerReady: handleCameraControllerReady
|
|
40538
40909
|
}
|
|
40539
|
-
) : /* @__PURE__ */
|
|
40910
|
+
) : /* @__PURE__ */ jsx36(
|
|
40540
40911
|
CadViewerManifold_default,
|
|
40541
40912
|
{
|
|
40542
40913
|
...props,
|
|
@@ -40546,7 +40917,7 @@ var CadViewerInner = (props) => {
|
|
|
40546
40917
|
onCameraControllerReady: handleCameraControllerReady
|
|
40547
40918
|
}
|
|
40548
40919
|
),
|
|
40549
|
-
/* @__PURE__ */
|
|
40920
|
+
/* @__PURE__ */ jsxs10(
|
|
40550
40921
|
"div",
|
|
40551
40922
|
{
|
|
40552
40923
|
style: {
|
|
@@ -40563,11 +40934,11 @@ var CadViewerInner = (props) => {
|
|
|
40563
40934
|
},
|
|
40564
40935
|
children: [
|
|
40565
40936
|
"Engine: ",
|
|
40566
|
-
/* @__PURE__ */
|
|
40937
|
+
/* @__PURE__ */ jsx36("b", { children: engine === "jscad" ? "JSCAD" : "Manifold" })
|
|
40567
40938
|
]
|
|
40568
40939
|
}
|
|
40569
40940
|
),
|
|
40570
|
-
menuVisible && /* @__PURE__ */
|
|
40941
|
+
menuVisible && /* @__PURE__ */ jsx36(
|
|
40571
40942
|
ContextMenu,
|
|
40572
40943
|
{
|
|
40573
40944
|
menuRef,
|
|
@@ -40587,8 +40958,19 @@ var CadViewerInner = (props) => {
|
|
|
40587
40958
|
onDownloadGltf: () => {
|
|
40588
40959
|
downloadGltf();
|
|
40589
40960
|
closeMenu();
|
|
40961
|
+
},
|
|
40962
|
+
onOpenKeyboardShortcuts: () => {
|
|
40963
|
+
setIsKeyboardShortcutsDialogOpen(true);
|
|
40964
|
+
closeMenu();
|
|
40590
40965
|
}
|
|
40591
40966
|
}
|
|
40967
|
+
),
|
|
40968
|
+
/* @__PURE__ */ jsx36(
|
|
40969
|
+
KeyboardShortcutsDialog,
|
|
40970
|
+
{
|
|
40971
|
+
open: isKeyboardShortcutsDialogOpen,
|
|
40972
|
+
onClose: () => setIsKeyboardShortcutsDialogOpen(false)
|
|
40973
|
+
}
|
|
40592
40974
|
)
|
|
40593
40975
|
]
|
|
40594
40976
|
},
|
|
@@ -40596,17 +40978,17 @@ var CadViewerInner = (props) => {
|
|
|
40596
40978
|
);
|
|
40597
40979
|
};
|
|
40598
40980
|
var CadViewer = (props) => {
|
|
40599
|
-
const defaultTarget =
|
|
40600
|
-
const initialCameraPosition =
|
|
40981
|
+
const defaultTarget = useMemo28(() => new THREE29.Vector3(0, 0, 0), []);
|
|
40982
|
+
const initialCameraPosition = useMemo28(
|
|
40601
40983
|
() => [5, -5, 5],
|
|
40602
40984
|
[]
|
|
40603
40985
|
);
|
|
40604
|
-
return /* @__PURE__ */
|
|
40986
|
+
return /* @__PURE__ */ jsx36(
|
|
40605
40987
|
CameraControllerProvider,
|
|
40606
40988
|
{
|
|
40607
40989
|
defaultTarget,
|
|
40608
40990
|
initialCameraPosition,
|
|
40609
|
-
children: /* @__PURE__ */
|
|
40991
|
+
children: /* @__PURE__ */ jsx36(LayerVisibilityProvider, { children: /* @__PURE__ */ jsx36(CadViewerInner, { ...props }) })
|
|
40610
40992
|
}
|
|
40611
40993
|
);
|
|
40612
40994
|
};
|
|
@@ -40890,10 +41272,10 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
40890
41272
|
|
|
40891
41273
|
// src/hooks/exporter/gltf.ts
|
|
40892
41274
|
import { GLTFExporter as GLTFExporter2 } from "three-stdlib";
|
|
40893
|
-
import { useEffect as
|
|
41275
|
+
import { useEffect as useEffect43, useState as useState37, useMemo as useMemo29, useCallback as useCallback23 } from "react";
|
|
40894
41276
|
function useSaveGltfAs(options = {}) {
|
|
40895
41277
|
const parse2 = useParser(options);
|
|
40896
|
-
const link =
|
|
41278
|
+
const link = useMemo29(() => document.createElement("a"), []);
|
|
40897
41279
|
const saveAs = async (filename) => {
|
|
40898
41280
|
const name = filename ?? options.filename ?? "";
|
|
40899
41281
|
if (options.binary == null) options.binary = name.endsWith(".glb");
|
|
@@ -40903,7 +41285,7 @@ function useSaveGltfAs(options = {}) {
|
|
|
40903
41285
|
link.dispatchEvent(new MouseEvent("click"));
|
|
40904
41286
|
URL.revokeObjectURL(url);
|
|
40905
41287
|
};
|
|
40906
|
-
|
|
41288
|
+
useEffect43(
|
|
40907
41289
|
() => () => {
|
|
40908
41290
|
link.remove();
|
|
40909
41291
|
instance = null;
|
|
@@ -40918,17 +41300,17 @@ function useSaveGltfAs(options = {}) {
|
|
|
40918
41300
|
}
|
|
40919
41301
|
function useExportGltfUrl(options = {}) {
|
|
40920
41302
|
const parse2 = useParser(options);
|
|
40921
|
-
const [url, setUrl] =
|
|
40922
|
-
const [error, setError] =
|
|
41303
|
+
const [url, setUrl] = useState37();
|
|
41304
|
+
const [error, setError] = useState37();
|
|
40923
41305
|
const ref = useCallback23(
|
|
40924
41306
|
(instance) => parse2(instance).then(setUrl).catch(setError),
|
|
40925
41307
|
[]
|
|
40926
41308
|
);
|
|
40927
|
-
|
|
41309
|
+
useEffect43(() => () => URL.revokeObjectURL(url), [url]);
|
|
40928
41310
|
return [ref, url, error];
|
|
40929
41311
|
}
|
|
40930
41312
|
function useParser(options = {}) {
|
|
40931
|
-
const exporter =
|
|
41313
|
+
const exporter = useMemo29(() => new GLTFExporter2(), []);
|
|
40932
41314
|
return (instance) => {
|
|
40933
41315
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
40934
41316
|
exporter.parse(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/3d-viewer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.440",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@vitejs/plugin-react": "^4.3.4",
|
|
62
62
|
"bun-match-svg": "^0.0.9",
|
|
63
63
|
"bun-types": "1.2.1",
|
|
64
|
-
"circuit-json": "0.0.
|
|
64
|
+
"circuit-json": "0.0.317",
|
|
65
65
|
"circuit-to-svg": "^0.0.179",
|
|
66
66
|
"debug": "^4.4.0",
|
|
67
67
|
"jscad-electronics": "^0.0.89",
|