@tscircuit/schematic-viewer 2.0.25 → 2.0.27
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 +432 -65
- package/dist/index.js.map +1 -1
- package/examples/example10-groups-view-schematic-groups.fixture.tsx +90 -0
- package/examples/example11-automatic-grouping-view-schematic-groups.fixture.tsx +99 -0
- package/lib/components/EditIcon.tsx +1 -1
- package/lib/components/GridIcon.tsx +1 -1
- package/lib/components/SchematicViewer.tsx +20 -0
- package/lib/components/SpiceSimulationIcon.tsx +1 -1
- package/lib/components/ViewMenu.tsx +147 -0
- package/lib/components/ViewMenuIcon.tsx +40 -0
- package/lib/hooks/useSchematicGroupsOverlay.ts +217 -0
- package/lib/hooks/useSpiceSimulation.ts +8 -18
- package/lib/utils/z-index-map.ts +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -192,6 +192,176 @@ var useChangeSchematicTracesForMovedComponents = ({
|
|
|
192
192
|
}, [svgDivRef, activeEditEvent, circuitJson, editEvents]);
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
+
// lib/hooks/useSchematicGroupsOverlay.ts
|
|
196
|
+
import { useEffect as useEffect3 } from "react";
|
|
197
|
+
import { su as su3 } from "@tscircuit/soup-util";
|
|
198
|
+
var useSchematicGroupsOverlay = (svgDivRef, circuitJson, circuitJsonKey, showGroups) => {
|
|
199
|
+
useEffect3(() => {
|
|
200
|
+
if (!svgDivRef.current || !showGroups || !circuitJson || circuitJson.length === 0) {
|
|
201
|
+
if (svgDivRef.current) {
|
|
202
|
+
const existingOverlays2 = svgDivRef.current.querySelectorAll(
|
|
203
|
+
".schematic-group-overlay"
|
|
204
|
+
);
|
|
205
|
+
existingOverlays2.forEach((overlay) => overlay.remove());
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const svg = svgDivRef.current.querySelector("svg");
|
|
210
|
+
if (!svg) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const existingOverlays = svg.querySelectorAll(".schematic-group-overlay");
|
|
214
|
+
existingOverlays.forEach((overlay) => overlay.remove());
|
|
215
|
+
try {
|
|
216
|
+
const sourceGroups = su3(circuitJson).source_group?.list() || [];
|
|
217
|
+
const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
|
|
218
|
+
let groupsToRender = [];
|
|
219
|
+
const hasMeaningfulGroups = sourceGroups.length > 0 && sourceGroups.some((group) => group.name && group.name !== "default" && group.name !== "");
|
|
220
|
+
if (hasMeaningfulGroups) {
|
|
221
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
222
|
+
for (const comp of schematicComponents) {
|
|
223
|
+
const sourceComp = su3(circuitJson).source_component.get(
|
|
224
|
+
comp.source_component_id
|
|
225
|
+
);
|
|
226
|
+
if (sourceComp?.source_group_id) {
|
|
227
|
+
if (!groupMap.has(sourceComp.source_group_id)) {
|
|
228
|
+
groupMap.set(sourceComp.source_group_id, []);
|
|
229
|
+
}
|
|
230
|
+
groupMap.get(sourceComp.source_group_id).push(comp);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
groupsToRender = Array.from(groupMap.entries()).map(
|
|
234
|
+
([groupId, components], index) => {
|
|
235
|
+
const group = sourceGroups.find(
|
|
236
|
+
(g) => g.source_group_id === groupId
|
|
237
|
+
);
|
|
238
|
+
return {
|
|
239
|
+
id: groupId,
|
|
240
|
+
name: group?.name || `Group ${index + 1}`,
|
|
241
|
+
components,
|
|
242
|
+
color: getGroupColor(index)
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
} else {
|
|
247
|
+
const componentTypeGroups = /* @__PURE__ */ new Map();
|
|
248
|
+
for (const comp of schematicComponents) {
|
|
249
|
+
const sourceComp = su3(circuitJson).source_component.get(
|
|
250
|
+
comp.source_component_id
|
|
251
|
+
);
|
|
252
|
+
if (sourceComp) {
|
|
253
|
+
const componentType = sourceComp.ftype || "other";
|
|
254
|
+
if (!componentTypeGroups.has(componentType)) {
|
|
255
|
+
componentTypeGroups.set(componentType, []);
|
|
256
|
+
}
|
|
257
|
+
componentTypeGroups.get(componentType).push(comp);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
groupsToRender = Array.from(componentTypeGroups.entries()).map(
|
|
261
|
+
([type, components], index) => ({
|
|
262
|
+
id: `type_${type}`,
|
|
263
|
+
name: `${type.charAt(0).toUpperCase() + type.slice(1)}s`,
|
|
264
|
+
components,
|
|
265
|
+
color: getGroupColor(index)
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
groupsToRender.forEach((group, groupIndex) => {
|
|
270
|
+
if (group.components.length === 0) return;
|
|
271
|
+
const groupBounds = calculateGroupBounds(group.components, svg);
|
|
272
|
+
if (!groupBounds) return;
|
|
273
|
+
const groupOverlay = document.createElementNS(
|
|
274
|
+
"http://www.w3.org/2000/svg",
|
|
275
|
+
"rect"
|
|
276
|
+
);
|
|
277
|
+
groupOverlay.setAttribute("class", "schematic-group-overlay");
|
|
278
|
+
groupOverlay.setAttribute("x", (groupBounds.minX - 25).toString());
|
|
279
|
+
groupOverlay.setAttribute("y", (groupBounds.minY - 25).toString());
|
|
280
|
+
groupOverlay.setAttribute(
|
|
281
|
+
"width",
|
|
282
|
+
(groupBounds.maxX - groupBounds.minX + 50).toString()
|
|
283
|
+
);
|
|
284
|
+
groupOverlay.setAttribute(
|
|
285
|
+
"height",
|
|
286
|
+
(groupBounds.maxY - groupBounds.minY + 50).toString()
|
|
287
|
+
);
|
|
288
|
+
groupOverlay.setAttribute("fill", "none");
|
|
289
|
+
groupOverlay.setAttribute("stroke", group.color);
|
|
290
|
+
groupOverlay.setAttribute("stroke-width", "3");
|
|
291
|
+
groupOverlay.setAttribute("stroke-dasharray", "8,4");
|
|
292
|
+
groupOverlay.setAttribute("opacity", "0.8");
|
|
293
|
+
groupOverlay.setAttribute("rx", "4");
|
|
294
|
+
groupOverlay.setAttribute("ry", "4");
|
|
295
|
+
const groupLabel = document.createElementNS(
|
|
296
|
+
"http://www.w3.org/2000/svg",
|
|
297
|
+
"text"
|
|
298
|
+
);
|
|
299
|
+
groupLabel.setAttribute("class", "schematic-group-overlay");
|
|
300
|
+
groupLabel.setAttribute("x", (groupBounds.minX - 10).toString());
|
|
301
|
+
groupLabel.setAttribute("y", (groupBounds.minY - 8).toString());
|
|
302
|
+
groupLabel.setAttribute("fill", group.color);
|
|
303
|
+
groupLabel.setAttribute("font-size", "14");
|
|
304
|
+
groupLabel.setAttribute("font-family", "Arial, sans-serif");
|
|
305
|
+
groupLabel.setAttribute("font-weight", "bold");
|
|
306
|
+
groupLabel.setAttribute("stroke", "#fff");
|
|
307
|
+
groupLabel.setAttribute("stroke-width", "0.5");
|
|
308
|
+
groupLabel.setAttribute("paint-order", "stroke fill");
|
|
309
|
+
groupLabel.textContent = group.name;
|
|
310
|
+
svg.appendChild(groupOverlay);
|
|
311
|
+
svg.appendChild(groupLabel);
|
|
312
|
+
});
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error("Error creating group overlays:", error);
|
|
315
|
+
}
|
|
316
|
+
}, [svgDivRef, circuitJsonKey, showGroups]);
|
|
317
|
+
};
|
|
318
|
+
function getGroupColor(index) {
|
|
319
|
+
const colors2 = [
|
|
320
|
+
"#FF6B6B",
|
|
321
|
+
// Red
|
|
322
|
+
"#4ECDC4",
|
|
323
|
+
// Teal
|
|
324
|
+
"#45B7D1",
|
|
325
|
+
// Blue
|
|
326
|
+
"#96CEB4",
|
|
327
|
+
// Green
|
|
328
|
+
"#FF8C42",
|
|
329
|
+
// Orange
|
|
330
|
+
"#DDA0DD",
|
|
331
|
+
// Plum
|
|
332
|
+
"#98D8C8",
|
|
333
|
+
// Mint
|
|
334
|
+
"#F7DC6F"
|
|
335
|
+
// Light Yellow
|
|
336
|
+
];
|
|
337
|
+
return colors2[index % colors2.length];
|
|
338
|
+
}
|
|
339
|
+
function calculateGroupBounds(components, svg) {
|
|
340
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
341
|
+
for (const component of components) {
|
|
342
|
+
let componentElement = svg.querySelector(
|
|
343
|
+
`g[data-schematic-component-id="${component.schematic_component_id}"]`
|
|
344
|
+
);
|
|
345
|
+
if (!componentElement) {
|
|
346
|
+
componentElement = svg.querySelector(
|
|
347
|
+
`[data-schematic-component-id="${component.schematic_component_id}"]`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
if (componentElement) {
|
|
351
|
+
const bbox = componentElement.getBBox();
|
|
352
|
+
minX = Math.min(minX, bbox.x);
|
|
353
|
+
minY = Math.min(minY, bbox.y);
|
|
354
|
+
maxX = Math.max(maxX, bbox.x + bbox.width);
|
|
355
|
+
maxY = Math.max(maxY, bbox.y + bbox.height);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (minX === Infinity) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
const bounds = { minX, minY, maxX, maxY };
|
|
362
|
+
return bounds;
|
|
363
|
+
}
|
|
364
|
+
|
|
195
365
|
// lib/utils/debug.ts
|
|
196
366
|
import Debug from "debug";
|
|
197
367
|
var debug = Debug("schematic-viewer");
|
|
@@ -201,7 +371,7 @@ var enableDebug = () => {
|
|
|
201
371
|
var debug_default = debug;
|
|
202
372
|
|
|
203
373
|
// lib/components/SchematicViewer.tsx
|
|
204
|
-
import { useEffect as
|
|
374
|
+
import { useEffect as useEffect7, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
|
|
205
375
|
import {
|
|
206
376
|
fromString,
|
|
207
377
|
identity,
|
|
@@ -210,11 +380,11 @@ import {
|
|
|
210
380
|
import { useMouseMatrixTransform } from "use-mouse-matrix-transform";
|
|
211
381
|
|
|
212
382
|
// lib/hooks/use-resize-handling.ts
|
|
213
|
-
import { useEffect as
|
|
383
|
+
import { useEffect as useEffect4, useState } from "react";
|
|
214
384
|
var useResizeHandling = (containerRef) => {
|
|
215
385
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
216
386
|
const [containerHeight, setContainerHeight] = useState(0);
|
|
217
|
-
|
|
387
|
+
useEffect4(() => {
|
|
218
388
|
if (!containerRef.current) return;
|
|
219
389
|
const updateDimensions = () => {
|
|
220
390
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
@@ -234,8 +404,8 @@ var useResizeHandling = (containerRef) => {
|
|
|
234
404
|
};
|
|
235
405
|
|
|
236
406
|
// lib/hooks/useComponentDragging.ts
|
|
237
|
-
import { su as
|
|
238
|
-
import { useCallback, useEffect as
|
|
407
|
+
import { su as su4 } from "@tscircuit/soup-util";
|
|
408
|
+
import { useCallback, useEffect as useEffect5, useRef as useRef3, useState as useState2 } from "react";
|
|
239
409
|
import { compose as compose2 } from "transformation-matrix";
|
|
240
410
|
var debug2 = debug_default.extend("useComponentDragging");
|
|
241
411
|
var useComponentDragging = ({
|
|
@@ -258,7 +428,7 @@ var useComponentDragging = ({
|
|
|
258
428
|
const componentPositionsRef = useRef3(
|
|
259
429
|
/* @__PURE__ */ new Map()
|
|
260
430
|
);
|
|
261
|
-
|
|
431
|
+
useEffect5(() => {
|
|
262
432
|
editEvents.forEach((event) => {
|
|
263
433
|
if ("edit_event_type" in event && event.edit_event_type === "edit_schematic_component_location" && !event.in_progress) {
|
|
264
434
|
componentPositionsRef.current.set(event.schematic_component_id, {
|
|
@@ -280,7 +450,7 @@ var useComponentDragging = ({
|
|
|
280
450
|
);
|
|
281
451
|
if (!schematic_component_id) return;
|
|
282
452
|
if (cancelDrag) cancelDrag();
|
|
283
|
-
const schematic_component =
|
|
453
|
+
const schematic_component = su4(circuitJson).schematic_component.get(
|
|
284
454
|
schematic_component_id
|
|
285
455
|
);
|
|
286
456
|
if (!schematic_component) return;
|
|
@@ -367,7 +537,7 @@ var useComponentDragging = ({
|
|
|
367
537
|
dragStartPosRef.current = null;
|
|
368
538
|
setActiveEditEvent(null);
|
|
369
539
|
}, [onEditEvent]);
|
|
370
|
-
|
|
540
|
+
useEffect5(() => {
|
|
371
541
|
window.addEventListener("mousemove", handleMouseMove);
|
|
372
542
|
window.addEventListener("mouseup", handleMouseUp);
|
|
373
543
|
return () => {
|
|
@@ -387,6 +557,9 @@ var zIndexMap = {
|
|
|
387
557
|
schematicEditIcon: 50,
|
|
388
558
|
schematicGridIcon: 49,
|
|
389
559
|
spiceSimulationIcon: 51,
|
|
560
|
+
viewMenuIcon: 48,
|
|
561
|
+
viewMenu: 55,
|
|
562
|
+
viewMenuBackdrop: 54,
|
|
390
563
|
clickToInteractOverlay: 100
|
|
391
564
|
};
|
|
392
565
|
|
|
@@ -403,7 +576,7 @@ var EditIcon = ({
|
|
|
403
576
|
style: {
|
|
404
577
|
position: "absolute",
|
|
405
578
|
top: "16px",
|
|
406
|
-
right: "
|
|
579
|
+
right: "64px",
|
|
407
580
|
backgroundColor: active ? "#4CAF50" : "#fff",
|
|
408
581
|
color: active ? "#fff" : "#000",
|
|
409
582
|
padding: "8px",
|
|
@@ -447,7 +620,7 @@ var GridIcon = ({
|
|
|
447
620
|
style: {
|
|
448
621
|
position: "absolute",
|
|
449
622
|
top: "56px",
|
|
450
|
-
right: "
|
|
623
|
+
right: "64px",
|
|
451
624
|
backgroundColor: active ? "#4CAF50" : "#fff",
|
|
452
625
|
color: active ? "#fff" : "#000",
|
|
453
626
|
padding: "8px",
|
|
@@ -475,9 +648,190 @@ var GridIcon = ({
|
|
|
475
648
|
);
|
|
476
649
|
};
|
|
477
650
|
|
|
651
|
+
// lib/components/ViewMenuIcon.tsx
|
|
652
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
653
|
+
var ViewMenuIcon = ({
|
|
654
|
+
onClick,
|
|
655
|
+
active
|
|
656
|
+
}) => {
|
|
657
|
+
return /* @__PURE__ */ jsx3(
|
|
658
|
+
"div",
|
|
659
|
+
{
|
|
660
|
+
onClick,
|
|
661
|
+
style: {
|
|
662
|
+
position: "absolute",
|
|
663
|
+
top: "16px",
|
|
664
|
+
right: "16px",
|
|
665
|
+
backgroundColor: active ? "#4CAF50" : "#fff",
|
|
666
|
+
color: active ? "#fff" : "#000",
|
|
667
|
+
padding: "8px",
|
|
668
|
+
borderRadius: "4px",
|
|
669
|
+
cursor: "pointer",
|
|
670
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
671
|
+
display: "flex",
|
|
672
|
+
alignItems: "center",
|
|
673
|
+
gap: "4px",
|
|
674
|
+
zIndex: zIndexMap.viewMenuIcon
|
|
675
|
+
},
|
|
676
|
+
children: /* @__PURE__ */ jsxs2(
|
|
677
|
+
"svg",
|
|
678
|
+
{
|
|
679
|
+
width: "16",
|
|
680
|
+
height: "16",
|
|
681
|
+
viewBox: "0 0 24 24",
|
|
682
|
+
fill: "none",
|
|
683
|
+
stroke: "currentColor",
|
|
684
|
+
strokeWidth: "2",
|
|
685
|
+
children: [
|
|
686
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "1" }),
|
|
687
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "5", r: "1" }),
|
|
688
|
+
/* @__PURE__ */ jsx3("circle", { cx: "12", cy: "19", r: "1" })
|
|
689
|
+
]
|
|
690
|
+
}
|
|
691
|
+
)
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
// lib/components/ViewMenu.tsx
|
|
697
|
+
import { useMemo } from "react";
|
|
698
|
+
import { su as su5 } from "@tscircuit/soup-util";
|
|
699
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
700
|
+
var ViewMenu = ({
|
|
701
|
+
circuitJson,
|
|
702
|
+
circuitJsonKey,
|
|
703
|
+
isVisible,
|
|
704
|
+
onClose,
|
|
705
|
+
showGroups,
|
|
706
|
+
onToggleGroups
|
|
707
|
+
}) => {
|
|
708
|
+
const hasGroups = useMemo(() => {
|
|
709
|
+
if (!circuitJson || circuitJson.length === 0) return false;
|
|
710
|
+
try {
|
|
711
|
+
const sourceGroups = su5(circuitJson).source_group?.list() || [];
|
|
712
|
+
if (sourceGroups.length > 0) return true;
|
|
713
|
+
const schematicComponents = su5(circuitJson).schematic_component?.list() || [];
|
|
714
|
+
if (schematicComponents.length > 1) {
|
|
715
|
+
const componentTypes = /* @__PURE__ */ new Set();
|
|
716
|
+
for (const comp of schematicComponents) {
|
|
717
|
+
const sourceComp = su5(circuitJson).source_component.get(
|
|
718
|
+
comp.source_component_id
|
|
719
|
+
);
|
|
720
|
+
if (sourceComp?.ftype) {
|
|
721
|
+
componentTypes.add(sourceComp.ftype);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return componentTypes.size > 1;
|
|
725
|
+
}
|
|
726
|
+
return false;
|
|
727
|
+
} catch (error) {
|
|
728
|
+
console.error("Error checking for groups:", error);
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
}, [circuitJsonKey]);
|
|
732
|
+
if (!isVisible) return null;
|
|
733
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
734
|
+
/* @__PURE__ */ jsx4(
|
|
735
|
+
"div",
|
|
736
|
+
{
|
|
737
|
+
onClick: onClose,
|
|
738
|
+
style: {
|
|
739
|
+
position: "absolute",
|
|
740
|
+
inset: 0,
|
|
741
|
+
backgroundColor: "transparent",
|
|
742
|
+
zIndex: zIndexMap.viewMenuBackdrop
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
),
|
|
746
|
+
/* @__PURE__ */ jsxs3(
|
|
747
|
+
"div",
|
|
748
|
+
{
|
|
749
|
+
style: {
|
|
750
|
+
position: "absolute",
|
|
751
|
+
top: "56px",
|
|
752
|
+
right: "16px",
|
|
753
|
+
backgroundColor: "#ffffff",
|
|
754
|
+
color: "#000000",
|
|
755
|
+
border: "1px solid #ccc",
|
|
756
|
+
borderRadius: "4px",
|
|
757
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
|
|
758
|
+
minWidth: "200px",
|
|
759
|
+
zIndex: zIndexMap.viewMenu
|
|
760
|
+
},
|
|
761
|
+
children: [
|
|
762
|
+
/* @__PURE__ */ jsxs3(
|
|
763
|
+
"div",
|
|
764
|
+
{
|
|
765
|
+
onClick: () => {
|
|
766
|
+
if (hasGroups) {
|
|
767
|
+
onToggleGroups(!showGroups);
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
style: {
|
|
771
|
+
padding: "8px 12px",
|
|
772
|
+
cursor: hasGroups ? "pointer" : "not-allowed",
|
|
773
|
+
opacity: hasGroups ? 1 : 0.5,
|
|
774
|
+
fontSize: "13px",
|
|
775
|
+
color: "#000000",
|
|
776
|
+
fontFamily: "sans-serif",
|
|
777
|
+
display: "flex",
|
|
778
|
+
alignItems: "center",
|
|
779
|
+
gap: "8px"
|
|
780
|
+
},
|
|
781
|
+
onMouseEnter: (e) => {
|
|
782
|
+
if (hasGroups) {
|
|
783
|
+
e.currentTarget.style.backgroundColor = "#f0f0f0";
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
onMouseLeave: (e) => {
|
|
787
|
+
if (hasGroups) {
|
|
788
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
children: [
|
|
792
|
+
/* @__PURE__ */ jsx4(
|
|
793
|
+
"div",
|
|
794
|
+
{
|
|
795
|
+
style: {
|
|
796
|
+
width: "16px",
|
|
797
|
+
height: "16px",
|
|
798
|
+
border: "2px solid #000",
|
|
799
|
+
borderRadius: "2px",
|
|
800
|
+
backgroundColor: "transparent",
|
|
801
|
+
display: "flex",
|
|
802
|
+
alignItems: "center",
|
|
803
|
+
justifyContent: "center",
|
|
804
|
+
fontSize: "10px",
|
|
805
|
+
fontWeight: "bold"
|
|
806
|
+
},
|
|
807
|
+
children: showGroups && "\u2713"
|
|
808
|
+
}
|
|
809
|
+
),
|
|
810
|
+
"View Schematic Groups"
|
|
811
|
+
]
|
|
812
|
+
}
|
|
813
|
+
),
|
|
814
|
+
!hasGroups && /* @__PURE__ */ jsx4(
|
|
815
|
+
"div",
|
|
816
|
+
{
|
|
817
|
+
style: {
|
|
818
|
+
padding: "8px 12px",
|
|
819
|
+
fontSize: "11px",
|
|
820
|
+
color: "#666",
|
|
821
|
+
fontStyle: "italic"
|
|
822
|
+
},
|
|
823
|
+
children: "No groups found in this schematic"
|
|
824
|
+
}
|
|
825
|
+
)
|
|
826
|
+
]
|
|
827
|
+
}
|
|
828
|
+
)
|
|
829
|
+
] });
|
|
830
|
+
};
|
|
831
|
+
|
|
478
832
|
// lib/components/SpiceIcon.tsx
|
|
479
|
-
import { jsx as
|
|
480
|
-
var SpiceIcon = () => /* @__PURE__ */
|
|
833
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
834
|
+
var SpiceIcon = () => /* @__PURE__ */ jsx5(
|
|
481
835
|
"svg",
|
|
482
836
|
{
|
|
483
837
|
width: "16",
|
|
@@ -488,23 +842,23 @@ var SpiceIcon = () => /* @__PURE__ */ jsx3(
|
|
|
488
842
|
strokeWidth: "2",
|
|
489
843
|
strokeLinecap: "round",
|
|
490
844
|
strokeLinejoin: "round",
|
|
491
|
-
children: /* @__PURE__ */
|
|
845
|
+
children: /* @__PURE__ */ jsx5("path", { d: "M3 12h2.5l2.5-9 4 18 4-9h5.5" })
|
|
492
846
|
}
|
|
493
847
|
);
|
|
494
848
|
|
|
495
849
|
// lib/components/SpiceSimulationIcon.tsx
|
|
496
|
-
import { jsx as
|
|
850
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
497
851
|
var SpiceSimulationIcon = ({
|
|
498
852
|
onClick
|
|
499
853
|
}) => {
|
|
500
|
-
return /* @__PURE__ */
|
|
854
|
+
return /* @__PURE__ */ jsx6(
|
|
501
855
|
"div",
|
|
502
856
|
{
|
|
503
857
|
onClick,
|
|
504
858
|
style: {
|
|
505
859
|
position: "absolute",
|
|
506
860
|
top: "16px",
|
|
507
|
-
right: "
|
|
861
|
+
right: "112px",
|
|
508
862
|
backgroundColor: "#fff",
|
|
509
863
|
color: "#000",
|
|
510
864
|
padding: "8px",
|
|
@@ -516,7 +870,7 @@ var SpiceSimulationIcon = ({
|
|
|
516
870
|
gap: "4px",
|
|
517
871
|
zIndex: zIndexMap.spiceSimulationIcon
|
|
518
872
|
},
|
|
519
|
-
children: /* @__PURE__ */
|
|
873
|
+
children: /* @__PURE__ */ jsx6(SpiceIcon, {})
|
|
520
874
|
}
|
|
521
875
|
);
|
|
522
876
|
};
|
|
@@ -533,7 +887,7 @@ import {
|
|
|
533
887
|
Legend
|
|
534
888
|
} from "chart.js";
|
|
535
889
|
import { Line } from "react-chartjs-2";
|
|
536
|
-
import { jsx as
|
|
890
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
537
891
|
ChartJS.register(
|
|
538
892
|
CategoryScale,
|
|
539
893
|
LinearScale,
|
|
@@ -574,7 +928,7 @@ var SpicePlot = ({
|
|
|
574
928
|
error
|
|
575
929
|
}) => {
|
|
576
930
|
if (isLoading) {
|
|
577
|
-
return /* @__PURE__ */
|
|
931
|
+
return /* @__PURE__ */ jsx7(
|
|
578
932
|
"div",
|
|
579
933
|
{
|
|
580
934
|
style: {
|
|
@@ -589,7 +943,7 @@ var SpicePlot = ({
|
|
|
589
943
|
);
|
|
590
944
|
}
|
|
591
945
|
if (error) {
|
|
592
|
-
return /* @__PURE__ */
|
|
946
|
+
return /* @__PURE__ */ jsxs4(
|
|
593
947
|
"div",
|
|
594
948
|
{
|
|
595
949
|
style: {
|
|
@@ -608,7 +962,7 @@ var SpicePlot = ({
|
|
|
608
962
|
);
|
|
609
963
|
}
|
|
610
964
|
if (plotData.length === 0) {
|
|
611
|
-
return /* @__PURE__ */
|
|
965
|
+
return /* @__PURE__ */ jsx7(
|
|
612
966
|
"div",
|
|
613
967
|
{
|
|
614
968
|
style: {
|
|
@@ -695,11 +1049,11 @@ var SpicePlot = ({
|
|
|
695
1049
|
}
|
|
696
1050
|
}
|
|
697
1051
|
};
|
|
698
|
-
return /* @__PURE__ */
|
|
1052
|
+
return /* @__PURE__ */ jsx7("div", { style: { position: "relative", height: "300px", width: "100%" }, children: /* @__PURE__ */ jsx7(Line, { options, data: chartData }) });
|
|
699
1053
|
};
|
|
700
1054
|
|
|
701
1055
|
// lib/components/SpiceSimulationOverlay.tsx
|
|
702
|
-
import { jsx as
|
|
1056
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
703
1057
|
var SpiceSimulationOverlay = ({
|
|
704
1058
|
spiceString,
|
|
705
1059
|
onClose,
|
|
@@ -708,7 +1062,7 @@ var SpiceSimulationOverlay = ({
|
|
|
708
1062
|
isLoading,
|
|
709
1063
|
error
|
|
710
1064
|
}) => {
|
|
711
|
-
return /* @__PURE__ */
|
|
1065
|
+
return /* @__PURE__ */ jsx8(
|
|
712
1066
|
"div",
|
|
713
1067
|
{
|
|
714
1068
|
style: {
|
|
@@ -724,7 +1078,7 @@ var SpiceSimulationOverlay = ({
|
|
|
724
1078
|
zIndex: 1002,
|
|
725
1079
|
fontFamily: "sans-serif"
|
|
726
1080
|
},
|
|
727
|
-
children: /* @__PURE__ */
|
|
1081
|
+
children: /* @__PURE__ */ jsxs5(
|
|
728
1082
|
"div",
|
|
729
1083
|
{
|
|
730
1084
|
style: {
|
|
@@ -736,7 +1090,7 @@ var SpiceSimulationOverlay = ({
|
|
|
736
1090
|
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.15)"
|
|
737
1091
|
},
|
|
738
1092
|
children: [
|
|
739
|
-
/* @__PURE__ */
|
|
1093
|
+
/* @__PURE__ */ jsxs5(
|
|
740
1094
|
"div",
|
|
741
1095
|
{
|
|
742
1096
|
style: {
|
|
@@ -748,7 +1102,7 @@ var SpiceSimulationOverlay = ({
|
|
|
748
1102
|
paddingBottom: "16px"
|
|
749
1103
|
},
|
|
750
1104
|
children: [
|
|
751
|
-
/* @__PURE__ */
|
|
1105
|
+
/* @__PURE__ */ jsx8(
|
|
752
1106
|
"h2",
|
|
753
1107
|
{
|
|
754
1108
|
style: {
|
|
@@ -760,7 +1114,7 @@ var SpiceSimulationOverlay = ({
|
|
|
760
1114
|
children: "SPICE Simulation"
|
|
761
1115
|
}
|
|
762
1116
|
),
|
|
763
|
-
/* @__PURE__ */
|
|
1117
|
+
/* @__PURE__ */ jsx8(
|
|
764
1118
|
"button",
|
|
765
1119
|
{
|
|
766
1120
|
onClick: onClose,
|
|
@@ -779,7 +1133,7 @@ var SpiceSimulationOverlay = ({
|
|
|
779
1133
|
]
|
|
780
1134
|
}
|
|
781
1135
|
),
|
|
782
|
-
/* @__PURE__ */
|
|
1136
|
+
/* @__PURE__ */ jsx8("div", { children: /* @__PURE__ */ jsx8(
|
|
783
1137
|
SpicePlot,
|
|
784
1138
|
{
|
|
785
1139
|
plotData,
|
|
@@ -788,8 +1142,8 @@ var SpiceSimulationOverlay = ({
|
|
|
788
1142
|
error
|
|
789
1143
|
}
|
|
790
1144
|
) }),
|
|
791
|
-
/* @__PURE__ */
|
|
792
|
-
/* @__PURE__ */
|
|
1145
|
+
/* @__PURE__ */ jsxs5("div", { style: { marginTop: "24px" }, children: [
|
|
1146
|
+
/* @__PURE__ */ jsx8(
|
|
793
1147
|
"h3",
|
|
794
1148
|
{
|
|
795
1149
|
style: {
|
|
@@ -802,7 +1156,7 @@ var SpiceSimulationOverlay = ({
|
|
|
802
1156
|
children: "SPICE Netlist"
|
|
803
1157
|
}
|
|
804
1158
|
),
|
|
805
|
-
/* @__PURE__ */
|
|
1159
|
+
/* @__PURE__ */ jsx8(
|
|
806
1160
|
"pre",
|
|
807
1161
|
{
|
|
808
1162
|
style: {
|
|
@@ -828,7 +1182,7 @@ var SpiceSimulationOverlay = ({
|
|
|
828
1182
|
};
|
|
829
1183
|
|
|
830
1184
|
// lib/hooks/useSpiceSimulation.ts
|
|
831
|
-
import { useState as useState3, useEffect as
|
|
1185
|
+
import { useState as useState3, useEffect as useEffect6 } from "react";
|
|
832
1186
|
|
|
833
1187
|
// lib/workers/spice-simulation.worker.blob.js
|
|
834
1188
|
var b64 = "dmFyIGU9bnVsbCxzPWFzeW5jKCk9Pihhd2FpdCBpbXBvcnQoImh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vZWVjaXJjdWl0LWVuZ2luZUAxLjUuMi8rZXNtIikpLlNpbXVsYXRpb24sYz1hc3luYygpPT57aWYoZSYmZS5pc0luaXRpYWxpemVkKCkpcmV0dXJuO2xldCBpPWF3YWl0IHMoKTtlPW5ldyBpLGF3YWl0IGUuc3RhcnQoKX07c2VsZi5vbm1lc3NhZ2U9YXN5bmMgaT0+e3RyeXtpZihhd2FpdCBjKCksIWUpdGhyb3cgbmV3IEVycm9yKCJTaW11bGF0aW9uIG5vdCBpbml0aWFsaXplZCIpO2xldCB0PWkuZGF0YS5zcGljZVN0cmluZyxhPXQubWF0Y2goL3dyZGF0YVxzKyhcUyspXHMrKC4qKS9pKTtpZihhKXtsZXQgbz1gLnByb2JlICR7YVsyXS50cmltKCkuc3BsaXQoL1xzKy8pLmpvaW4oIiAiKX1gO3Q9dC5yZXBsYWNlKC93cmRhdGEuKi9pLG8pfWVsc2UgaWYoIXQubWF0Y2goL1wucHJvYmUvaSkpdGhyb3cgdC5tYXRjaCgvcGxvdFxzKyguKikvaSk/bmV3IEVycm9yKCJUaGUgJ3Bsb3QnIGNvbW1hbmQgaXMgbm90IHN1cHBvcnRlZCBmb3IgZGF0YSBleHRyYWN0aW9uLiBQbGVhc2UgdXNlICd3cmRhdGEgPGZpbGVuYW1lPiA8dmFyMT4gLi4uJyBvciAnLnByb2JlIDx2YXIxPiAuLi4nIGluc3RlYWQuIik6bmV3IEVycm9yKCJObyAnLnByb2JlJyBvciAnd3JkYXRhJyBjb21tYW5kIGZvdW5kIGluIFNQSUNFIGZpbGUuIFVzZSAnd3JkYXRhIDxmaWxlbmFtZT4gPHZhcjE+IC4uLicgdG8gc3BlY2lmeSBvdXRwdXQuIik7ZS5zZXROZXRMaXN0KHQpO2xldCBuPWF3YWl0IGUucnVuU2ltKCk7c2VsZi5wb3N0TWVzc2FnZSh7dHlwZToicmVzdWx0IixyZXN1bHQ6bn0pfWNhdGNoKHQpe3NlbGYucG9zdE1lc3NhZ2Uoe3R5cGU6ImVycm9yIixlcnJvcjp0Lm1lc3NhZ2V9KX19Owo=";
|
|
@@ -885,7 +1239,7 @@ var useSpiceSimulation = (spiceString) => {
|
|
|
885
1239
|
const [nodes, setNodes] = useState3([]);
|
|
886
1240
|
const [isLoading, setIsLoading] = useState3(true);
|
|
887
1241
|
const [error, setError] = useState3(null);
|
|
888
|
-
|
|
1242
|
+
useEffect6(() => {
|
|
889
1243
|
if (!spiceString) {
|
|
890
1244
|
setIsLoading(false);
|
|
891
1245
|
setPlotData([]);
|
|
@@ -897,21 +1251,13 @@ var useSpiceSimulation = (spiceString) => {
|
|
|
897
1251
|
setError(null);
|
|
898
1252
|
setPlotData([]);
|
|
899
1253
|
setNodes([]);
|
|
900
|
-
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
);
|
|
906
|
-
} else {
|
|
907
|
-
const workerUrl = getSpiceSimulationWorkerBlobUrl();
|
|
908
|
-
if (!workerUrl) {
|
|
909
|
-
setError("Could not create SPICE simulation worker.");
|
|
910
|
-
setIsLoading(false);
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
worker = new Worker(workerUrl, { type: "module" });
|
|
1254
|
+
const workerUrl = getSpiceSimulationWorkerBlobUrl();
|
|
1255
|
+
if (!workerUrl) {
|
|
1256
|
+
setError("Could not create SPICE simulation worker.");
|
|
1257
|
+
setIsLoading(false);
|
|
1258
|
+
return;
|
|
914
1259
|
}
|
|
1260
|
+
const worker = new Worker(workerUrl, { type: "module" });
|
|
915
1261
|
worker.onmessage = (event) => {
|
|
916
1262
|
if (event.data.type === "result") {
|
|
917
1263
|
try {
|
|
@@ -999,7 +1345,7 @@ var getSpiceFromCircuitJson = (circuitJson) => {
|
|
|
999
1345
|
};
|
|
1000
1346
|
|
|
1001
1347
|
// lib/components/SchematicViewer.tsx
|
|
1002
|
-
import { jsx as
|
|
1348
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1003
1349
|
var SchematicViewer = ({
|
|
1004
1350
|
circuitJson,
|
|
1005
1351
|
containerStyle,
|
|
@@ -1020,11 +1366,11 @@ var SchematicViewer = ({
|
|
|
1020
1366
|
const getCircuitHash = (circuitJson2) => {
|
|
1021
1367
|
return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
|
|
1022
1368
|
};
|
|
1023
|
-
const circuitJsonKey =
|
|
1369
|
+
const circuitJsonKey = useMemo2(
|
|
1024
1370
|
() => getCircuitHash(circuitJson),
|
|
1025
1371
|
[circuitJson]
|
|
1026
1372
|
);
|
|
1027
|
-
const spiceString =
|
|
1373
|
+
const spiceString = useMemo2(() => {
|
|
1028
1374
|
if (!spiceSimulationEnabled) return null;
|
|
1029
1375
|
try {
|
|
1030
1376
|
return getSpiceFromCircuitJson(circuitJson);
|
|
@@ -1044,6 +1390,8 @@ var SchematicViewer = ({
|
|
|
1044
1390
|
const [isInteractionEnabled, setIsInteractionEnabled] = useState4(
|
|
1045
1391
|
!clickToInteractEnabled
|
|
1046
1392
|
);
|
|
1393
|
+
const [showViewMenu, setShowViewMenu] = useState4(false);
|
|
1394
|
+
const [showSchematicGroups, setShowSchematicGroups] = useState4(false);
|
|
1047
1395
|
const svgDivRef = useRef4(null);
|
|
1048
1396
|
const touchStartRef = useRef4(null);
|
|
1049
1397
|
const handleTouchStart = (e) => {
|
|
@@ -1067,7 +1415,7 @@ var SchematicViewer = ({
|
|
|
1067
1415
|
};
|
|
1068
1416
|
const [internalEditEvents, setInternalEditEvents] = useState4([]);
|
|
1069
1417
|
const circuitJsonRef = useRef4(circuitJson);
|
|
1070
|
-
|
|
1418
|
+
useEffect7(() => {
|
|
1071
1419
|
const circuitHash = getCircuitHash(circuitJson);
|
|
1072
1420
|
const circuitHashRef = getCircuitHash(circuitJsonRef.current);
|
|
1073
1421
|
if (circuitHash !== circuitHashRef) {
|
|
@@ -1088,7 +1436,7 @@ var SchematicViewer = ({
|
|
|
1088
1436
|
enabled: isInteractionEnabled && !showSpiceOverlay
|
|
1089
1437
|
});
|
|
1090
1438
|
const { containerWidth, containerHeight } = useResizeHandling(containerRef);
|
|
1091
|
-
const svgString =
|
|
1439
|
+
const svgString = useMemo2(() => {
|
|
1092
1440
|
if (!containerWidth || !containerHeight) return "";
|
|
1093
1441
|
return convertCircuitJsonToSchematicSvg(circuitJson, {
|
|
1094
1442
|
width: containerWidth,
|
|
@@ -1100,13 +1448,13 @@ var SchematicViewer = ({
|
|
|
1100
1448
|
colorOverrides
|
|
1101
1449
|
});
|
|
1102
1450
|
}, [circuitJson, containerWidth, containerHeight]);
|
|
1103
|
-
const containerBackgroundColor =
|
|
1451
|
+
const containerBackgroundColor = useMemo2(() => {
|
|
1104
1452
|
const match = svgString.match(
|
|
1105
1453
|
/<svg[^>]*style="[^"]*background-color:\s*([^;\"]+)/i
|
|
1106
1454
|
);
|
|
1107
1455
|
return match?.[1] ?? "transparent";
|
|
1108
1456
|
}, [svgString]);
|
|
1109
|
-
const realToSvgProjection =
|
|
1457
|
+
const realToSvgProjection = useMemo2(() => {
|
|
1110
1458
|
if (!svgString) return identity();
|
|
1111
1459
|
const transformString = svgString.match(
|
|
1112
1460
|
/data-real-to-screen-transform="([^"]+)"/
|
|
@@ -1124,7 +1472,7 @@ var SchematicViewer = ({
|
|
|
1124
1472
|
onEditEvent(event);
|
|
1125
1473
|
}
|
|
1126
1474
|
};
|
|
1127
|
-
const editEventsWithUnappliedEditEvents =
|
|
1475
|
+
const editEventsWithUnappliedEditEvents = useMemo2(() => {
|
|
1128
1476
|
return [...unappliedEditEvents, ...internalEditEvents];
|
|
1129
1477
|
}, [unappliedEditEvents, internalEditEvents]);
|
|
1130
1478
|
const { handleMouseDown, isDragging, activeEditEvent } = useComponentDragging(
|
|
@@ -1152,8 +1500,9 @@ var SchematicViewer = ({
|
|
|
1152
1500
|
activeEditEvent,
|
|
1153
1501
|
editEvents: editEventsWithUnappliedEditEvents
|
|
1154
1502
|
});
|
|
1155
|
-
|
|
1156
|
-
|
|
1503
|
+
useSchematicGroupsOverlay(svgDivRef, circuitJson, circuitJsonKey, showSchematicGroups);
|
|
1504
|
+
const svgDiv = useMemo2(
|
|
1505
|
+
() => /* @__PURE__ */ jsx9(
|
|
1157
1506
|
"div",
|
|
1158
1507
|
{
|
|
1159
1508
|
ref: svgDivRef,
|
|
@@ -1166,7 +1515,7 @@ var SchematicViewer = ({
|
|
|
1166
1515
|
),
|
|
1167
1516
|
[svgString, isInteractionEnabled, clickToInteractEnabled]
|
|
1168
1517
|
);
|
|
1169
|
-
return /* @__PURE__ */
|
|
1518
|
+
return /* @__PURE__ */ jsxs6(
|
|
1170
1519
|
"div",
|
|
1171
1520
|
{
|
|
1172
1521
|
ref: containerRef,
|
|
@@ -1207,7 +1556,7 @@ var SchematicViewer = ({
|
|
|
1207
1556
|
handleTouchEnd(e);
|
|
1208
1557
|
},
|
|
1209
1558
|
children: [
|
|
1210
|
-
!isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */
|
|
1559
|
+
!isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx9(
|
|
1211
1560
|
"div",
|
|
1212
1561
|
{
|
|
1213
1562
|
onClick: (e) => {
|
|
@@ -1226,7 +1575,7 @@ var SchematicViewer = ({
|
|
|
1226
1575
|
pointerEvents: "all",
|
|
1227
1576
|
touchAction: "pan-x pan-y pinch-zoom"
|
|
1228
1577
|
},
|
|
1229
|
-
children: /* @__PURE__ */
|
|
1578
|
+
children: /* @__PURE__ */ jsx9(
|
|
1230
1579
|
"div",
|
|
1231
1580
|
{
|
|
1232
1581
|
style: {
|
|
@@ -1243,22 +1592,40 @@ var SchematicViewer = ({
|
|
|
1243
1592
|
)
|
|
1244
1593
|
}
|
|
1245
1594
|
),
|
|
1246
|
-
|
|
1595
|
+
/* @__PURE__ */ jsx9(
|
|
1596
|
+
ViewMenuIcon,
|
|
1597
|
+
{
|
|
1598
|
+
active: showViewMenu,
|
|
1599
|
+
onClick: () => setShowViewMenu(!showViewMenu)
|
|
1600
|
+
}
|
|
1601
|
+
),
|
|
1602
|
+
editingEnabled && /* @__PURE__ */ jsx9(
|
|
1247
1603
|
EditIcon,
|
|
1248
1604
|
{
|
|
1249
1605
|
active: editModeEnabled,
|
|
1250
1606
|
onClick: () => setEditModeEnabled(!editModeEnabled)
|
|
1251
1607
|
}
|
|
1252
1608
|
),
|
|
1253
|
-
editingEnabled && editModeEnabled && /* @__PURE__ */
|
|
1609
|
+
editingEnabled && editModeEnabled && /* @__PURE__ */ jsx9(
|
|
1254
1610
|
GridIcon,
|
|
1255
1611
|
{
|
|
1256
1612
|
active: snapToGrid,
|
|
1257
1613
|
onClick: () => setSnapToGrid(!snapToGrid)
|
|
1258
1614
|
}
|
|
1259
1615
|
),
|
|
1260
|
-
|
|
1261
|
-
|
|
1616
|
+
/* @__PURE__ */ jsx9(
|
|
1617
|
+
ViewMenu,
|
|
1618
|
+
{
|
|
1619
|
+
circuitJson,
|
|
1620
|
+
circuitJsonKey,
|
|
1621
|
+
isVisible: showViewMenu,
|
|
1622
|
+
onClose: () => setShowViewMenu(false),
|
|
1623
|
+
showGroups: showSchematicGroups,
|
|
1624
|
+
onToggleGroups: setShowSchematicGroups
|
|
1625
|
+
}
|
|
1626
|
+
),
|
|
1627
|
+
spiceSimulationEnabled && /* @__PURE__ */ jsx9(SpiceSimulationIcon, { onClick: () => setShowSpiceOverlay(true) }),
|
|
1628
|
+
showSpiceOverlay && /* @__PURE__ */ jsx9(
|
|
1262
1629
|
SpiceSimulationOverlay,
|
|
1263
1630
|
{
|
|
1264
1631
|
spiceString,
|