@tscircuit/3d-viewer 0.0.641 → 0.0.642
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 +4 -1
- package/dist/index.js +133 -93
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -60,7 +60,10 @@ type CadViewerProps = Omit<React.ComponentProps<typeof CadViewerJscad>, "circuit
|
|
|
60
60
|
circuitJson?: any[];
|
|
61
61
|
/** Settled circuit retaining authored assembly.explosion stages. */
|
|
62
62
|
circuit?: RootCircuit;
|
|
63
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Pre-rendered circuit-json-to-gltf GLB URL. Bypasses geometry/model generation.
|
|
65
|
+
* Changing the GLB preserves the camera. Change the viewer key to frame a different board.
|
|
66
|
+
*/
|
|
64
67
|
glbUrl?: string;
|
|
65
68
|
/** In-memory alternative to glbUrl. Supply only one GLB source. */
|
|
66
69
|
glbData?: ArrayBuffer;
|
package/dist/index.js
CHANGED
|
@@ -22344,7 +22344,7 @@ import * as THREE31 from "three";
|
|
|
22344
22344
|
// package.json
|
|
22345
22345
|
var package_default = {
|
|
22346
22346
|
name: "@tscircuit/3d-viewer",
|
|
22347
|
-
version: "0.0.
|
|
22347
|
+
version: "0.0.641",
|
|
22348
22348
|
repository: {
|
|
22349
22349
|
type: "git",
|
|
22350
22350
|
url: "https://github.com/tscircuit/3d-viewer"
|
|
@@ -32042,33 +32042,51 @@ var CadViewerManifold = ({
|
|
|
32042
32042
|
var CadViewerManifold_default = CadViewerManifold;
|
|
32043
32043
|
|
|
32044
32044
|
// src/CadViewerGlb.tsx
|
|
32045
|
-
import {
|
|
32045
|
+
import {
|
|
32046
|
+
useEffect as useEffect37,
|
|
32047
|
+
useLayoutEffect as useLayoutEffect2,
|
|
32048
|
+
useState as useState23
|
|
32049
|
+
} from "react";
|
|
32046
32050
|
|
|
32047
32051
|
// src/hooks/useGlbModel.ts
|
|
32048
|
-
import { useEffect as useEffect36, useMemo as useMemo30, useState as useState22 } from "react";
|
|
32052
|
+
import { useEffect as useEffect36, useLayoutEffect, useMemo as useMemo30, useState as useState22 } from "react";
|
|
32049
32053
|
function useGlbModel(source) {
|
|
32050
32054
|
const request = useMemo30(() => ({ source }), [source]);
|
|
32051
32055
|
const [result, setResult] = useState22();
|
|
32056
|
+
const displayedGlb = result?.loadedGlb;
|
|
32052
32057
|
useEffect36(() => {
|
|
32053
32058
|
const controller = new AbortController();
|
|
32054
|
-
let
|
|
32055
|
-
loadGlbScene(request.source, controller.signal).then((
|
|
32056
|
-
|
|
32057
|
-
|
|
32058
|
-
|
|
32059
|
+
let loadedGlb;
|
|
32060
|
+
loadGlbScene(request.source, controller.signal).then((model) => {
|
|
32061
|
+
if (controller.signal.aborted) {
|
|
32062
|
+
disposeGlbScene(model);
|
|
32063
|
+
return;
|
|
32064
|
+
}
|
|
32065
|
+
loadedGlb = { model, wasDisplayed: false };
|
|
32066
|
+
setResult({ request, loadedGlb });
|
|
32059
32067
|
}).catch((error) => {
|
|
32060
|
-
if (
|
|
32061
|
-
|
|
32062
|
-
|
|
32063
|
-
|
|
32064
|
-
|
|
32068
|
+
if (controller.signal.aborted) return;
|
|
32069
|
+
setResult((previous) => ({
|
|
32070
|
+
request,
|
|
32071
|
+
loadedGlb: previous?.loadedGlb,
|
|
32072
|
+
error: error instanceof Error ? error.message : String(error)
|
|
32073
|
+
}));
|
|
32065
32074
|
});
|
|
32066
32075
|
return () => {
|
|
32067
32076
|
controller.abort();
|
|
32068
|
-
if (
|
|
32077
|
+
if (loadedGlb && !loadedGlb.wasDisplayed) disposeGlbScene(loadedGlb.model);
|
|
32069
32078
|
};
|
|
32070
32079
|
}, [request]);
|
|
32071
|
-
|
|
32080
|
+
useLayoutEffect(() => {
|
|
32081
|
+
if (!displayedGlb) return;
|
|
32082
|
+
displayedGlb.wasDisplayed = true;
|
|
32083
|
+
return () => disposeGlbScene(displayedGlb.model);
|
|
32084
|
+
}, [displayedGlb]);
|
|
32085
|
+
return {
|
|
32086
|
+
model: displayedGlb?.model,
|
|
32087
|
+
isLoading: result?.request !== request,
|
|
32088
|
+
error: result?.request === request ? result.error : void 0
|
|
32089
|
+
};
|
|
32072
32090
|
}
|
|
32073
32091
|
|
|
32074
32092
|
// src/CadViewerGlb.tsx
|
|
@@ -32081,19 +32099,7 @@ function GlbScene({
|
|
|
32081
32099
|
const { rootObject } = useThree();
|
|
32082
32100
|
const { addHoverable, removeHoverable } = useHover();
|
|
32083
32101
|
const [hover, setHover] = useState23();
|
|
32084
|
-
|
|
32085
|
-
const box = new THREE62.Box3().setFromObject(model);
|
|
32086
|
-
if (box.isEmpty()) return;
|
|
32087
|
-
return {
|
|
32088
|
-
minX: box.min.x,
|
|
32089
|
-
maxX: box.max.x,
|
|
32090
|
-
minY: box.min.y,
|
|
32091
|
-
maxY: box.max.y,
|
|
32092
|
-
minZ: box.min.z,
|
|
32093
|
-
maxZ: box.max.z
|
|
32094
|
-
};
|
|
32095
|
-
}, [model]);
|
|
32096
|
-
useEffect37(() => {
|
|
32102
|
+
useLayoutEffect2(() => {
|
|
32097
32103
|
rootObject.add(model);
|
|
32098
32104
|
return () => {
|
|
32099
32105
|
rootObject.remove(model);
|
|
@@ -32144,54 +32150,88 @@ function GlbScene({
|
|
|
32144
32150
|
for (const mesh of meshes) removeHoverable(mesh);
|
|
32145
32151
|
};
|
|
32146
32152
|
}, [model, objectVisibility, addHoverable, removeHoverable]);
|
|
32153
|
+
return /* @__PURE__ */ jsx24(Fragment5, { children: hover && /* @__PURE__ */ jsx24(
|
|
32154
|
+
Html,
|
|
32155
|
+
{
|
|
32156
|
+
position: hover.position,
|
|
32157
|
+
style: {
|
|
32158
|
+
background: "white",
|
|
32159
|
+
color: "black",
|
|
32160
|
+
padding: "5px",
|
|
32161
|
+
borderRadius: "3px",
|
|
32162
|
+
fontFamily: "sans-serif",
|
|
32163
|
+
transform: "translate(1rem, 1rem)"
|
|
32164
|
+
},
|
|
32165
|
+
children: hover.name
|
|
32166
|
+
}
|
|
32167
|
+
) });
|
|
32168
|
+
}
|
|
32169
|
+
function CadViewerGlb({
|
|
32170
|
+
source,
|
|
32171
|
+
objectVisibility,
|
|
32172
|
+
...containerProps
|
|
32173
|
+
}) {
|
|
32174
|
+
const { model, isLoading, error } = useGlbModel(source);
|
|
32175
|
+
const message = error ? `Unable to load 3D view: ${error}` : "Loading 3D view...";
|
|
32176
|
+
const role = error ? "alert" : "status";
|
|
32177
|
+
if (!model) return /* @__PURE__ */ jsx24("div", { role, children: message });
|
|
32147
32178
|
return /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
32148
|
-
|
|
32149
|
-
|
|
32150
|
-
Html,
|
|
32179
|
+
/* @__PURE__ */ jsx24(
|
|
32180
|
+
GlbViewport,
|
|
32151
32181
|
{
|
|
32152
|
-
|
|
32153
|
-
|
|
32154
|
-
|
|
32155
|
-
color: "black",
|
|
32156
|
-
padding: "5px",
|
|
32157
|
-
borderRadius: "3px",
|
|
32158
|
-
fontFamily: "sans-serif",
|
|
32159
|
-
transform: "translate(1rem, 1rem)"
|
|
32160
|
-
},
|
|
32161
|
-
children: hover.name
|
|
32182
|
+
model,
|
|
32183
|
+
objectVisibility,
|
|
32184
|
+
...containerProps
|
|
32162
32185
|
}
|
|
32163
|
-
)
|
|
32186
|
+
),
|
|
32187
|
+
(isLoading || error) && /* @__PURE__ */ jsx24("div", { role, style: { position: "absolute", top: 12, left: 12 }, children: message })
|
|
32164
32188
|
] });
|
|
32165
32189
|
}
|
|
32166
|
-
function
|
|
32167
|
-
|
|
32190
|
+
function GlbViewport({
|
|
32191
|
+
model,
|
|
32168
32192
|
objectVisibility,
|
|
32169
32193
|
...containerProps
|
|
32170
32194
|
}) {
|
|
32171
|
-
const
|
|
32172
|
-
|
|
32173
|
-
if (
|
|
32174
|
-
const
|
|
32175
|
-
if (
|
|
32176
|
-
|
|
32177
|
-
|
|
32178
|
-
|
|
32179
|
-
|
|
32180
|
-
|
|
32181
|
-
|
|
32182
|
-
|
|
32183
|
-
|
|
32184
|
-
|
|
32185
|
-
|
|
32186
|
-
|
|
32187
|
-
|
|
32188
|
-
|
|
32189
|
-
|
|
32190
|
-
|
|
32191
|
-
|
|
32192
|
-
|
|
32193
|
-
|
|
32194
|
-
|
|
32195
|
+
const [framing, setFraming] = useState23(() => getGlbFraming(model));
|
|
32196
|
+
useEffect37(() => {
|
|
32197
|
+
if (framing) return;
|
|
32198
|
+
const nextFraming = getGlbFraming(model);
|
|
32199
|
+
if (nextFraming) setFraming(nextFraming);
|
|
32200
|
+
}, [framing, model]);
|
|
32201
|
+
return /* @__PURE__ */ jsxs8(
|
|
32202
|
+
CadViewerContainer,
|
|
32203
|
+
{
|
|
32204
|
+
...containerProps,
|
|
32205
|
+
boardDimensions: framing?.boardDimensions,
|
|
32206
|
+
boardCenter: framing?.boardCenter,
|
|
32207
|
+
children: [
|
|
32208
|
+
framing && /* @__PURE__ */ jsx24(FitCameraToComparison, { bounds: framing.bounds }),
|
|
32209
|
+
/* @__PURE__ */ jsx24(GlbScene, { model, objectVisibility })
|
|
32210
|
+
]
|
|
32211
|
+
}
|
|
32212
|
+
);
|
|
32213
|
+
}
|
|
32214
|
+
function getGlbFraming(model) {
|
|
32215
|
+
const box = new THREE62.Box3().setFromObject(model);
|
|
32216
|
+
if (box.isEmpty()) return void 0;
|
|
32217
|
+
return {
|
|
32218
|
+
bounds: {
|
|
32219
|
+
minX: box.min.x,
|
|
32220
|
+
maxX: box.max.x,
|
|
32221
|
+
minY: box.min.y,
|
|
32222
|
+
maxY: box.max.y,
|
|
32223
|
+
minZ: box.min.z,
|
|
32224
|
+
maxZ: box.max.z
|
|
32225
|
+
},
|
|
32226
|
+
boardDimensions: {
|
|
32227
|
+
width: box.max.x - box.min.x,
|
|
32228
|
+
height: box.max.y - box.min.y
|
|
32229
|
+
},
|
|
32230
|
+
boardCenter: {
|
|
32231
|
+
x: (box.min.x + box.max.x) / 2,
|
|
32232
|
+
y: (box.min.y + box.max.y) / 2
|
|
32233
|
+
}
|
|
32234
|
+
};
|
|
32195
32235
|
}
|
|
32196
32236
|
|
|
32197
32237
|
// node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs
|
|
@@ -32392,7 +32432,7 @@ var IS_DEVELOPMENT = false;
|
|
|
32392
32432
|
|
|
32393
32433
|
// node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
|
|
32394
32434
|
import * as React13 from "react";
|
|
32395
|
-
var
|
|
32435
|
+
var useLayoutEffect22 = globalThis?.document ? React13.useLayoutEffect : () => {
|
|
32396
32436
|
};
|
|
32397
32437
|
|
|
32398
32438
|
// node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
@@ -32416,7 +32456,7 @@ function useEffectEvent(callback) {
|
|
|
32416
32456
|
ref.current = callback;
|
|
32417
32457
|
});
|
|
32418
32458
|
} else {
|
|
32419
|
-
|
|
32459
|
+
useLayoutEffect22(() => {
|
|
32420
32460
|
ref.current = callback;
|
|
32421
32461
|
});
|
|
32422
32462
|
}
|
|
@@ -32427,7 +32467,7 @@ __name4(useEffectEvent, "useEffectEvent");
|
|
|
32427
32467
|
// node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
32428
32468
|
var __defProp6 = Object.defineProperty;
|
|
32429
32469
|
var __name5 = (target, value) => __defProp6(target, "name", { value, configurable: true });
|
|
32430
|
-
var useInsertionEffect = React15[" useInsertionEffect ".trim().toString()] ||
|
|
32470
|
+
var useInsertionEffect = React15[" useInsertionEffect ".trim().toString()] || useLayoutEffect22;
|
|
32431
32471
|
function useControllableState({
|
|
32432
32472
|
prop,
|
|
32433
32473
|
defaultProp,
|
|
@@ -33267,7 +33307,7 @@ function createCollection2(name) {
|
|
|
33267
33307
|
[registryRef]
|
|
33268
33308
|
);
|
|
33269
33309
|
const composedRefs = useComposedRefs(forwardedRef, registerElement, ref);
|
|
33270
|
-
|
|
33310
|
+
useLayoutEffect22(() => {
|
|
33271
33311
|
const node = ref.current;
|
|
33272
33312
|
const registry = registryRef.current;
|
|
33273
33313
|
if (node && registry.has(node)) {
|
|
@@ -33420,7 +33460,7 @@ function useCallbackRef(callback) {
|
|
|
33420
33460
|
callbackRef.current = callback;
|
|
33421
33461
|
});
|
|
33422
33462
|
} else {
|
|
33423
|
-
|
|
33463
|
+
useLayoutEffect22(() => {
|
|
33424
33464
|
callbackRef.current = callback;
|
|
33425
33465
|
});
|
|
33426
33466
|
}
|
|
@@ -34089,7 +34129,7 @@ var useReactId = React26[" useId ".trim().toString()] || (() => void 0);
|
|
|
34089
34129
|
var count2 = 0;
|
|
34090
34130
|
function useId2(deterministicId) {
|
|
34091
34131
|
const [id, setId] = React26.useState(useReactId());
|
|
34092
|
-
|
|
34132
|
+
useLayoutEffect22(() => {
|
|
34093
34133
|
if (!deterministicId) setId((reactId) => reactId ?? String(count2++));
|
|
34094
34134
|
}, [deterministicId]);
|
|
34095
34135
|
return deterministicId || (id ? `radix-${id}` : "");
|
|
@@ -35693,12 +35733,12 @@ var computePosition2 = (reference, floating, options) => {
|
|
|
35693
35733
|
|
|
35694
35734
|
// node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.mjs
|
|
35695
35735
|
import * as React27 from "react";
|
|
35696
|
-
import { useLayoutEffect as
|
|
35736
|
+
import { useLayoutEffect as useLayoutEffect4 } from "react";
|
|
35697
35737
|
import * as ReactDOM3 from "react-dom";
|
|
35698
35738
|
var isClient = typeof document !== "undefined";
|
|
35699
35739
|
var noop = function noop2() {
|
|
35700
35740
|
};
|
|
35701
|
-
var index = isClient ?
|
|
35741
|
+
var index = isClient ? useLayoutEffect4 : noop;
|
|
35702
35742
|
function deepEqual(a, b) {
|
|
35703
35743
|
if (a === b) {
|
|
35704
35744
|
return true;
|
|
@@ -36008,7 +36048,7 @@ var __defProp16 = Object.defineProperty;
|
|
|
36008
36048
|
var __name15 = (target, value) => __defProp16(target, "name", { value, configurable: true });
|
|
36009
36049
|
function useSize(element) {
|
|
36010
36050
|
const [size4, setSize] = React28.useState(void 0);
|
|
36011
|
-
|
|
36051
|
+
useLayoutEffect22(() => {
|
|
36012
36052
|
if (element) {
|
|
36013
36053
|
setSize({ width: element.offsetWidth, height: element.offsetHeight });
|
|
36014
36054
|
let rAF = 0;
|
|
@@ -36215,7 +36255,7 @@ var PopperContent = /* @__PURE__ */ React29.forwardRef(
|
|
|
36215
36255
|
]
|
|
36216
36256
|
});
|
|
36217
36257
|
const setPlacementState = context.setPlacementState;
|
|
36218
|
-
|
|
36258
|
+
useLayoutEffect22(() => {
|
|
36219
36259
|
setPlacementState(placement);
|
|
36220
36260
|
return () => {
|
|
36221
36261
|
setPlacementState(void 0);
|
|
@@ -36223,7 +36263,7 @@ var PopperContent = /* @__PURE__ */ React29.forwardRef(
|
|
|
36223
36263
|
}, [placement, setPlacementState]);
|
|
36224
36264
|
const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
|
|
36225
36265
|
const handlePlaced = useCallbackRef(onPlaced);
|
|
36226
|
-
|
|
36266
|
+
useLayoutEffect22(() => {
|
|
36227
36267
|
if (isPositioned) {
|
|
36228
36268
|
handlePlaced?.();
|
|
36229
36269
|
}
|
|
@@ -36232,7 +36272,7 @@ var PopperContent = /* @__PURE__ */ React29.forwardRef(
|
|
|
36232
36272
|
const arrowY = middlewareData.arrow?.y;
|
|
36233
36273
|
const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
|
|
36234
36274
|
const [contentZIndex, setContentZIndex] = React29.useState();
|
|
36235
|
-
|
|
36275
|
+
useLayoutEffect22(() => {
|
|
36236
36276
|
if (content) setContentZIndex(window.getComputedStyle(content).zIndex);
|
|
36237
36277
|
}, [content]);
|
|
36238
36278
|
return /* @__PURE__ */ jsx32(
|
|
@@ -36342,7 +36382,7 @@ var Portal = /* @__PURE__ */ React30.forwardRef(
|
|
|
36342
36382
|
/* @__PURE__ */ __name17(function Portal2(props, forwardedRef) {
|
|
36343
36383
|
const { container: containerProp, ...portalProps } = props;
|
|
36344
36384
|
const [mounted, setMounted] = React30.useState(false);
|
|
36345
|
-
|
|
36385
|
+
useLayoutEffect22(() => setMounted(true), []);
|
|
36346
36386
|
const container = containerProp || mounted && globalThis?.document?.body;
|
|
36347
36387
|
return container ? ReactDOM4.createPortal(/* @__PURE__ */ jsx33(Primitive.div, { ...portalProps, ref: forwardedRef }), container) : null;
|
|
36348
36388
|
}, "Portal")
|
|
@@ -36396,7 +36436,7 @@ function usePresence(present) {
|
|
|
36396
36436
|
prevAnimationNameRef.current = "none";
|
|
36397
36437
|
}
|
|
36398
36438
|
}, [state]);
|
|
36399
|
-
|
|
36439
|
+
useLayoutEffect22(() => {
|
|
36400
36440
|
const styles = stylesRef.current;
|
|
36401
36441
|
const wasPresent = prevPresentRef.current;
|
|
36402
36442
|
const hasPresentChanged = wasPresent !== present;
|
|
@@ -36419,7 +36459,7 @@ function usePresence(present) {
|
|
|
36419
36459
|
prevPresentRef.current = present;
|
|
36420
36460
|
}
|
|
36421
36461
|
}, [present, send]);
|
|
36422
|
-
|
|
36462
|
+
useLayoutEffect22(() => {
|
|
36423
36463
|
if (node) {
|
|
36424
36464
|
let timeoutId;
|
|
36425
36465
|
const ownerWindow = node.ownerDocument.defaultView ?? window;
|
|
@@ -36699,7 +36739,7 @@ var RovingFocusGroupItem = /* @__PURE__ */ React33.forwardRef(
|
|
|
36699
36739
|
const getItems = useCollection(__scopeRovingFocusGroup);
|
|
36700
36740
|
const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;
|
|
36701
36741
|
const isHydrated = useIsHydrated2();
|
|
36702
|
-
|
|
36742
|
+
useLayoutEffect22(() => {
|
|
36703
36743
|
if (!isHydrated || !focusable) {
|
|
36704
36744
|
return;
|
|
36705
36745
|
}
|
|
@@ -38593,12 +38633,12 @@ var DropdownMenuSubContent = /* @__PURE__ */ React42.forwardRef(/* @__PURE__ */
|
|
|
38593
38633
|
}, "DropdownMenuSubContent"));
|
|
38594
38634
|
|
|
38595
38635
|
// src/components/ViewportSubContent.tsx
|
|
38596
|
-
import { useLayoutEffect as
|
|
38636
|
+
import { useLayoutEffect as useLayoutEffect6, useState as useState40 } from "react";
|
|
38597
38637
|
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
38598
38638
|
var ViewportSubContent = (props) => {
|
|
38599
38639
|
const [content, setContent] = useState40(null);
|
|
38600
38640
|
const [overlapWidth, setOverlapWidth] = useState40();
|
|
38601
|
-
|
|
38641
|
+
useLayoutEffect6(() => {
|
|
38602
38642
|
if (!content) return;
|
|
38603
38643
|
const trigger = document.getElementById(
|
|
38604
38644
|
content.getAttribute("aria-labelledby") ?? ""
|
|
@@ -39741,10 +39781,10 @@ var ContextMenu = ({
|
|
|
39741
39781
|
};
|
|
39742
39782
|
|
|
39743
39783
|
// src/components/KeyboardShortcutsDialog.tsx
|
|
39744
|
-
import { useEffect as useEffect55, useMemo as
|
|
39784
|
+
import { useEffect as useEffect55, useMemo as useMemo40, useRef as useRef28, useState as useState44 } from "react";
|
|
39745
39785
|
|
|
39746
39786
|
// src/hooks/useRegisteredHotkey.ts
|
|
39747
|
-
import { useEffect as useEffect54, useMemo as
|
|
39787
|
+
import { useEffect as useEffect54, useMemo as useMemo39, useRef as useRef27, useState as useState43 } from "react";
|
|
39748
39788
|
var hotkeyRegistry = /* @__PURE__ */ new Map();
|
|
39749
39789
|
var subscribers = /* @__PURE__ */ new Set();
|
|
39750
39790
|
var isListenerAttached = false;
|
|
@@ -39868,7 +39908,7 @@ var subscribeToRegistry = (subscriber) => {
|
|
|
39868
39908
|
var useRegisteredHotkey = (id, handler, metadata) => {
|
|
39869
39909
|
const handlerRef = useRef27(handler);
|
|
39870
39910
|
handlerRef.current = handler;
|
|
39871
|
-
const normalizedMetadata =
|
|
39911
|
+
const normalizedMetadata = useMemo39(
|
|
39872
39912
|
() => ({
|
|
39873
39913
|
shortcut: metadata.shortcut,
|
|
39874
39914
|
description: metadata.description
|
|
@@ -39926,7 +39966,7 @@ var KeyboardShortcutsDialog = ({
|
|
|
39926
39966
|
}, 0);
|
|
39927
39967
|
}
|
|
39928
39968
|
}, [open]);
|
|
39929
|
-
const filteredHotkeys =
|
|
39969
|
+
const filteredHotkeys = useMemo40(() => {
|
|
39930
39970
|
const normalizedQuery = query.trim().toLowerCase();
|
|
39931
39971
|
if (!normalizedQuery) {
|
|
39932
39972
|
return hotkeys;
|
|
@@ -41153,10 +41193,10 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
41153
41193
|
import {
|
|
41154
41194
|
GLTFExporter as GLTFExporter3
|
|
41155
41195
|
} from "three/examples/jsm/exporters/GLTFExporter.js";
|
|
41156
|
-
import { useEffect as useEffect58, useState as useState47, useMemo as
|
|
41196
|
+
import { useEffect as useEffect58, useState as useState47, useMemo as useMemo41, useCallback as useCallback26 } from "react";
|
|
41157
41197
|
function useSaveGltfAs(options = {}) {
|
|
41158
41198
|
const parse2 = useParser(options);
|
|
41159
|
-
const link =
|
|
41199
|
+
const link = useMemo41(() => document.createElement("a"), []);
|
|
41160
41200
|
const saveAs = async (filename) => {
|
|
41161
41201
|
const name = filename ?? options.filename ?? "";
|
|
41162
41202
|
if (options.binary == null) options.binary = name.endsWith(".glb");
|
|
@@ -41191,7 +41231,7 @@ function useExportGltfUrl(options = {}) {
|
|
|
41191
41231
|
return [ref, url, error];
|
|
41192
41232
|
}
|
|
41193
41233
|
function useParser(options = {}) {
|
|
41194
|
-
const exporter =
|
|
41234
|
+
const exporter = useMemo41(() => new GLTFExporter3(), []);
|
|
41195
41235
|
return (instance) => {
|
|
41196
41236
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
41197
41237
|
exporter.parse(
|