@tscircuit/schematic-viewer 2.0.24 → 2.0.26

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 CHANGED
@@ -201,7 +201,7 @@ var enableDebug = () => {
201
201
  var debug_default = debug;
202
202
 
203
203
  // lib/components/SchematicViewer.tsx
204
- import { useEffect as useEffect5, useMemo, useRef as useRef4, useState as useState3 } from "react";
204
+ import { useEffect as useEffect6, useMemo, useRef as useRef4, useState as useState4 } from "react";
205
205
  import {
206
206
  fromString,
207
207
  identity,
@@ -386,6 +386,7 @@ var useComponentDragging = ({
386
386
  var zIndexMap = {
387
387
  schematicEditIcon: 50,
388
388
  schematicGridIcon: 49,
389
+ spiceSimulationIcon: 51,
389
390
  clickToInteractOverlay: 100
390
391
  };
391
392
 
@@ -474,8 +475,523 @@ var GridIcon = ({
474
475
  );
475
476
  };
476
477
 
478
+ // lib/components/SpiceIcon.tsx
479
+ import { jsx as jsx3 } from "react/jsx-runtime";
480
+ var SpiceIcon = () => /* @__PURE__ */ jsx3(
481
+ "svg",
482
+ {
483
+ width: "16",
484
+ height: "16",
485
+ viewBox: "0 0 24 24",
486
+ fill: "none",
487
+ stroke: "currentColor",
488
+ strokeWidth: "2",
489
+ strokeLinecap: "round",
490
+ strokeLinejoin: "round",
491
+ children: /* @__PURE__ */ jsx3("path", { d: "M3 12h2.5l2.5-9 4 18 4-9h5.5" })
492
+ }
493
+ );
494
+
495
+ // lib/components/SpiceSimulationIcon.tsx
496
+ import { jsx as jsx4 } from "react/jsx-runtime";
497
+ var SpiceSimulationIcon = ({
498
+ onClick
499
+ }) => {
500
+ return /* @__PURE__ */ jsx4(
501
+ "div",
502
+ {
503
+ onClick,
504
+ style: {
505
+ position: "absolute",
506
+ top: "16px",
507
+ right: "56px",
508
+ backgroundColor: "#fff",
509
+ color: "#000",
510
+ padding: "8px",
511
+ borderRadius: "4px",
512
+ cursor: "pointer",
513
+ boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
514
+ display: "flex",
515
+ alignItems: "center",
516
+ gap: "4px",
517
+ zIndex: zIndexMap.spiceSimulationIcon
518
+ },
519
+ children: /* @__PURE__ */ jsx4(SpiceIcon, {})
520
+ }
521
+ );
522
+ };
523
+
524
+ // lib/components/SpicePlot.tsx
525
+ import {
526
+ Chart as ChartJS,
527
+ CategoryScale,
528
+ LinearScale,
529
+ PointElement,
530
+ LineElement,
531
+ Title,
532
+ Tooltip,
533
+ Legend
534
+ } from "chart.js";
535
+ import { Line } from "react-chartjs-2";
536
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
537
+ ChartJS.register(
538
+ CategoryScale,
539
+ LinearScale,
540
+ PointElement,
541
+ LineElement,
542
+ Title,
543
+ Tooltip,
544
+ Legend
545
+ );
546
+ var colors = ["#8884d8", "#82ca9d", "#ffc658", "#ff7300", "#387908"];
547
+ var formatTimeWithUnits = (seconds) => {
548
+ if (seconds === 0) return "0s";
549
+ const absSeconds = Math.abs(seconds);
550
+ let unit = "s";
551
+ let scale = 1;
552
+ if (absSeconds < 1e-12) {
553
+ unit = "fs";
554
+ scale = 1e15;
555
+ } else if (absSeconds < 1e-9) {
556
+ unit = "ps";
557
+ scale = 1e12;
558
+ } else if (absSeconds < 1e-6) {
559
+ unit = "ns";
560
+ scale = 1e9;
561
+ } else if (absSeconds < 1e-3) {
562
+ unit = "us";
563
+ scale = 1e6;
564
+ } else if (absSeconds < 1) {
565
+ unit = "ms";
566
+ scale = 1e3;
567
+ }
568
+ return `${parseFloat((seconds * scale).toPrecision(3))}${unit}`;
569
+ };
570
+ var SpicePlot = ({
571
+ plotData,
572
+ nodes,
573
+ isLoading,
574
+ error
575
+ }) => {
576
+ if (isLoading) {
577
+ return /* @__PURE__ */ jsx5(
578
+ "div",
579
+ {
580
+ style: {
581
+ height: "300px",
582
+ width: "100%",
583
+ display: "flex",
584
+ alignItems: "center",
585
+ justifyContent: "center"
586
+ },
587
+ children: "Running simulation..."
588
+ }
589
+ );
590
+ }
591
+ if (error) {
592
+ return /* @__PURE__ */ jsxs2(
593
+ "div",
594
+ {
595
+ style: {
596
+ height: "300px",
597
+ width: "100%",
598
+ display: "flex",
599
+ alignItems: "center",
600
+ justifyContent: "center",
601
+ color: "red"
602
+ },
603
+ children: [
604
+ "Error: ",
605
+ error
606
+ ]
607
+ }
608
+ );
609
+ }
610
+ if (plotData.length === 0) {
611
+ return /* @__PURE__ */ jsx5(
612
+ "div",
613
+ {
614
+ style: {
615
+ height: "300px",
616
+ width: "100%",
617
+ display: "flex",
618
+ alignItems: "center",
619
+ justifyContent: "center"
620
+ },
621
+ children: "No data to plot. Check simulation output or SPICE netlist."
622
+ }
623
+ );
624
+ }
625
+ const chartData = {
626
+ datasets: nodes.map((node, i) => ({
627
+ label: node,
628
+ data: plotData.map((p) => ({
629
+ x: Number(p.name),
630
+ y: p[node]
631
+ })),
632
+ borderColor: colors[i % colors.length],
633
+ backgroundColor: colors[i % colors.length],
634
+ fill: false,
635
+ tension: 0.1
636
+ }))
637
+ };
638
+ const options = {
639
+ responsive: true,
640
+ maintainAspectRatio: false,
641
+ plugins: {
642
+ legend: {
643
+ position: "top",
644
+ labels: {
645
+ font: {
646
+ family: "sans-serif"
647
+ }
648
+ }
649
+ },
650
+ title: {
651
+ display: false
652
+ },
653
+ tooltip: {
654
+ callbacks: {
655
+ title: (tooltipItems) => {
656
+ if (tooltipItems.length > 0) {
657
+ const item = tooltipItems[0];
658
+ return formatTimeWithUnits(item.parsed.x);
659
+ }
660
+ return "";
661
+ }
662
+ }
663
+ }
664
+ },
665
+ scales: {
666
+ x: {
667
+ type: "linear",
668
+ title: {
669
+ display: true,
670
+ text: "Time",
671
+ font: {
672
+ family: "sans-serif"
673
+ }
674
+ },
675
+ ticks: {
676
+ callback: (value) => formatTimeWithUnits(value),
677
+ font: {
678
+ family: "sans-serif"
679
+ }
680
+ }
681
+ },
682
+ y: {
683
+ title: {
684
+ display: true,
685
+ text: "Voltage",
686
+ font: {
687
+ family: "sans-serif"
688
+ }
689
+ },
690
+ ticks: {
691
+ font: {
692
+ family: "sans-serif"
693
+ }
694
+ }
695
+ }
696
+ }
697
+ };
698
+ return /* @__PURE__ */ jsx5("div", { style: { position: "relative", height: "300px", width: "100%" }, children: /* @__PURE__ */ jsx5(Line, { options, data: chartData }) });
699
+ };
700
+
701
+ // lib/components/SpiceSimulationOverlay.tsx
702
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
703
+ var SpiceSimulationOverlay = ({
704
+ spiceString,
705
+ onClose,
706
+ plotData,
707
+ nodes,
708
+ isLoading,
709
+ error
710
+ }) => {
711
+ return /* @__PURE__ */ jsx6(
712
+ "div",
713
+ {
714
+ style: {
715
+ position: "fixed",
716
+ top: 0,
717
+ left: 0,
718
+ right: 0,
719
+ bottom: 0,
720
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
721
+ display: "flex",
722
+ alignItems: "center",
723
+ justifyContent: "center",
724
+ zIndex: 1002,
725
+ fontFamily: "sans-serif"
726
+ },
727
+ children: /* @__PURE__ */ jsxs3(
728
+ "div",
729
+ {
730
+ style: {
731
+ backgroundColor: "white",
732
+ padding: "24px",
733
+ borderRadius: "12px",
734
+ width: "90%",
735
+ maxWidth: "900px",
736
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.15)"
737
+ },
738
+ children: [
739
+ /* @__PURE__ */ jsxs3(
740
+ "div",
741
+ {
742
+ style: {
743
+ display: "flex",
744
+ justifyContent: "space-between",
745
+ alignItems: "center",
746
+ marginBottom: "24px",
747
+ borderBottom: "1px solid #eee",
748
+ paddingBottom: "16px"
749
+ },
750
+ children: [
751
+ /* @__PURE__ */ jsx6(
752
+ "h2",
753
+ {
754
+ style: {
755
+ margin: 0,
756
+ fontSize: "22px",
757
+ fontWeight: 600,
758
+ color: "#333"
759
+ },
760
+ children: "SPICE Simulation"
761
+ }
762
+ ),
763
+ /* @__PURE__ */ jsx6(
764
+ "button",
765
+ {
766
+ onClick: onClose,
767
+ style: {
768
+ background: "none",
769
+ border: "none",
770
+ fontSize: "28px",
771
+ cursor: "pointer",
772
+ color: "#888",
773
+ padding: 0,
774
+ lineHeight: 1
775
+ },
776
+ children: "\xD7"
777
+ }
778
+ )
779
+ ]
780
+ }
781
+ ),
782
+ /* @__PURE__ */ jsx6("div", { children: /* @__PURE__ */ jsx6(
783
+ SpicePlot,
784
+ {
785
+ plotData,
786
+ nodes,
787
+ isLoading,
788
+ error
789
+ }
790
+ ) }),
791
+ /* @__PURE__ */ jsxs3("div", { style: { marginTop: "24px" }, children: [
792
+ /* @__PURE__ */ jsx6(
793
+ "h3",
794
+ {
795
+ style: {
796
+ marginTop: 0,
797
+ marginBottom: "12px",
798
+ fontSize: "18px",
799
+ fontWeight: 600,
800
+ color: "#333"
801
+ },
802
+ children: "SPICE Netlist"
803
+ }
804
+ ),
805
+ /* @__PURE__ */ jsx6(
806
+ "pre",
807
+ {
808
+ style: {
809
+ backgroundColor: "#fafafa",
810
+ padding: "16px",
811
+ borderRadius: "6px",
812
+ maxHeight: "150px",
813
+ overflowY: "auto",
814
+ border: "1px solid #eee",
815
+ color: "#333",
816
+ fontSize: "13px",
817
+ fontFamily: "monospace"
818
+ },
819
+ children: spiceString
820
+ }
821
+ )
822
+ ] })
823
+ ]
824
+ }
825
+ )
826
+ }
827
+ );
828
+ };
829
+
830
+ // lib/hooks/useSpiceSimulation.ts
831
+ import { useState as useState3, useEffect as useEffect5 } from "react";
832
+
833
+ // lib/workers/spice-simulation.worker.blob.js
834
+ var b64 = "dmFyIGU9bnVsbCxzPWFzeW5jKCk9Pihhd2FpdCBpbXBvcnQoImh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vZWVjaXJjdWl0LWVuZ2luZUAxLjUuMi8rZXNtIikpLlNpbXVsYXRpb24sYz1hc3luYygpPT57aWYoZSYmZS5pc0luaXRpYWxpemVkKCkpcmV0dXJuO2xldCBpPWF3YWl0IHMoKTtlPW5ldyBpLGF3YWl0IGUuc3RhcnQoKX07c2VsZi5vbm1lc3NhZ2U9YXN5bmMgaT0+e3RyeXtpZihhd2FpdCBjKCksIWUpdGhyb3cgbmV3IEVycm9yKCJTaW11bGF0aW9uIG5vdCBpbml0aWFsaXplZCIpO2xldCB0PWkuZGF0YS5zcGljZVN0cmluZyxhPXQubWF0Y2goL3dyZGF0YVxzKyhcUyspXHMrKC4qKS9pKTtpZihhKXtsZXQgbz1gLnByb2JlICR7YVsyXS50cmltKCkuc3BsaXQoL1xzKy8pLmpvaW4oIiAiKX1gO3Q9dC5yZXBsYWNlKC93cmRhdGEuKi9pLG8pfWVsc2UgaWYoIXQubWF0Y2goL1wucHJvYmUvaSkpdGhyb3cgdC5tYXRjaCgvcGxvdFxzKyguKikvaSk/bmV3IEVycm9yKCJUaGUgJ3Bsb3QnIGNvbW1hbmQgaXMgbm90IHN1cHBvcnRlZCBmb3IgZGF0YSBleHRyYWN0aW9uLiBQbGVhc2UgdXNlICd3cmRhdGEgPGZpbGVuYW1lPiA8dmFyMT4gLi4uJyBvciAnLnByb2JlIDx2YXIxPiAuLi4nIGluc3RlYWQuIik6bmV3IEVycm9yKCJObyAnLnByb2JlJyBvciAnd3JkYXRhJyBjb21tYW5kIGZvdW5kIGluIFNQSUNFIGZpbGUuIFVzZSAnd3JkYXRhIDxmaWxlbmFtZT4gPHZhcjE+IC4uLicgdG8gc3BlY2lmeSBvdXRwdXQuIik7ZS5zZXROZXRMaXN0KHQpO2xldCBuPWF3YWl0IGUucnVuU2ltKCk7c2VsZi5wb3N0TWVzc2FnZSh7dHlwZToicmVzdWx0IixyZXN1bHQ6bn0pfWNhdGNoKHQpe3NlbGYucG9zdE1lc3NhZ2Uoe3R5cGU6ImVycm9yIixlcnJvcjp0Lm1lc3NhZ2V9KX19Owo=";
835
+ var blobUrl = null;
836
+ var getSpiceSimulationWorkerBlobUrl = () => {
837
+ if (typeof window === "undefined") return null;
838
+ if (blobUrl) return blobUrl;
839
+ try {
840
+ const blob = new Blob([atob(b64)], { type: "application/javascript" });
841
+ blobUrl = URL.createObjectURL(blob);
842
+ return blobUrl;
843
+ } catch (e) {
844
+ console.error("Failed to create blob URL for worker", e);
845
+ return null;
846
+ }
847
+ };
848
+
849
+ // lib/hooks/useSpiceSimulation.ts
850
+ var parseEecEngineOutput = (result) => {
851
+ const columnData = {};
852
+ if (result.dataType === "real") {
853
+ result.data.forEach((col) => {
854
+ columnData[col.name] = col.values;
855
+ });
856
+ } else if (result.dataType === "complex") {
857
+ result.data.forEach((col) => {
858
+ columnData[col.name] = col.values.map((v) => v.real);
859
+ });
860
+ } else {
861
+ throw new Error("Unsupported data type in simulation result");
862
+ }
863
+ const timeKey = Object.keys(columnData).find(
864
+ (k) => k.toLowerCase() === "time" || k.toLowerCase() === "frequency"
865
+ );
866
+ if (!timeKey) {
867
+ throw new Error("No time or frequency data in simulation result");
868
+ }
869
+ const timeValues = columnData[timeKey];
870
+ const probedVariables = Object.keys(columnData).filter((k) => k !== timeKey);
871
+ const plotableNodes = probedVariables.map(
872
+ (n) => n.replace(/v\(([^)]+)\)/i, "$1")
873
+ );
874
+ const plotData = timeValues.map((t, i) => {
875
+ const point = { name: t.toExponential(2) };
876
+ probedVariables.forEach((variable, j) => {
877
+ point[plotableNodes[j]] = columnData[variable][i];
878
+ });
879
+ return point;
880
+ });
881
+ return { plotData, nodes: plotableNodes };
882
+ };
883
+ var useSpiceSimulation = (spiceString) => {
884
+ const [plotData, setPlotData] = useState3([]);
885
+ const [nodes, setNodes] = useState3([]);
886
+ const [isLoading, setIsLoading] = useState3(true);
887
+ const [error, setError] = useState3(null);
888
+ useEffect5(() => {
889
+ if (!spiceString) {
890
+ setIsLoading(false);
891
+ setPlotData([]);
892
+ setNodes([]);
893
+ setError(null);
894
+ return;
895
+ }
896
+ setIsLoading(true);
897
+ setError(null);
898
+ setPlotData([]);
899
+ setNodes([]);
900
+ const workerUrl = getSpiceSimulationWorkerBlobUrl();
901
+ if (!workerUrl) {
902
+ setError("Could not create SPICE simulation worker.");
903
+ setIsLoading(false);
904
+ return;
905
+ }
906
+ const worker = new Worker(workerUrl, { type: "module" });
907
+ worker.onmessage = (event) => {
908
+ if (event.data.type === "result") {
909
+ try {
910
+ const { plotData: parsedData, nodes: parsedNodes } = parseEecEngineOutput(event.data.result);
911
+ setPlotData(parsedData);
912
+ setNodes(parsedNodes);
913
+ } catch (e) {
914
+ setError(e.message || "Failed to parse simulation result");
915
+ console.error(e);
916
+ }
917
+ } else if (event.data.type === "error") {
918
+ setError(event.data.error);
919
+ }
920
+ setIsLoading(false);
921
+ };
922
+ worker.onerror = (err) => {
923
+ setError(err.message);
924
+ setIsLoading(false);
925
+ };
926
+ worker.postMessage({ spiceString });
927
+ return () => {
928
+ worker.terminate();
929
+ };
930
+ }, [spiceString]);
931
+ return { plotData, nodes, isLoading, error };
932
+ };
933
+
934
+ // lib/utils/spice-utils.ts
935
+ import { circuitJsonToSpice } from "circuit-json-to-spice";
936
+ var getSpiceFromCircuitJson = (circuitJson) => {
937
+ const spiceNetlist = circuitJsonToSpice(circuitJson);
938
+ const baseSpiceString = spiceNetlist.toSpiceString();
939
+ const lines = baseSpiceString.split("\n").filter((l) => l.trim() !== "");
940
+ const componentLines = lines.filter(
941
+ (l) => !l.startsWith("*") && !l.startsWith(".") && l.trim() !== ""
942
+ );
943
+ const allNodes = /* @__PURE__ */ new Set();
944
+ const capacitorNodes = /* @__PURE__ */ new Set();
945
+ for (const line of componentLines) {
946
+ const parts = line.trim().split(/\s+/);
947
+ if (parts.length < 3) continue;
948
+ const componentType = parts[0][0].toUpperCase();
949
+ let nodesOnLine = [];
950
+ if (["R", "C", "L", "V", "I", "D"].includes(componentType)) {
951
+ nodesOnLine = parts.slice(1, 3);
952
+ } else if (componentType === "Q" && parts.length >= 4) {
953
+ nodesOnLine = parts.slice(1, 4);
954
+ } else if (componentType === "M" && parts.length >= 5) {
955
+ nodesOnLine = parts.slice(1, 5);
956
+ } else if (componentType === "X") {
957
+ nodesOnLine = parts.slice(1, -1);
958
+ } else {
959
+ continue;
960
+ }
961
+ nodesOnLine.forEach((node) => allNodes.add(node));
962
+ if (componentType === "C") {
963
+ nodesOnLine.forEach((node) => capacitorNodes.add(node));
964
+ }
965
+ }
966
+ allNodes.delete("0");
967
+ capacitorNodes.delete("0");
968
+ const icLines = Array.from(capacitorNodes).map((node) => `.ic V(${node})=0`);
969
+ const probeNodes = Array.from(allNodes).map((node) => `V(${node})`);
970
+ const probeLine = probeNodes.length > 0 ? `.probe ${probeNodes.join(" ")}` : "";
971
+ const tranLine = ".tran 0.1ms 50ms UIC";
972
+ const endStatement = ".end";
973
+ const originalLines = baseSpiceString.split("\n");
974
+ let endIndex = -1;
975
+ for (let i = originalLines.length - 1; i >= 0; i--) {
976
+ if (originalLines[i].trim().toLowerCase().startsWith(endStatement)) {
977
+ endIndex = i;
978
+ break;
979
+ }
980
+ }
981
+ const injectionLines = [...icLines, probeLine, tranLine].filter(Boolean);
982
+ let finalLines;
983
+ if (endIndex !== -1) {
984
+ const beforeEnd = originalLines.slice(0, endIndex);
985
+ const endLineAndAfter = originalLines.slice(endIndex);
986
+ finalLines = [...beforeEnd, ...injectionLines, ...endLineAndAfter];
987
+ } else {
988
+ finalLines = [...originalLines, ...injectionLines, endStatement];
989
+ }
990
+ return finalLines.join("\n");
991
+ };
992
+
477
993
  // lib/components/SchematicViewer.tsx
478
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
994
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
479
995
  var SchematicViewer = ({
480
996
  circuitJson,
481
997
  containerStyle,
@@ -486,14 +1002,38 @@ var SchematicViewer = ({
486
1002
  editingEnabled = false,
487
1003
  debug: debug3 = false,
488
1004
  clickToInteractEnabled = false,
489
- colorOverrides
1005
+ colorOverrides,
1006
+ spiceSimulationEnabled = false
490
1007
  }) => {
491
1008
  if (debug3) {
492
1009
  enableDebug();
493
1010
  }
494
- const [editModeEnabled, setEditModeEnabled] = useState3(defaultEditMode);
495
- const [snapToGrid, setSnapToGrid] = useState3(true);
496
- const [isInteractionEnabled, setIsInteractionEnabled] = useState3(
1011
+ const [showSpiceOverlay, setShowSpiceOverlay] = useState4(false);
1012
+ const getCircuitHash = (circuitJson2) => {
1013
+ return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
1014
+ };
1015
+ const circuitJsonKey = useMemo(
1016
+ () => getCircuitHash(circuitJson),
1017
+ [circuitJson]
1018
+ );
1019
+ const spiceString = useMemo(() => {
1020
+ if (!spiceSimulationEnabled) return null;
1021
+ try {
1022
+ return getSpiceFromCircuitJson(circuitJson);
1023
+ } catch (e) {
1024
+ console.error("Failed to generate SPICE string", e);
1025
+ return null;
1026
+ }
1027
+ }, [circuitJsonKey, spiceSimulationEnabled]);
1028
+ const {
1029
+ plotData,
1030
+ nodes,
1031
+ isLoading: isSpiceSimLoading,
1032
+ error: spiceSimError
1033
+ } = useSpiceSimulation(spiceString);
1034
+ const [editModeEnabled, setEditModeEnabled] = useState4(defaultEditMode);
1035
+ const [snapToGrid, setSnapToGrid] = useState4(true);
1036
+ const [isInteractionEnabled, setIsInteractionEnabled] = useState4(
497
1037
  !clickToInteractEnabled
498
1038
  );
499
1039
  const svgDivRef = useRef4(null);
@@ -517,12 +1057,9 @@ var SchematicViewer = ({
517
1057
  }
518
1058
  touchStartRef.current = null;
519
1059
  };
520
- const [internalEditEvents, setInternalEditEvents] = useState3([]);
1060
+ const [internalEditEvents, setInternalEditEvents] = useState4([]);
521
1061
  const circuitJsonRef = useRef4(circuitJson);
522
- const getCircuitHash = (circuitJson2) => {
523
- return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
524
- };
525
- useEffect5(() => {
1062
+ useEffect6(() => {
526
1063
  const circuitHash = getCircuitHash(circuitJson);
527
1064
  const circuitHashRef = getCircuitHash(circuitJsonRef.current);
528
1065
  if (circuitHash !== circuitHashRef) {
@@ -540,7 +1077,7 @@ var SchematicViewer = ({
540
1077
  svgDivRef.current.style.transform = transformToString(transform);
541
1078
  },
542
1079
  // @ts-ignore disabled is a valid prop but not typed
543
- enabled: isInteractionEnabled
1080
+ enabled: isInteractionEnabled && !showSpiceOverlay
544
1081
  });
545
1082
  const { containerWidth, containerHeight } = useResizeHandling(containerRef);
546
1083
  const svgString = useMemo(() => {
@@ -590,7 +1127,7 @@ var SchematicViewer = ({
590
1127
  svgToScreenProjection,
591
1128
  circuitJson,
592
1129
  editEvents: editEventsWithUnappliedEditEvents,
593
- enabled: editModeEnabled && isInteractionEnabled,
1130
+ enabled: editModeEnabled && isInteractionEnabled && !showSpiceOverlay,
594
1131
  snapToGrid
595
1132
  }
596
1133
  );
@@ -608,7 +1145,7 @@ var SchematicViewer = ({
608
1145
  editEvents: editEventsWithUnappliedEditEvents
609
1146
  });
610
1147
  const svgDiv = useMemo(
611
- () => /* @__PURE__ */ jsx3(
1148
+ () => /* @__PURE__ */ jsx7(
612
1149
  "div",
613
1150
  {
614
1151
  ref: svgDivRef,
@@ -621,7 +1158,7 @@ var SchematicViewer = ({
621
1158
  ),
622
1159
  [svgString, isInteractionEnabled, clickToInteractEnabled]
623
1160
  );
624
- return /* @__PURE__ */ jsxs2(
1161
+ return /* @__PURE__ */ jsxs4(
625
1162
  "div",
626
1163
  {
627
1164
  ref: containerRef,
@@ -629,10 +1166,15 @@ var SchematicViewer = ({
629
1166
  position: "relative",
630
1167
  backgroundColor: containerBackgroundColor,
631
1168
  overflow: "hidden",
632
- cursor: isDragging ? "grabbing" : clickToInteractEnabled && !isInteractionEnabled ? "pointer" : "grab",
1169
+ cursor: showSpiceOverlay ? "auto" : isDragging ? "grabbing" : clickToInteractEnabled && !isInteractionEnabled ? "pointer" : "grab",
633
1170
  minHeight: "300px",
634
1171
  ...containerStyle
635
1172
  },
1173
+ onWheelCapture: (e) => {
1174
+ if (showSpiceOverlay) {
1175
+ e.stopPropagation();
1176
+ }
1177
+ },
636
1178
  onMouseDown: (e) => {
637
1179
  if (clickToInteractEnabled && !isInteractionEnabled) {
638
1180
  e.preventDefault();
@@ -648,10 +1190,16 @@ var SchematicViewer = ({
648
1190
  return;
649
1191
  }
650
1192
  },
651
- onTouchStart: handleTouchStart,
652
- onTouchEnd: handleTouchEnd,
1193
+ onTouchStart: (e) => {
1194
+ if (showSpiceOverlay) return;
1195
+ handleTouchStart(e);
1196
+ },
1197
+ onTouchEnd: (e) => {
1198
+ if (showSpiceOverlay) return;
1199
+ handleTouchEnd(e);
1200
+ },
653
1201
  children: [
654
- !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx3(
1202
+ !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx7(
655
1203
  "div",
656
1204
  {
657
1205
  onClick: (e) => {
@@ -670,7 +1218,7 @@ var SchematicViewer = ({
670
1218
  pointerEvents: "all",
671
1219
  touchAction: "pan-x pan-y pinch-zoom"
672
1220
  },
673
- children: /* @__PURE__ */ jsx3(
1221
+ children: /* @__PURE__ */ jsx7(
674
1222
  "div",
675
1223
  {
676
1224
  style: {
@@ -687,20 +1235,32 @@ var SchematicViewer = ({
687
1235
  )
688
1236
  }
689
1237
  ),
690
- editingEnabled && /* @__PURE__ */ jsx3(
1238
+ editingEnabled && /* @__PURE__ */ jsx7(
691
1239
  EditIcon,
692
1240
  {
693
1241
  active: editModeEnabled,
694
1242
  onClick: () => setEditModeEnabled(!editModeEnabled)
695
1243
  }
696
1244
  ),
697
- editingEnabled && editModeEnabled && /* @__PURE__ */ jsx3(
1245
+ editingEnabled && editModeEnabled && /* @__PURE__ */ jsx7(
698
1246
  GridIcon,
699
1247
  {
700
1248
  active: snapToGrid,
701
1249
  onClick: () => setSnapToGrid(!snapToGrid)
702
1250
  }
703
1251
  ),
1252
+ spiceSimulationEnabled && /* @__PURE__ */ jsx7(SpiceSimulationIcon, { onClick: () => setShowSpiceOverlay(true) }),
1253
+ showSpiceOverlay && /* @__PURE__ */ jsx7(
1254
+ SpiceSimulationOverlay,
1255
+ {
1256
+ spiceString,
1257
+ onClose: () => setShowSpiceOverlay(false),
1258
+ plotData,
1259
+ nodes,
1260
+ isLoading: isSpiceSimLoading,
1261
+ error: spiceSimError
1262
+ }
1263
+ ),
704
1264
  svgDiv
705
1265
  ]
706
1266
  }