@tscircuit/3d-viewer 0.0.639 → 0.0.641

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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/dist/index.js +70 -33
  3. 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.js CHANGED
@@ -19303,6 +19303,17 @@ var useObjectVisibility = (circuitKey) => {
19303
19303
  };
19304
19304
  };
19305
19305
 
19306
+ // src/utils/find-model-object.ts
19307
+ var findModelObject = (root, objectId) => {
19308
+ let model;
19309
+ root.traverse((object) => {
19310
+ const { cad_component_id, cad_cable_id } = object.userData;
19311
+ if (!model && (cad_component_id ?? cad_cable_id) === objectId)
19312
+ model = object;
19313
+ });
19314
+ return model;
19315
+ };
19316
+
19306
19317
  // src/utils/configure-runtime-font.ts
19307
19318
  import { configureTextBuilder } from "troika-three-text";
19308
19319
 
@@ -22333,7 +22344,7 @@ import * as THREE31 from "three";
22333
22344
  // package.json
22334
22345
  var package_default = {
22335
22346
  name: "@tscircuit/3d-viewer",
22336
- version: "0.0.638",
22347
+ version: "0.0.640",
22337
22348
  repository: {
22338
22349
  type: "git",
22339
22350
  url: "https://github.com/tscircuit/3d-viewer"
@@ -22849,6 +22860,7 @@ var Canvas = forwardRef(
22849
22860
  mountRef.current.removeChild(renderer.domElement);
22850
22861
  }
22851
22862
  renderer.dispose();
22863
+ renderer.forceContextLoss();
22852
22864
  scene.remove(rootObject.current);
22853
22865
  if (window.__TSCIRCUIT_THREE_OBJECT === rootObject.current) {
22854
22866
  window.__TSCIRCUIT_THREE_OBJECT = void 0;
@@ -39249,6 +39261,7 @@ var ContextMenu = ({
39249
39261
  onSectionView,
39250
39262
  onExplosionView,
39251
39263
  explosionAvailable = false,
39264
+ downloadModelName,
39252
39265
  onDownloadGltf,
39253
39266
  onOpenKeyboardShortcuts
39254
39267
  }) => {
@@ -39559,7 +39572,7 @@ var ContextMenu = ({
39559
39572
  }
39560
39573
  ),
39561
39574
  /* @__PURE__ */ jsx40(DropdownMenuSeparator, { style: separatorStyles2 }),
39562
- /* @__PURE__ */ jsx40(
39575
+ /* @__PURE__ */ jsxs10(
39563
39576
  DropdownMenuItem,
39564
39577
  {
39565
39578
  style: {
@@ -39571,7 +39584,10 @@ var ContextMenu = ({
39571
39584
  onMouseEnter: () => setHoveredItem("download"),
39572
39585
  onMouseLeave: () => setHoveredItem(null),
39573
39586
  onTouchStart: () => setHoveredItem("download"),
39574
- children: /* @__PURE__ */ jsx40("span", { style: { display: "flex", alignItems: "center" }, children: "Download GLTF" })
39587
+ children: [
39588
+ /* @__PURE__ */ jsx40("span", { style: { flex: 1, display: "flex", alignItems: "center" }, children: "Download GLTF" }),
39589
+ downloadModelName && /* @__PURE__ */ jsx40("span", { style: { opacity: 0.55, fontSize: 13 }, children: downloadModelName })
39590
+ ]
39575
39591
  }
39576
39592
  ),
39577
39593
  /* @__PURE__ */ jsx40(DropdownMenuSeparator, { style: separatorStyles2 }),
@@ -40400,34 +40416,51 @@ function createSectionExport(root, spec) {
40400
40416
  // src/hooks/useGlobalDownloadGltf.ts
40401
40417
  import { useCallback as useCallback24 } from "react";
40402
40418
  import { GLTFExporter as GLTFExporter2 } from "three/examples/jsm/exporters/GLTFExporter.js";
40419
+
40420
+ // src/utils/clone-model-for-export.ts
40421
+ var cloneModelForExport = (root, objectId) => {
40422
+ const source = findModelObject(root, objectId);
40423
+ if (!source) return;
40424
+ source.updateWorldMatrix(true, true);
40425
+ const output = source.clone(true);
40426
+ source.matrixWorld.decompose(output.position, output.quaternion, output.scale);
40427
+ return output;
40428
+ };
40429
+
40430
+ // src/hooks/useGlobalDownloadGltf.ts
40403
40431
  var useGlobalDownloadGltf = () => {
40404
- return useCallback24(() => {
40405
- const root = window.__TSCIRCUIT_THREE_OBJECT;
40406
- if (!root) return;
40407
- const section = root.userData.sectionView;
40408
- const sectionExport = section ? createSectionExport(root, section) : void 0;
40409
- const exporter = new GLTFExporter2();
40410
- exporter.parse(
40411
- sectionExport?.root ?? root,
40412
- (gltf) => {
40413
- sectionExport?.dispose();
40414
- const blob = new Blob(
40415
- [gltf instanceof ArrayBuffer ? gltf : JSON.stringify(gltf)],
40416
- { type: "model/gltf+json" }
40417
- );
40418
- const url = URL.createObjectURL(blob);
40419
- const link = document.createElement("a");
40420
- link.href = url;
40421
- link.download = "scene.gltf";
40422
- link.click();
40423
- URL.revokeObjectURL(url);
40424
- },
40425
- (err) => {
40426
- sectionExport?.dispose();
40427
- console.error("Failed to export GLTF", err);
40428
- }
40429
- );
40430
- }, []);
40432
+ return useCallback24(
40433
+ (model, root = window.__TSCIRCUIT_THREE_OBJECT) => {
40434
+ if (!root) return;
40435
+ const target = model ? cloneModelForExport(root, model.objectId) : root;
40436
+ if (!target) return;
40437
+ if (model) target.name = model.name;
40438
+ const section = root.userData.sectionView;
40439
+ const sectionExport = section ? createSectionExport(target, section) : void 0;
40440
+ const exporter = new GLTFExporter2();
40441
+ exporter.parse(
40442
+ sectionExport?.root ?? target,
40443
+ (gltf) => {
40444
+ sectionExport?.dispose();
40445
+ const blob = new Blob(
40446
+ [gltf instanceof ArrayBuffer ? gltf : JSON.stringify(gltf)],
40447
+ { type: "model/gltf+json" }
40448
+ );
40449
+ const url = URL.createObjectURL(blob);
40450
+ const link = document.createElement("a");
40451
+ link.href = url;
40452
+ link.download = model ? `${model.name}.gltf` : "scene.gltf";
40453
+ link.click();
40454
+ URL.revokeObjectURL(url);
40455
+ },
40456
+ (err) => {
40457
+ sectionExport?.dispose();
40458
+ console.error("Failed to export GLTF", err);
40459
+ }
40460
+ );
40461
+ },
40462
+ []
40463
+ );
40431
40464
  };
40432
40465
 
40433
40466
  // src/CadViewer.tsx
@@ -40493,10 +40526,13 @@ var CadViewerInner = (props) => {
40493
40526
  const sceneRef = useContext13(SchematicNavigationContext);
40494
40527
  const [selectedComponent, setSelectedComponent] = useState46();
40495
40528
  const [selectedObject, setSelectedObject] = useState46();
40529
+ const [selectedModel, setSelectedModel] = useState46();
40496
40530
  const handleMenuOpen = useCallback25(
40497
40531
  (position) => {
40498
- setSelectedObject(
40499
- pickSceneObject(sceneRef.current, position, circuitJson)
40532
+ const picked = pickSceneObject(sceneRef.current, position, circuitJson);
40533
+ setSelectedObject(picked);
40534
+ setSelectedModel(
40535
+ picked && sceneRef.current && findModelObject(sceneRef.current.rootObject, picked.objectId) ? picked : void 0
40500
40536
  );
40501
40537
  setSelectedComponent(
40502
40538
  props.onViewSchematicComponent ? pickSchematicComponent(sceneRef.current, position, circuitJson) : void 0
@@ -40775,8 +40811,9 @@ var CadViewerInner = (props) => {
40775
40811
  setCameraPreset("Custom");
40776
40812
  closeMenu();
40777
40813
  },
40814
+ downloadModelName: selectedModel?.name,
40778
40815
  onDownloadGltf: () => {
40779
- downloadGltf();
40816
+ downloadGltf(selectedModel, sceneRef.current?.rootObject);
40780
40817
  closeMenu();
40781
40818
  },
40782
40819
  onOpenKeyboardShortcuts: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.639",
3
+ "version": "0.0.641",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/tscircuit/3d-viewer"