@tscircuit/3d-viewer 0.0.348 → 0.0.350
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 +5 -6
- package/dist/index.js +293 -229
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { AnyCircuitElement, PcbBoard } from 'circuit-json';
|
|
3
|
-
import * as React from 'react';
|
|
1
|
+
import * as react from 'react';
|
|
4
2
|
import * as THREE from 'three';
|
|
3
|
+
import { AnyCircuitElement, PcbBoard } from 'circuit-json';
|
|
5
4
|
import { GLTFExporterOptions } from 'three-stdlib';
|
|
6
5
|
import { ManifoldToplevel } from 'manifold-3d/manifold.d.ts';
|
|
7
6
|
import { JSDOM } from 'jsdom';
|
|
8
7
|
|
|
9
|
-
declare const CadViewer:
|
|
8
|
+
declare const CadViewer: react.ForwardRefExoticComponent<Omit<any, "ref"> & react.RefAttributes<THREE.Object3D<THREE.Object3DEventMap>>>;
|
|
10
9
|
|
|
11
10
|
interface CircuitToSvgOptions {
|
|
12
11
|
width?: number;
|
|
@@ -34,11 +33,11 @@ type Options = Omit<GLTFExporterOptions, "animations" | "includeCustomExtensions
|
|
|
34
33
|
declare function useSaveGltfAs(options?: Options & {
|
|
35
34
|
filename?: string;
|
|
36
35
|
}): [
|
|
37
|
-
ref3D:
|
|
36
|
+
ref3D: react.ForwardedRef<THREE.Object3D>,
|
|
38
37
|
saveAs: (filename?: string) => Promise<void>
|
|
39
38
|
];
|
|
40
39
|
declare function useExportGltfUrl(options?: Options): [
|
|
41
|
-
ref3D:
|
|
40
|
+
ref3D: react.ForwardedRef<THREE.Object3D>,
|
|
42
41
|
url: string | undefined,
|
|
43
42
|
error: ErrorEvent | undefined
|
|
44
43
|
];
|
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 useState16, useCallback as useCallback8, useRef as useRef10, useEffect as useEffect23, forwardRef as forwardRef5 } from "react";
|
|
14232
14232
|
|
|
14233
14233
|
// src/CadViewerJscad.tsx
|
|
14234
14234
|
import { su as su4 } from "@tscircuit/circuit-json-util";
|
|
@@ -25583,7 +25583,7 @@ import * as THREE10 from "three";
|
|
|
25583
25583
|
// package.json
|
|
25584
25584
|
var package_default = {
|
|
25585
25585
|
name: "@tscircuit/3d-viewer",
|
|
25586
|
-
version: "0.0.
|
|
25586
|
+
version: "0.0.349",
|
|
25587
25587
|
main: "./dist/index.js",
|
|
25588
25588
|
module: "./dist/index.js",
|
|
25589
25589
|
type: "module",
|
|
@@ -25630,7 +25630,7 @@ var package_default = {
|
|
|
25630
25630
|
"@storybook/blocks": "9.0.0-alpha.17",
|
|
25631
25631
|
"@storybook/react-vite": "^9.1.2",
|
|
25632
25632
|
"@tscircuit/circuit-json-util": "^0.0.66",
|
|
25633
|
-
"@tscircuit/core": "^0.0.
|
|
25633
|
+
"@tscircuit/core": "^0.0.673",
|
|
25634
25634
|
"@tscircuit/props": "^0.0.288",
|
|
25635
25635
|
"@types/jsdom": "^21.1.7",
|
|
25636
25636
|
"@types/react": "19",
|
|
@@ -25994,9 +25994,14 @@ var OrbitControls = ({
|
|
|
25994
25994
|
]);
|
|
25995
25995
|
useEffect11(() => {
|
|
25996
25996
|
if (!controls || !onStart) return;
|
|
25997
|
-
|
|
25997
|
+
const handleStart = (event) => {
|
|
25998
|
+
if (event.button !== 2) {
|
|
25999
|
+
onStart();
|
|
26000
|
+
}
|
|
26001
|
+
};
|
|
26002
|
+
controls.addEventListener("start", handleStart);
|
|
25998
26003
|
return () => {
|
|
25999
|
-
controls.removeEventListener("start",
|
|
26004
|
+
controls.removeEventListener("start", handleStart);
|
|
26000
26005
|
};
|
|
26001
26006
|
}, [controls, onStart]);
|
|
26002
26007
|
useEffect11(() => {
|
|
@@ -27763,7 +27768,7 @@ var CadViewerJscad = forwardRef3(
|
|
|
27763
27768
|
|
|
27764
27769
|
// src/CadViewerManifold.tsx
|
|
27765
27770
|
import { su as su13 } from "@tscircuit/circuit-json-util";
|
|
27766
|
-
import { useEffect as useEffect20, useMemo as useMemo18, useState as useState13 } from "react";
|
|
27771
|
+
import { forwardRef as forwardRef4, useEffect as useEffect20, useMemo as useMemo18, useState as useState13 } from "react";
|
|
27767
27772
|
|
|
27768
27773
|
// src/hooks/useManifoldBoardBuilder.ts
|
|
27769
27774
|
import { useState as useState12, useEffect as useEffect19, useMemo as useMemo17, useRef as useRef7 } from "react";
|
|
@@ -28873,50 +28878,51 @@ var BoardMeshes = ({
|
|
|
28873
28878
|
return null;
|
|
28874
28879
|
};
|
|
28875
28880
|
var MANIFOLD_CDN_BASE_URL = "https://cdn.jsdelivr.net/npm/manifold-3d@3.2.1";
|
|
28876
|
-
var CadViewerManifold = (
|
|
28877
|
-
|
|
28878
|
-
|
|
28879
|
-
|
|
28880
|
-
|
|
28881
|
-
|
|
28882
|
-
|
|
28883
|
-
|
|
28884
|
-
|
|
28885
|
-
|
|
28886
|
-
|
|
28887
|
-
|
|
28888
|
-
|
|
28889
|
-
|
|
28890
|
-
|
|
28891
|
-
|
|
28892
|
-
|
|
28893
|
-
|
|
28894
|
-
|
|
28895
|
-
|
|
28896
|
-
|
|
28897
|
-
|
|
28898
|
-
|
|
28899
|
-
|
|
28900
|
-
|
|
28901
|
-
|
|
28902
|
-
|
|
28903
|
-
initManifold(window.ManifoldModule);
|
|
28904
|
-
return;
|
|
28905
|
-
}
|
|
28906
|
-
const eventName = "manifoldLoaded";
|
|
28907
|
-
const handleLoad = () => {
|
|
28881
|
+
var CadViewerManifold = forwardRef4(
|
|
28882
|
+
({
|
|
28883
|
+
circuitJson: circuitJsonProp,
|
|
28884
|
+
autoRotateDisabled,
|
|
28885
|
+
clickToInteractEnabled,
|
|
28886
|
+
onUserInteraction,
|
|
28887
|
+
children
|
|
28888
|
+
}, ref) => {
|
|
28889
|
+
const childrenCircuitJson = useConvertChildrenToSoup(children);
|
|
28890
|
+
const circuitJson = useMemo18(() => {
|
|
28891
|
+
return circuitJsonProp ?? childrenCircuitJson;
|
|
28892
|
+
}, [circuitJsonProp, childrenCircuitJson]);
|
|
28893
|
+
const [manifoldJSModule, setManifoldJSModule] = useState13(null);
|
|
28894
|
+
const [manifoldLoadingError, setManifoldLoadingError] = useState13(null);
|
|
28895
|
+
useEffect20(() => {
|
|
28896
|
+
const initManifold = async (ManifoldModule) => {
|
|
28897
|
+
try {
|
|
28898
|
+
const loadedModule = await ManifoldModule();
|
|
28899
|
+
loadedModule.setup();
|
|
28900
|
+
setManifoldJSModule(loadedModule);
|
|
28901
|
+
} catch (error) {
|
|
28902
|
+
console.error("Failed to initialize Manifold:", error);
|
|
28903
|
+
setManifoldLoadingError(
|
|
28904
|
+
`Failed to initialize Manifold: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
28905
|
+
);
|
|
28906
|
+
}
|
|
28907
|
+
};
|
|
28908
28908
|
if (window.ManifoldModule) {
|
|
28909
28909
|
initManifold(window.ManifoldModule);
|
|
28910
|
-
|
|
28911
|
-
const errText = "ManifoldModule not found on window after script load.";
|
|
28912
|
-
console.error(errText);
|
|
28913
|
-
setManifoldLoadingError(errText);
|
|
28910
|
+
return;
|
|
28914
28911
|
}
|
|
28915
|
-
|
|
28916
|
-
|
|
28917
|
-
|
|
28918
|
-
|
|
28919
|
-
|
|
28912
|
+
const eventName = "manifoldLoaded";
|
|
28913
|
+
const handleLoad = () => {
|
|
28914
|
+
if (window.ManifoldModule) {
|
|
28915
|
+
initManifold(window.ManifoldModule);
|
|
28916
|
+
} else {
|
|
28917
|
+
const errText = "ManifoldModule not found on window after script load.";
|
|
28918
|
+
console.error(errText);
|
|
28919
|
+
setManifoldLoadingError(errText);
|
|
28920
|
+
}
|
|
28921
|
+
};
|
|
28922
|
+
window.addEventListener(eventName, handleLoad, { once: true });
|
|
28923
|
+
const script = document.createElement("script");
|
|
28924
|
+
script.type = "module";
|
|
28925
|
+
script.innerHTML = `
|
|
28920
28926
|
try {
|
|
28921
28927
|
const { default: ManifoldModule } = await import('${MANIFOLD_CDN_BASE_URL}/manifold.js');
|
|
28922
28928
|
window.ManifoldModule = ManifoldModule;
|
|
@@ -28926,123 +28932,125 @@ try {
|
|
|
28926
28932
|
window.dispatchEvent(new CustomEvent('${eventName}'));
|
|
28927
28933
|
}
|
|
28928
28934
|
`.trim();
|
|
28929
|
-
|
|
28930
|
-
|
|
28931
|
-
|
|
28932
|
-
|
|
28933
|
-
|
|
28934
|
-
|
|
28935
|
-
|
|
28936
|
-
|
|
28937
|
-
|
|
28938
|
-
|
|
28939
|
-
|
|
28940
|
-
|
|
28941
|
-
|
|
28942
|
-
|
|
28943
|
-
|
|
28944
|
-
|
|
28945
|
-
|
|
28946
|
-
|
|
28947
|
-
|
|
28948
|
-
|
|
28949
|
-
|
|
28950
|
-
|
|
28951
|
-
|
|
28952
|
-
|
|
28953
|
-
|
|
28954
|
-
);
|
|
28955
|
-
const cadComponents = useMemo18(
|
|
28956
|
-
() => su13(circuitJson).cad_component.list(),
|
|
28957
|
-
[circuitJson]
|
|
28958
|
-
);
|
|
28959
|
-
const boardDimensions = useMemo18(() => {
|
|
28960
|
-
if (!boardData) return void 0;
|
|
28961
|
-
const { width: width10 = 0, height: height10 = 0 } = boardData;
|
|
28962
|
-
return { width: width10, height: height10 };
|
|
28963
|
-
}, [boardData]);
|
|
28964
|
-
const initialCameraPosition = useMemo18(() => {
|
|
28965
|
-
if (!boardData) return [5, 5, 5];
|
|
28966
|
-
const { width: width10 = 0, height: height10 = 0 } = boardData;
|
|
28967
|
-
const safeWidth = Math.max(width10, 1);
|
|
28968
|
-
const safeHeight = Math.max(height10, 1);
|
|
28969
|
-
const largestDim = Math.max(safeWidth, safeHeight, 5);
|
|
28970
|
-
return [largestDim * 0.75, largestDim * 0.75, largestDim * 0.75];
|
|
28971
|
-
}, [boardData]);
|
|
28972
|
-
if (manifoldLoadingError) {
|
|
28973
|
-
return /* @__PURE__ */ jsxs7(
|
|
28974
|
-
"div",
|
|
28975
|
-
{
|
|
28976
|
-
style: {
|
|
28977
|
-
color: "red",
|
|
28978
|
-
padding: "1em",
|
|
28979
|
-
border: "1px solid red",
|
|
28980
|
-
margin: "1em"
|
|
28981
|
-
},
|
|
28982
|
-
children: [
|
|
28983
|
-
"Error: ",
|
|
28984
|
-
manifoldLoadingError
|
|
28985
|
-
]
|
|
28986
|
-
}
|
|
28935
|
+
const scriptError = (err) => {
|
|
28936
|
+
const errText = "Failed to load Manifold loader script.";
|
|
28937
|
+
console.error(errText, err);
|
|
28938
|
+
setManifoldLoadingError(errText);
|
|
28939
|
+
window.removeEventListener(eventName, handleLoad);
|
|
28940
|
+
};
|
|
28941
|
+
script.addEventListener("error", scriptError);
|
|
28942
|
+
document.body.appendChild(script);
|
|
28943
|
+
return () => {
|
|
28944
|
+
window.removeEventListener(eventName, handleLoad);
|
|
28945
|
+
script.removeEventListener("error", scriptError);
|
|
28946
|
+
};
|
|
28947
|
+
}, []);
|
|
28948
|
+
const {
|
|
28949
|
+
geoms,
|
|
28950
|
+
textures,
|
|
28951
|
+
pcbThickness,
|
|
28952
|
+
error: builderError,
|
|
28953
|
+
isLoading: builderIsLoading,
|
|
28954
|
+
boardData
|
|
28955
|
+
} = useManifoldBoardBuilder(manifoldJSModule, circuitJson);
|
|
28956
|
+
const geometryMeshes = useMemo18(() => createGeometryMeshes(geoms), [geoms]);
|
|
28957
|
+
const textureMeshes = useMemo18(
|
|
28958
|
+
() => createTextureMeshes(textures, boardData, pcbThickness),
|
|
28959
|
+
[textures, boardData, pcbThickness]
|
|
28987
28960
|
);
|
|
28988
|
-
|
|
28989
|
-
|
|
28990
|
-
|
|
28991
|
-
|
|
28992
|
-
|
|
28961
|
+
const cadComponents = useMemo18(
|
|
28962
|
+
() => su13(circuitJson).cad_component.list(),
|
|
28963
|
+
[circuitJson]
|
|
28964
|
+
);
|
|
28965
|
+
const boardDimensions = useMemo18(() => {
|
|
28966
|
+
if (!boardData) return void 0;
|
|
28967
|
+
const { width: width10 = 0, height: height10 = 0 } = boardData;
|
|
28968
|
+
return { width: width10, height: height10 };
|
|
28969
|
+
}, [boardData]);
|
|
28970
|
+
const initialCameraPosition = useMemo18(() => {
|
|
28971
|
+
if (!boardData) return [5, 5, 5];
|
|
28972
|
+
const { width: width10 = 0, height: height10 = 0 } = boardData;
|
|
28973
|
+
const safeWidth = Math.max(width10, 1);
|
|
28974
|
+
const safeHeight = Math.max(height10, 1);
|
|
28975
|
+
const largestDim = Math.max(safeWidth, safeHeight, 5);
|
|
28976
|
+
return [largestDim * 0.75, largestDim * 0.75, largestDim * 0.75];
|
|
28977
|
+
}, [boardData]);
|
|
28978
|
+
if (manifoldLoadingError) {
|
|
28979
|
+
return /* @__PURE__ */ jsxs7(
|
|
28980
|
+
"div",
|
|
28981
|
+
{
|
|
28982
|
+
style: {
|
|
28983
|
+
color: "red",
|
|
28984
|
+
padding: "1em",
|
|
28985
|
+
border: "1px solid red",
|
|
28986
|
+
margin: "1em"
|
|
28987
|
+
},
|
|
28988
|
+
children: [
|
|
28989
|
+
"Error: ",
|
|
28990
|
+
manifoldLoadingError
|
|
28991
|
+
]
|
|
28992
|
+
}
|
|
28993
|
+
);
|
|
28994
|
+
}
|
|
28995
|
+
if (!manifoldJSModule) {
|
|
28996
|
+
return /* @__PURE__ */ jsx13("div", { style: { padding: "1em" }, children: "Loading Manifold module..." });
|
|
28997
|
+
}
|
|
28998
|
+
if (builderError) {
|
|
28999
|
+
return /* @__PURE__ */ jsxs7(
|
|
29000
|
+
"div",
|
|
29001
|
+
{
|
|
29002
|
+
style: {
|
|
29003
|
+
color: "red",
|
|
29004
|
+
padding: "1em",
|
|
29005
|
+
border: "1px solid red",
|
|
29006
|
+
margin: "1em"
|
|
29007
|
+
},
|
|
29008
|
+
children: [
|
|
29009
|
+
"Error: ",
|
|
29010
|
+
builderError
|
|
29011
|
+
]
|
|
29012
|
+
}
|
|
29013
|
+
);
|
|
29014
|
+
}
|
|
29015
|
+
if (builderIsLoading) {
|
|
29016
|
+
return /* @__PURE__ */ jsx13("div", { style: { padding: "1em" }, children: "Processing board geometry..." });
|
|
29017
|
+
}
|
|
28993
29018
|
return /* @__PURE__ */ jsxs7(
|
|
28994
|
-
|
|
29019
|
+
CadViewerContainer,
|
|
28995
29020
|
{
|
|
28996
|
-
|
|
28997
|
-
|
|
28998
|
-
|
|
28999
|
-
|
|
29000
|
-
|
|
29001
|
-
|
|
29021
|
+
ref,
|
|
29022
|
+
initialCameraPosition,
|
|
29023
|
+
autoRotateDisabled,
|
|
29024
|
+
clickToInteractEnabled,
|
|
29025
|
+
boardDimensions,
|
|
29026
|
+
onUserInteraction,
|
|
29002
29027
|
children: [
|
|
29003
|
-
|
|
29004
|
-
|
|
29028
|
+
/* @__PURE__ */ jsx13(
|
|
29029
|
+
BoardMeshes,
|
|
29030
|
+
{
|
|
29031
|
+
geometryMeshes,
|
|
29032
|
+
textureMeshes
|
|
29033
|
+
}
|
|
29034
|
+
),
|
|
29035
|
+
cadComponents.map((cad_component2) => /* @__PURE__ */ jsx13(
|
|
29036
|
+
ThreeErrorBoundary,
|
|
29037
|
+
{
|
|
29038
|
+
fallback: ({ error }) => /* @__PURE__ */ jsx13(Error3d, { cad_component: cad_component2, error }),
|
|
29039
|
+
children: /* @__PURE__ */ jsx13(
|
|
29040
|
+
AnyCadComponent,
|
|
29041
|
+
{
|
|
29042
|
+
cad_component: cad_component2,
|
|
29043
|
+
circuitJson
|
|
29044
|
+
}
|
|
29045
|
+
)
|
|
29046
|
+
},
|
|
29047
|
+
cad_component2.cad_component_id
|
|
29048
|
+
))
|
|
29005
29049
|
]
|
|
29006
29050
|
}
|
|
29007
29051
|
);
|
|
29008
29052
|
}
|
|
29009
|
-
|
|
29010
|
-
return /* @__PURE__ */ jsx13("div", { style: { padding: "1em" }, children: "Processing board geometry..." });
|
|
29011
|
-
}
|
|
29012
|
-
return /* @__PURE__ */ jsxs7(
|
|
29013
|
-
CadViewerContainer,
|
|
29014
|
-
{
|
|
29015
|
-
initialCameraPosition,
|
|
29016
|
-
autoRotateDisabled,
|
|
29017
|
-
clickToInteractEnabled,
|
|
29018
|
-
boardDimensions,
|
|
29019
|
-
onUserInteraction,
|
|
29020
|
-
children: [
|
|
29021
|
-
/* @__PURE__ */ jsx13(
|
|
29022
|
-
BoardMeshes,
|
|
29023
|
-
{
|
|
29024
|
-
geometryMeshes,
|
|
29025
|
-
textureMeshes
|
|
29026
|
-
}
|
|
29027
|
-
),
|
|
29028
|
-
cadComponents.map((cad_component2) => /* @__PURE__ */ jsx13(
|
|
29029
|
-
ThreeErrorBoundary,
|
|
29030
|
-
{
|
|
29031
|
-
fallback: ({ error }) => /* @__PURE__ */ jsx13(Error3d, { cad_component: cad_component2, error }),
|
|
29032
|
-
children: /* @__PURE__ */ jsx13(
|
|
29033
|
-
AnyCadComponent,
|
|
29034
|
-
{
|
|
29035
|
-
cad_component: cad_component2,
|
|
29036
|
-
circuitJson
|
|
29037
|
-
}
|
|
29038
|
-
)
|
|
29039
|
-
},
|
|
29040
|
-
cad_component2.cad_component_id
|
|
29041
|
-
))
|
|
29042
|
-
]
|
|
29043
|
-
}
|
|
29044
|
-
);
|
|
29045
|
-
};
|
|
29053
|
+
);
|
|
29046
29054
|
var CadViewerManifold_default = CadViewerManifold;
|
|
29047
29055
|
|
|
29048
29056
|
// src/hooks/useContextMenu.ts
|
|
@@ -29147,13 +29155,87 @@ var useContextMenu = ({ containerRef }) => {
|
|
|
29147
29155
|
};
|
|
29148
29156
|
};
|
|
29149
29157
|
|
|
29158
|
+
// src/hooks/exporter/gltf.ts
|
|
29159
|
+
import { GLTFExporter } from "three-stdlib";
|
|
29160
|
+
import { useEffect as useEffect22, useState as useState15, useMemo as useMemo19, useCallback as useCallback7, useRef as useRef9 } from "react";
|
|
29161
|
+
function useSaveGltfAs(options = {}) {
|
|
29162
|
+
const parse = useParser(options);
|
|
29163
|
+
const link = useMemo19(() => document.createElement("a"), []);
|
|
29164
|
+
const instanceRef = useRef9(null);
|
|
29165
|
+
const saveAs = async (filename) => {
|
|
29166
|
+
const name = filename ?? options.filename ?? "";
|
|
29167
|
+
if (options.binary == null) options.binary = name.endsWith(".glb");
|
|
29168
|
+
if (!instanceRef.current) {
|
|
29169
|
+
console.error("No 3D object available for export");
|
|
29170
|
+
return;
|
|
29171
|
+
}
|
|
29172
|
+
try {
|
|
29173
|
+
const url = await parse(instanceRef.current);
|
|
29174
|
+
link.download = name;
|
|
29175
|
+
link.href = url;
|
|
29176
|
+
link.dispatchEvent(new MouseEvent("click"));
|
|
29177
|
+
URL.revokeObjectURL(url);
|
|
29178
|
+
} catch (error) {
|
|
29179
|
+
console.error("Failed to export GLTF:", error);
|
|
29180
|
+
}
|
|
29181
|
+
};
|
|
29182
|
+
useEffect22(
|
|
29183
|
+
() => () => {
|
|
29184
|
+
link.remove();
|
|
29185
|
+
instanceRef.current = null;
|
|
29186
|
+
},
|
|
29187
|
+
[]
|
|
29188
|
+
);
|
|
29189
|
+
const ref = useCallback7((obj3D) => {
|
|
29190
|
+
instanceRef.current = obj3D;
|
|
29191
|
+
}, []);
|
|
29192
|
+
return [ref, saveAs];
|
|
29193
|
+
}
|
|
29194
|
+
function useExportGltfUrl(options = {}) {
|
|
29195
|
+
const parse = useParser(options);
|
|
29196
|
+
const [url, setUrl] = useState15();
|
|
29197
|
+
const [error, setError] = useState15();
|
|
29198
|
+
const instanceRef = useRef9(null);
|
|
29199
|
+
const ref = useCallback7(
|
|
29200
|
+
(instance) => {
|
|
29201
|
+
instanceRef.current = instance;
|
|
29202
|
+
if (instance) {
|
|
29203
|
+
parse(instance).then(setUrl).catch(setError);
|
|
29204
|
+
}
|
|
29205
|
+
},
|
|
29206
|
+
[parse]
|
|
29207
|
+
);
|
|
29208
|
+
useEffect22(() => () => URL.revokeObjectURL(url), [url]);
|
|
29209
|
+
return [ref, url, error];
|
|
29210
|
+
}
|
|
29211
|
+
function useParser(options = {}) {
|
|
29212
|
+
const exporter = useMemo19(() => new GLTFExporter(), []);
|
|
29213
|
+
return (instance) => {
|
|
29214
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
29215
|
+
exporter.parse(
|
|
29216
|
+
instance,
|
|
29217
|
+
(gltf) => {
|
|
29218
|
+
const type = options.binary ? "gltf-binary" : "gltf+json";
|
|
29219
|
+
const blob = new Blob(
|
|
29220
|
+
[gltf instanceof ArrayBuffer ? gltf : JSON.stringify(gltf)],
|
|
29221
|
+
{ type: `model/${type}` }
|
|
29222
|
+
);
|
|
29223
|
+
resolve(URL.createObjectURL(blob));
|
|
29224
|
+
},
|
|
29225
|
+
reject,
|
|
29226
|
+
options
|
|
29227
|
+
);
|
|
29228
|
+
return promise;
|
|
29229
|
+
};
|
|
29230
|
+
}
|
|
29231
|
+
|
|
29150
29232
|
// src/CadViewer.tsx
|
|
29151
29233
|
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
29152
|
-
var CadViewer = (props) => {
|
|
29153
|
-
const [engine, setEngine] =
|
|
29154
|
-
const containerRef =
|
|
29155
|
-
const [autoRotate, setAutoRotate] =
|
|
29156
|
-
const [autoRotateUserToggled, setAutoRotateUserToggled] =
|
|
29234
|
+
var CadViewer = forwardRef5((props, ref) => {
|
|
29235
|
+
const [engine, setEngine] = useState16("manifold");
|
|
29236
|
+
const containerRef = useRef10(null);
|
|
29237
|
+
const [autoRotate, setAutoRotate] = useState16(true);
|
|
29238
|
+
const [autoRotateUserToggled, setAutoRotateUserToggled] = useState16(false);
|
|
29157
29239
|
const {
|
|
29158
29240
|
menuVisible,
|
|
29159
29241
|
menuPos,
|
|
@@ -29161,14 +29243,28 @@ var CadViewer = (props) => {
|
|
|
29161
29243
|
contextMenuEventHandlers,
|
|
29162
29244
|
setMenuVisible
|
|
29163
29245
|
} = useContextMenu({ containerRef });
|
|
29164
|
-
const
|
|
29246
|
+
const [sceneRef, saveGltfAs] = useSaveGltfAs();
|
|
29247
|
+
function useCombinedRefs(...refs) {
|
|
29248
|
+
return useCallback8(
|
|
29249
|
+
(value) => {
|
|
29250
|
+
refs.forEach((ref2) => {
|
|
29251
|
+
if (!ref2) return;
|
|
29252
|
+
if (typeof ref2 === "function") ref2(value);
|
|
29253
|
+
else ref2.current = value;
|
|
29254
|
+
});
|
|
29255
|
+
},
|
|
29256
|
+
[refs]
|
|
29257
|
+
);
|
|
29258
|
+
}
|
|
29259
|
+
const mergedRef = useCombinedRefs(ref, sceneRef);
|
|
29260
|
+
const autoRotateUserToggledRef = useRef10(autoRotateUserToggled);
|
|
29165
29261
|
autoRotateUserToggledRef.current = autoRotateUserToggled;
|
|
29166
|
-
const handleUserInteraction =
|
|
29262
|
+
const handleUserInteraction = useCallback8(() => {
|
|
29167
29263
|
if (!autoRotateUserToggledRef.current) {
|
|
29168
29264
|
setAutoRotate(false);
|
|
29169
29265
|
}
|
|
29170
29266
|
}, []);
|
|
29171
|
-
const toggleAutoRotate =
|
|
29267
|
+
const toggleAutoRotate = useCallback8(() => {
|
|
29172
29268
|
setAutoRotate((prev) => !prev);
|
|
29173
29269
|
setAutoRotateUserToggled(true);
|
|
29174
29270
|
}, []);
|
|
@@ -29176,13 +29272,17 @@ var CadViewer = (props) => {
|
|
|
29176
29272
|
setEngine(newEngine);
|
|
29177
29273
|
setMenuVisible(false);
|
|
29178
29274
|
};
|
|
29179
|
-
|
|
29275
|
+
const handleDownloadGltf = useCallback8(() => {
|
|
29276
|
+
saveGltfAs("pcb.glb");
|
|
29277
|
+
setMenuVisible(false);
|
|
29278
|
+
}, [saveGltfAs, setMenuVisible]);
|
|
29279
|
+
useEffect23(() => {
|
|
29180
29280
|
const stored = window.localStorage.getItem("cadViewerEngine");
|
|
29181
29281
|
if (stored === "jscad" || stored === "manifold") {
|
|
29182
29282
|
setEngine(stored);
|
|
29183
29283
|
}
|
|
29184
29284
|
}, []);
|
|
29185
|
-
|
|
29285
|
+
useEffect23(() => {
|
|
29186
29286
|
window.localStorage.setItem("cadViewerEngine", engine);
|
|
29187
29287
|
}, [engine]);
|
|
29188
29288
|
const viewerKey = props.circuitJson ? JSON.stringify(props.circuitJson) : void 0;
|
|
@@ -29197,15 +29297,19 @@ var CadViewer = (props) => {
|
|
|
29197
29297
|
CadViewerJscad,
|
|
29198
29298
|
{
|
|
29199
29299
|
...props,
|
|
29300
|
+
ref: mergedRef,
|
|
29200
29301
|
autoRotateDisabled: props.autoRotateDisabled || !autoRotate,
|
|
29201
|
-
onUserInteraction: handleUserInteraction
|
|
29302
|
+
onUserInteraction: handleUserInteraction,
|
|
29303
|
+
children: props.children
|
|
29202
29304
|
}
|
|
29203
29305
|
) : /* @__PURE__ */ jsx14(
|
|
29204
29306
|
CadViewerManifold_default,
|
|
29205
29307
|
{
|
|
29206
29308
|
...props,
|
|
29309
|
+
ref: mergedRef,
|
|
29207
29310
|
autoRotateDisabled: props.autoRotateDisabled || !autoRotate,
|
|
29208
|
-
onUserInteraction: handleUserInteraction
|
|
29311
|
+
onUserInteraction: handleUserInteraction,
|
|
29312
|
+
children: props.children
|
|
29209
29313
|
}
|
|
29210
29314
|
),
|
|
29211
29315
|
/* @__PURE__ */ jsxs8(
|
|
@@ -29312,6 +29416,26 @@ var CadViewer = (props) => {
|
|
|
29312
29416
|
]
|
|
29313
29417
|
}
|
|
29314
29418
|
),
|
|
29419
|
+
/* @__PURE__ */ jsx14(
|
|
29420
|
+
"div",
|
|
29421
|
+
{
|
|
29422
|
+
style: {
|
|
29423
|
+
padding: "12px 18px",
|
|
29424
|
+
cursor: "pointer",
|
|
29425
|
+
display: "flex",
|
|
29426
|
+
alignItems: "center",
|
|
29427
|
+
gap: 10,
|
|
29428
|
+
color: "#f5f6fa",
|
|
29429
|
+
fontWeight: 500,
|
|
29430
|
+
borderRadius: 6,
|
|
29431
|
+
transition: "background 0.1s"
|
|
29432
|
+
},
|
|
29433
|
+
onClick: handleDownloadGltf,
|
|
29434
|
+
onMouseOver: (e) => e.currentTarget.style.background = "#2d313a",
|
|
29435
|
+
onMouseOut: (e) => e.currentTarget.style.background = "transparent",
|
|
29436
|
+
children: "Download GLTF"
|
|
29437
|
+
}
|
|
29438
|
+
),
|
|
29315
29439
|
/* @__PURE__ */ jsx14(
|
|
29316
29440
|
"div",
|
|
29317
29441
|
{
|
|
@@ -29346,7 +29470,7 @@ var CadViewer = (props) => {
|
|
|
29346
29470
|
},
|
|
29347
29471
|
viewerKey
|
|
29348
29472
|
);
|
|
29349
|
-
};
|
|
29473
|
+
});
|
|
29350
29474
|
|
|
29351
29475
|
// src/convert-circuit-json-to-3d-svg.ts
|
|
29352
29476
|
var import_debug = __toESM(require_browser(), 1);
|
|
@@ -29615,66 +29739,6 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
29615
29739
|
return serialized;
|
|
29616
29740
|
}
|
|
29617
29741
|
|
|
29618
|
-
// src/hooks/exporter/gltf.ts
|
|
29619
|
-
import { GLTFExporter } from "three-stdlib";
|
|
29620
|
-
import { useEffect as useEffect23, useState as useState16, useMemo as useMemo19, useCallback as useCallback8 } from "react";
|
|
29621
|
-
function useSaveGltfAs(options = {}) {
|
|
29622
|
-
const parse = useParser(options);
|
|
29623
|
-
const link = useMemo19(() => document.createElement("a"), []);
|
|
29624
|
-
const saveAs = async (filename) => {
|
|
29625
|
-
const name = filename ?? options.filename ?? "";
|
|
29626
|
-
if (options.binary == null) options.binary = name.endsWith(".glb");
|
|
29627
|
-
const url = await parse(instance);
|
|
29628
|
-
link.download = name;
|
|
29629
|
-
link.href = url;
|
|
29630
|
-
link.dispatchEvent(new MouseEvent("click"));
|
|
29631
|
-
URL.revokeObjectURL(url);
|
|
29632
|
-
};
|
|
29633
|
-
useEffect23(
|
|
29634
|
-
() => () => {
|
|
29635
|
-
link.remove();
|
|
29636
|
-
instance = null;
|
|
29637
|
-
},
|
|
29638
|
-
[]
|
|
29639
|
-
);
|
|
29640
|
-
let instance;
|
|
29641
|
-
const ref = useCallback8((obj3D) => {
|
|
29642
|
-
instance = obj3D;
|
|
29643
|
-
}, []);
|
|
29644
|
-
return [ref, saveAs];
|
|
29645
|
-
}
|
|
29646
|
-
function useExportGltfUrl(options = {}) {
|
|
29647
|
-
const parse = useParser(options);
|
|
29648
|
-
const [url, setUrl] = useState16();
|
|
29649
|
-
const [error, setError] = useState16();
|
|
29650
|
-
const ref = useCallback8(
|
|
29651
|
-
(instance) => parse(instance).then(setUrl).catch(setError),
|
|
29652
|
-
[]
|
|
29653
|
-
);
|
|
29654
|
-
useEffect23(() => () => URL.revokeObjectURL(url), [url]);
|
|
29655
|
-
return [ref, url, error];
|
|
29656
|
-
}
|
|
29657
|
-
function useParser(options = {}) {
|
|
29658
|
-
const exporter = useMemo19(() => new GLTFExporter(), []);
|
|
29659
|
-
return (instance) => {
|
|
29660
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
29661
|
-
exporter.parse(
|
|
29662
|
-
instance,
|
|
29663
|
-
(gltf) => {
|
|
29664
|
-
const type = options.binary ? "gltf-binary" : "gltf+json";
|
|
29665
|
-
const blob = new Blob(
|
|
29666
|
-
[gltf instanceof ArrayBuffer ? gltf : JSON.stringify(gltf)],
|
|
29667
|
-
{ type: `model/${type}` }
|
|
29668
|
-
);
|
|
29669
|
-
resolve(URL.createObjectURL(blob));
|
|
29670
|
-
},
|
|
29671
|
-
reject,
|
|
29672
|
-
options
|
|
29673
|
-
);
|
|
29674
|
-
return promise;
|
|
29675
|
-
};
|
|
29676
|
-
}
|
|
29677
|
-
|
|
29678
29742
|
// src/utils/jsdom-shim.ts
|
|
29679
29743
|
function applyJsdomShim(jsdom) {
|
|
29680
29744
|
global.window = jsdom.window;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/3d-viewer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.350",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@storybook/blocks": "9.0.0-alpha.17",
|
|
48
48
|
"@storybook/react-vite": "^9.1.2",
|
|
49
49
|
"@tscircuit/circuit-json-util": "^0.0.66",
|
|
50
|
-
"@tscircuit/core": "^0.0.
|
|
50
|
+
"@tscircuit/core": "^0.0.673",
|
|
51
51
|
"@tscircuit/props": "^0.0.288",
|
|
52
52
|
"@types/jsdom": "^21.1.7",
|
|
53
53
|
"@types/react": "19",
|