@tscircuit/3d-viewer 0.0.640 → 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/README.md +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +134 -93
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -121,6 +121,7 @@ const svgString = await convert3dCircuitToSvg(circuitJson, options)
|
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
The `convert3dCircuitToSvg` function accepts the following options:
|
|
124
|
+
|
|
124
125
|
- `width`: Width of the output SVG (default: 800)
|
|
125
126
|
- `height`: Height of the output SVG (default: 600)
|
|
126
127
|
- `backgroundColor`: Background color in hex format (default: "#ffffff")
|
|
@@ -137,6 +138,7 @@ The `convert3dCircuitToSvg` function accepts the following options:
|
|
|
137
138
|
Main component for rendering the 3D PCB viewer.
|
|
138
139
|
|
|
139
140
|
Props:
|
|
141
|
+
|
|
140
142
|
- `circuit-json`: (optional) An array of AnyCircuitElement objects representing the PCB layout.
|
|
141
143
|
- `children`: (optional) React children elements describing the PCB layout (alternative to using `circuit-json`).
|
|
142
144
|
- `resolveStaticAsset`: (optional) Function that receives each component model URL (`obj`, `wrl`, `stl`, `gltf`, `glb`, `step`) and returns the resolved URL to load.
|
|
@@ -146,12 +148,14 @@ Props:
|
|
|
146
148
|
Defines the PCB board dimensions.
|
|
147
149
|
|
|
148
150
|
Props:
|
|
151
|
+
|
|
149
152
|
- `width`: Width of the board (e.g., "20mm").
|
|
150
153
|
- `height`: Height of the board (e.g., "20mm").
|
|
151
154
|
|
|
152
155
|
### Component Elements
|
|
153
156
|
|
|
154
157
|
Various component elements can be used as children of the `<board>` element:
|
|
158
|
+
|
|
155
159
|
- `<resistor>`
|
|
156
160
|
- `<capacitor>`
|
|
157
161
|
- `<chip>`
|
|
@@ -306,7 +310,6 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
306
310
|
|
|
307
311
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
308
312
|
|
|
309
|
-
|
|
310
313
|
### Section views
|
|
311
314
|
|
|
312
315
|
Right-click the viewer and choose **Advanced → Section View…**.
|
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"
|
|
@@ -22860,6 +22860,7 @@ var Canvas = forwardRef(
|
|
|
22860
22860
|
mountRef.current.removeChild(renderer.domElement);
|
|
22861
22861
|
}
|
|
22862
22862
|
renderer.dispose();
|
|
22863
|
+
renderer.forceContextLoss();
|
|
22863
22864
|
scene.remove(rootObject.current);
|
|
22864
22865
|
if (window.__TSCIRCUIT_THREE_OBJECT === rootObject.current) {
|
|
22865
22866
|
window.__TSCIRCUIT_THREE_OBJECT = void 0;
|
|
@@ -32041,33 +32042,51 @@ var CadViewerManifold = ({
|
|
|
32041
32042
|
var CadViewerManifold_default = CadViewerManifold;
|
|
32042
32043
|
|
|
32043
32044
|
// src/CadViewerGlb.tsx
|
|
32044
|
-
import {
|
|
32045
|
+
import {
|
|
32046
|
+
useEffect as useEffect37,
|
|
32047
|
+
useLayoutEffect as useLayoutEffect2,
|
|
32048
|
+
useState as useState23
|
|
32049
|
+
} from "react";
|
|
32045
32050
|
|
|
32046
32051
|
// src/hooks/useGlbModel.ts
|
|
32047
|
-
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";
|
|
32048
32053
|
function useGlbModel(source) {
|
|
32049
32054
|
const request = useMemo30(() => ({ source }), [source]);
|
|
32050
32055
|
const [result, setResult] = useState22();
|
|
32056
|
+
const displayedGlb = result?.loadedGlb;
|
|
32051
32057
|
useEffect36(() => {
|
|
32052
32058
|
const controller = new AbortController();
|
|
32053
|
-
let
|
|
32054
|
-
loadGlbScene(request.source, controller.signal).then((
|
|
32055
|
-
|
|
32056
|
-
|
|
32057
|
-
|
|
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 });
|
|
32058
32067
|
}).catch((error) => {
|
|
32059
|
-
if (
|
|
32060
|
-
|
|
32061
|
-
|
|
32062
|
-
|
|
32063
|
-
|
|
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
|
+
}));
|
|
32064
32074
|
});
|
|
32065
32075
|
return () => {
|
|
32066
32076
|
controller.abort();
|
|
32067
|
-
if (
|
|
32077
|
+
if (loadedGlb && !loadedGlb.wasDisplayed) disposeGlbScene(loadedGlb.model);
|
|
32068
32078
|
};
|
|
32069
32079
|
}, [request]);
|
|
32070
|
-
|
|
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
|
+
};
|
|
32071
32090
|
}
|
|
32072
32091
|
|
|
32073
32092
|
// src/CadViewerGlb.tsx
|
|
@@ -32080,19 +32099,7 @@ function GlbScene({
|
|
|
32080
32099
|
const { rootObject } = useThree();
|
|
32081
32100
|
const { addHoverable, removeHoverable } = useHover();
|
|
32082
32101
|
const [hover, setHover] = useState23();
|
|
32083
|
-
|
|
32084
|
-
const box = new THREE62.Box3().setFromObject(model);
|
|
32085
|
-
if (box.isEmpty()) return;
|
|
32086
|
-
return {
|
|
32087
|
-
minX: box.min.x,
|
|
32088
|
-
maxX: box.max.x,
|
|
32089
|
-
minY: box.min.y,
|
|
32090
|
-
maxY: box.max.y,
|
|
32091
|
-
minZ: box.min.z,
|
|
32092
|
-
maxZ: box.max.z
|
|
32093
|
-
};
|
|
32094
|
-
}, [model]);
|
|
32095
|
-
useEffect37(() => {
|
|
32102
|
+
useLayoutEffect2(() => {
|
|
32096
32103
|
rootObject.add(model);
|
|
32097
32104
|
return () => {
|
|
32098
32105
|
rootObject.remove(model);
|
|
@@ -32143,54 +32150,88 @@ function GlbScene({
|
|
|
32143
32150
|
for (const mesh of meshes) removeHoverable(mesh);
|
|
32144
32151
|
};
|
|
32145
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 });
|
|
32146
32178
|
return /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
32147
|
-
|
|
32148
|
-
|
|
32149
|
-
Html,
|
|
32179
|
+
/* @__PURE__ */ jsx24(
|
|
32180
|
+
GlbViewport,
|
|
32150
32181
|
{
|
|
32151
|
-
|
|
32152
|
-
|
|
32153
|
-
|
|
32154
|
-
color: "black",
|
|
32155
|
-
padding: "5px",
|
|
32156
|
-
borderRadius: "3px",
|
|
32157
|
-
fontFamily: "sans-serif",
|
|
32158
|
-
transform: "translate(1rem, 1rem)"
|
|
32159
|
-
},
|
|
32160
|
-
children: hover.name
|
|
32182
|
+
model,
|
|
32183
|
+
objectVisibility,
|
|
32184
|
+
...containerProps
|
|
32161
32185
|
}
|
|
32162
|
-
)
|
|
32186
|
+
),
|
|
32187
|
+
(isLoading || error) && /* @__PURE__ */ jsx24("div", { role, style: { position: "absolute", top: 12, left: 12 }, children: message })
|
|
32163
32188
|
] });
|
|
32164
32189
|
}
|
|
32165
|
-
function
|
|
32166
|
-
|
|
32190
|
+
function GlbViewport({
|
|
32191
|
+
model,
|
|
32167
32192
|
objectVisibility,
|
|
32168
32193
|
...containerProps
|
|
32169
32194
|
}) {
|
|
32170
|
-
const
|
|
32171
|
-
|
|
32172
|
-
if (
|
|
32173
|
-
const
|
|
32174
|
-
if (
|
|
32175
|
-
|
|
32176
|
-
|
|
32177
|
-
|
|
32178
|
-
|
|
32179
|
-
|
|
32180
|
-
|
|
32181
|
-
|
|
32182
|
-
|
|
32183
|
-
|
|
32184
|
-
|
|
32185
|
-
|
|
32186
|
-
|
|
32187
|
-
|
|
32188
|
-
|
|
32189
|
-
|
|
32190
|
-
|
|
32191
|
-
|
|
32192
|
-
|
|
32193
|
-
|
|
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
|
+
};
|
|
32194
32235
|
}
|
|
32195
32236
|
|
|
32196
32237
|
// node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs
|
|
@@ -32391,7 +32432,7 @@ var IS_DEVELOPMENT = false;
|
|
|
32391
32432
|
|
|
32392
32433
|
// node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
|
|
32393
32434
|
import * as React13 from "react";
|
|
32394
|
-
var
|
|
32435
|
+
var useLayoutEffect22 = globalThis?.document ? React13.useLayoutEffect : () => {
|
|
32395
32436
|
};
|
|
32396
32437
|
|
|
32397
32438
|
// node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
@@ -32415,7 +32456,7 @@ function useEffectEvent(callback) {
|
|
|
32415
32456
|
ref.current = callback;
|
|
32416
32457
|
});
|
|
32417
32458
|
} else {
|
|
32418
|
-
|
|
32459
|
+
useLayoutEffect22(() => {
|
|
32419
32460
|
ref.current = callback;
|
|
32420
32461
|
});
|
|
32421
32462
|
}
|
|
@@ -32426,7 +32467,7 @@ __name4(useEffectEvent, "useEffectEvent");
|
|
|
32426
32467
|
// node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
32427
32468
|
var __defProp6 = Object.defineProperty;
|
|
32428
32469
|
var __name5 = (target, value) => __defProp6(target, "name", { value, configurable: true });
|
|
32429
|
-
var useInsertionEffect = React15[" useInsertionEffect ".trim().toString()] ||
|
|
32470
|
+
var useInsertionEffect = React15[" useInsertionEffect ".trim().toString()] || useLayoutEffect22;
|
|
32430
32471
|
function useControllableState({
|
|
32431
32472
|
prop,
|
|
32432
32473
|
defaultProp,
|
|
@@ -33266,7 +33307,7 @@ function createCollection2(name) {
|
|
|
33266
33307
|
[registryRef]
|
|
33267
33308
|
);
|
|
33268
33309
|
const composedRefs = useComposedRefs(forwardedRef, registerElement, ref);
|
|
33269
|
-
|
|
33310
|
+
useLayoutEffect22(() => {
|
|
33270
33311
|
const node = ref.current;
|
|
33271
33312
|
const registry = registryRef.current;
|
|
33272
33313
|
if (node && registry.has(node)) {
|
|
@@ -33419,7 +33460,7 @@ function useCallbackRef(callback) {
|
|
|
33419
33460
|
callbackRef.current = callback;
|
|
33420
33461
|
});
|
|
33421
33462
|
} else {
|
|
33422
|
-
|
|
33463
|
+
useLayoutEffect22(() => {
|
|
33423
33464
|
callbackRef.current = callback;
|
|
33424
33465
|
});
|
|
33425
33466
|
}
|
|
@@ -34088,7 +34129,7 @@ var useReactId = React26[" useId ".trim().toString()] || (() => void 0);
|
|
|
34088
34129
|
var count2 = 0;
|
|
34089
34130
|
function useId2(deterministicId) {
|
|
34090
34131
|
const [id, setId] = React26.useState(useReactId());
|
|
34091
|
-
|
|
34132
|
+
useLayoutEffect22(() => {
|
|
34092
34133
|
if (!deterministicId) setId((reactId) => reactId ?? String(count2++));
|
|
34093
34134
|
}, [deterministicId]);
|
|
34094
34135
|
return deterministicId || (id ? `radix-${id}` : "");
|
|
@@ -35692,12 +35733,12 @@ var computePosition2 = (reference, floating, options) => {
|
|
|
35692
35733
|
|
|
35693
35734
|
// node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.mjs
|
|
35694
35735
|
import * as React27 from "react";
|
|
35695
|
-
import { useLayoutEffect as
|
|
35736
|
+
import { useLayoutEffect as useLayoutEffect4 } from "react";
|
|
35696
35737
|
import * as ReactDOM3 from "react-dom";
|
|
35697
35738
|
var isClient = typeof document !== "undefined";
|
|
35698
35739
|
var noop = function noop2() {
|
|
35699
35740
|
};
|
|
35700
|
-
var index = isClient ?
|
|
35741
|
+
var index = isClient ? useLayoutEffect4 : noop;
|
|
35701
35742
|
function deepEqual(a, b) {
|
|
35702
35743
|
if (a === b) {
|
|
35703
35744
|
return true;
|
|
@@ -36007,7 +36048,7 @@ var __defProp16 = Object.defineProperty;
|
|
|
36007
36048
|
var __name15 = (target, value) => __defProp16(target, "name", { value, configurable: true });
|
|
36008
36049
|
function useSize(element) {
|
|
36009
36050
|
const [size4, setSize] = React28.useState(void 0);
|
|
36010
|
-
|
|
36051
|
+
useLayoutEffect22(() => {
|
|
36011
36052
|
if (element) {
|
|
36012
36053
|
setSize({ width: element.offsetWidth, height: element.offsetHeight });
|
|
36013
36054
|
let rAF = 0;
|
|
@@ -36214,7 +36255,7 @@ var PopperContent = /* @__PURE__ */ React29.forwardRef(
|
|
|
36214
36255
|
]
|
|
36215
36256
|
});
|
|
36216
36257
|
const setPlacementState = context.setPlacementState;
|
|
36217
|
-
|
|
36258
|
+
useLayoutEffect22(() => {
|
|
36218
36259
|
setPlacementState(placement);
|
|
36219
36260
|
return () => {
|
|
36220
36261
|
setPlacementState(void 0);
|
|
@@ -36222,7 +36263,7 @@ var PopperContent = /* @__PURE__ */ React29.forwardRef(
|
|
|
36222
36263
|
}, [placement, setPlacementState]);
|
|
36223
36264
|
const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
|
|
36224
36265
|
const handlePlaced = useCallbackRef(onPlaced);
|
|
36225
|
-
|
|
36266
|
+
useLayoutEffect22(() => {
|
|
36226
36267
|
if (isPositioned) {
|
|
36227
36268
|
handlePlaced?.();
|
|
36228
36269
|
}
|
|
@@ -36231,7 +36272,7 @@ var PopperContent = /* @__PURE__ */ React29.forwardRef(
|
|
|
36231
36272
|
const arrowY = middlewareData.arrow?.y;
|
|
36232
36273
|
const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
|
|
36233
36274
|
const [contentZIndex, setContentZIndex] = React29.useState();
|
|
36234
|
-
|
|
36275
|
+
useLayoutEffect22(() => {
|
|
36235
36276
|
if (content) setContentZIndex(window.getComputedStyle(content).zIndex);
|
|
36236
36277
|
}, [content]);
|
|
36237
36278
|
return /* @__PURE__ */ jsx32(
|
|
@@ -36341,7 +36382,7 @@ var Portal = /* @__PURE__ */ React30.forwardRef(
|
|
|
36341
36382
|
/* @__PURE__ */ __name17(function Portal2(props, forwardedRef) {
|
|
36342
36383
|
const { container: containerProp, ...portalProps } = props;
|
|
36343
36384
|
const [mounted, setMounted] = React30.useState(false);
|
|
36344
|
-
|
|
36385
|
+
useLayoutEffect22(() => setMounted(true), []);
|
|
36345
36386
|
const container = containerProp || mounted && globalThis?.document?.body;
|
|
36346
36387
|
return container ? ReactDOM4.createPortal(/* @__PURE__ */ jsx33(Primitive.div, { ...portalProps, ref: forwardedRef }), container) : null;
|
|
36347
36388
|
}, "Portal")
|
|
@@ -36395,7 +36436,7 @@ function usePresence(present) {
|
|
|
36395
36436
|
prevAnimationNameRef.current = "none";
|
|
36396
36437
|
}
|
|
36397
36438
|
}, [state]);
|
|
36398
|
-
|
|
36439
|
+
useLayoutEffect22(() => {
|
|
36399
36440
|
const styles = stylesRef.current;
|
|
36400
36441
|
const wasPresent = prevPresentRef.current;
|
|
36401
36442
|
const hasPresentChanged = wasPresent !== present;
|
|
@@ -36418,7 +36459,7 @@ function usePresence(present) {
|
|
|
36418
36459
|
prevPresentRef.current = present;
|
|
36419
36460
|
}
|
|
36420
36461
|
}, [present, send]);
|
|
36421
|
-
|
|
36462
|
+
useLayoutEffect22(() => {
|
|
36422
36463
|
if (node) {
|
|
36423
36464
|
let timeoutId;
|
|
36424
36465
|
const ownerWindow = node.ownerDocument.defaultView ?? window;
|
|
@@ -36698,7 +36739,7 @@ var RovingFocusGroupItem = /* @__PURE__ */ React33.forwardRef(
|
|
|
36698
36739
|
const getItems = useCollection(__scopeRovingFocusGroup);
|
|
36699
36740
|
const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;
|
|
36700
36741
|
const isHydrated = useIsHydrated2();
|
|
36701
|
-
|
|
36742
|
+
useLayoutEffect22(() => {
|
|
36702
36743
|
if (!isHydrated || !focusable) {
|
|
36703
36744
|
return;
|
|
36704
36745
|
}
|
|
@@ -38592,12 +38633,12 @@ var DropdownMenuSubContent = /* @__PURE__ */ React42.forwardRef(/* @__PURE__ */
|
|
|
38592
38633
|
}, "DropdownMenuSubContent"));
|
|
38593
38634
|
|
|
38594
38635
|
// src/components/ViewportSubContent.tsx
|
|
38595
|
-
import { useLayoutEffect as
|
|
38636
|
+
import { useLayoutEffect as useLayoutEffect6, useState as useState40 } from "react";
|
|
38596
38637
|
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
38597
38638
|
var ViewportSubContent = (props) => {
|
|
38598
38639
|
const [content, setContent] = useState40(null);
|
|
38599
38640
|
const [overlapWidth, setOverlapWidth] = useState40();
|
|
38600
|
-
|
|
38641
|
+
useLayoutEffect6(() => {
|
|
38601
38642
|
if (!content) return;
|
|
38602
38643
|
const trigger = document.getElementById(
|
|
38603
38644
|
content.getAttribute("aria-labelledby") ?? ""
|
|
@@ -39740,10 +39781,10 @@ var ContextMenu = ({
|
|
|
39740
39781
|
};
|
|
39741
39782
|
|
|
39742
39783
|
// src/components/KeyboardShortcutsDialog.tsx
|
|
39743
|
-
import { useEffect as useEffect55, useMemo as
|
|
39784
|
+
import { useEffect as useEffect55, useMemo as useMemo40, useRef as useRef28, useState as useState44 } from "react";
|
|
39744
39785
|
|
|
39745
39786
|
// src/hooks/useRegisteredHotkey.ts
|
|
39746
|
-
import { useEffect as useEffect54, useMemo as
|
|
39787
|
+
import { useEffect as useEffect54, useMemo as useMemo39, useRef as useRef27, useState as useState43 } from "react";
|
|
39747
39788
|
var hotkeyRegistry = /* @__PURE__ */ new Map();
|
|
39748
39789
|
var subscribers = /* @__PURE__ */ new Set();
|
|
39749
39790
|
var isListenerAttached = false;
|
|
@@ -39867,7 +39908,7 @@ var subscribeToRegistry = (subscriber) => {
|
|
|
39867
39908
|
var useRegisteredHotkey = (id, handler, metadata) => {
|
|
39868
39909
|
const handlerRef = useRef27(handler);
|
|
39869
39910
|
handlerRef.current = handler;
|
|
39870
|
-
const normalizedMetadata =
|
|
39911
|
+
const normalizedMetadata = useMemo39(
|
|
39871
39912
|
() => ({
|
|
39872
39913
|
shortcut: metadata.shortcut,
|
|
39873
39914
|
description: metadata.description
|
|
@@ -39925,7 +39966,7 @@ var KeyboardShortcutsDialog = ({
|
|
|
39925
39966
|
}, 0);
|
|
39926
39967
|
}
|
|
39927
39968
|
}, [open]);
|
|
39928
|
-
const filteredHotkeys =
|
|
39969
|
+
const filteredHotkeys = useMemo40(() => {
|
|
39929
39970
|
const normalizedQuery = query.trim().toLowerCase();
|
|
39930
39971
|
if (!normalizedQuery) {
|
|
39931
39972
|
return hotkeys;
|
|
@@ -41152,10 +41193,10 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
41152
41193
|
import {
|
|
41153
41194
|
GLTFExporter as GLTFExporter3
|
|
41154
41195
|
} from "three/examples/jsm/exporters/GLTFExporter.js";
|
|
41155
|
-
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";
|
|
41156
41197
|
function useSaveGltfAs(options = {}) {
|
|
41157
41198
|
const parse2 = useParser(options);
|
|
41158
|
-
const link =
|
|
41199
|
+
const link = useMemo41(() => document.createElement("a"), []);
|
|
41159
41200
|
const saveAs = async (filename) => {
|
|
41160
41201
|
const name = filename ?? options.filename ?? "";
|
|
41161
41202
|
if (options.binary == null) options.binary = name.endsWith(".glb");
|
|
@@ -41190,7 +41231,7 @@ function useExportGltfUrl(options = {}) {
|
|
|
41190
41231
|
return [ref, url, error];
|
|
41191
41232
|
}
|
|
41192
41233
|
function useParser(options = {}) {
|
|
41193
|
-
const exporter =
|
|
41234
|
+
const exporter = useMemo41(() => new GLTFExporter3(), []);
|
|
41194
41235
|
return (instance) => {
|
|
41195
41236
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
41196
41237
|
exporter.parse(
|