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