@tscircuit/schematic-viewer 2.0.24 → 2.0.25

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,531 @@ 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
+ let worker;
901
+ if (import.meta.env.DEV) {
902
+ worker = new Worker(
903
+ new URL("../workers/spice-simulation.worker.ts", import.meta.url),
904
+ { type: "module" }
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" });
914
+ }
915
+ worker.onmessage = (event) => {
916
+ if (event.data.type === "result") {
917
+ try {
918
+ const { plotData: parsedData, nodes: parsedNodes } = parseEecEngineOutput(event.data.result);
919
+ setPlotData(parsedData);
920
+ setNodes(parsedNodes);
921
+ } catch (e) {
922
+ setError(e.message || "Failed to parse simulation result");
923
+ console.error(e);
924
+ }
925
+ } else if (event.data.type === "error") {
926
+ setError(event.data.error);
927
+ }
928
+ setIsLoading(false);
929
+ };
930
+ worker.onerror = (err) => {
931
+ setError(err.message);
932
+ setIsLoading(false);
933
+ };
934
+ worker.postMessage({ spiceString });
935
+ return () => {
936
+ worker.terminate();
937
+ };
938
+ }, [spiceString]);
939
+ return { plotData, nodes, isLoading, error };
940
+ };
941
+
942
+ // lib/utils/spice-utils.ts
943
+ import { circuitJsonToSpice } from "circuit-json-to-spice";
944
+ var getSpiceFromCircuitJson = (circuitJson) => {
945
+ const spiceNetlist = circuitJsonToSpice(circuitJson);
946
+ const baseSpiceString = spiceNetlist.toSpiceString();
947
+ const lines = baseSpiceString.split("\n").filter((l) => l.trim() !== "");
948
+ const componentLines = lines.filter(
949
+ (l) => !l.startsWith("*") && !l.startsWith(".") && l.trim() !== ""
950
+ );
951
+ const allNodes = /* @__PURE__ */ new Set();
952
+ const capacitorNodes = /* @__PURE__ */ new Set();
953
+ for (const line of componentLines) {
954
+ const parts = line.trim().split(/\s+/);
955
+ if (parts.length < 3) continue;
956
+ const componentType = parts[0][0].toUpperCase();
957
+ let nodesOnLine = [];
958
+ if (["R", "C", "L", "V", "I", "D"].includes(componentType)) {
959
+ nodesOnLine = parts.slice(1, 3);
960
+ } else if (componentType === "Q" && parts.length >= 4) {
961
+ nodesOnLine = parts.slice(1, 4);
962
+ } else if (componentType === "M" && parts.length >= 5) {
963
+ nodesOnLine = parts.slice(1, 5);
964
+ } else if (componentType === "X") {
965
+ nodesOnLine = parts.slice(1, -1);
966
+ } else {
967
+ continue;
968
+ }
969
+ nodesOnLine.forEach((node) => allNodes.add(node));
970
+ if (componentType === "C") {
971
+ nodesOnLine.forEach((node) => capacitorNodes.add(node));
972
+ }
973
+ }
974
+ allNodes.delete("0");
975
+ capacitorNodes.delete("0");
976
+ const icLines = Array.from(capacitorNodes).map((node) => `.ic V(${node})=0`);
977
+ const probeNodes = Array.from(allNodes).map((node) => `V(${node})`);
978
+ const probeLine = probeNodes.length > 0 ? `.probe ${probeNodes.join(" ")}` : "";
979
+ const tranLine = ".tran 0.1ms 50ms UIC";
980
+ const endStatement = ".end";
981
+ const originalLines = baseSpiceString.split("\n");
982
+ let endIndex = -1;
983
+ for (let i = originalLines.length - 1; i >= 0; i--) {
984
+ if (originalLines[i].trim().toLowerCase().startsWith(endStatement)) {
985
+ endIndex = i;
986
+ break;
987
+ }
988
+ }
989
+ const injectionLines = [...icLines, probeLine, tranLine].filter(Boolean);
990
+ let finalLines;
991
+ if (endIndex !== -1) {
992
+ const beforeEnd = originalLines.slice(0, endIndex);
993
+ const endLineAndAfter = originalLines.slice(endIndex);
994
+ finalLines = [...beforeEnd, ...injectionLines, ...endLineAndAfter];
995
+ } else {
996
+ finalLines = [...originalLines, ...injectionLines, endStatement];
997
+ }
998
+ return finalLines.join("\n");
999
+ };
1000
+
477
1001
  // lib/components/SchematicViewer.tsx
478
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1002
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
479
1003
  var SchematicViewer = ({
480
1004
  circuitJson,
481
1005
  containerStyle,
@@ -486,14 +1010,38 @@ var SchematicViewer = ({
486
1010
  editingEnabled = false,
487
1011
  debug: debug3 = false,
488
1012
  clickToInteractEnabled = false,
489
- colorOverrides
1013
+ colorOverrides,
1014
+ spiceSimulationEnabled = false
490
1015
  }) => {
491
1016
  if (debug3) {
492
1017
  enableDebug();
493
1018
  }
494
- const [editModeEnabled, setEditModeEnabled] = useState3(defaultEditMode);
495
- const [snapToGrid, setSnapToGrid] = useState3(true);
496
- const [isInteractionEnabled, setIsInteractionEnabled] = useState3(
1019
+ const [showSpiceOverlay, setShowSpiceOverlay] = useState4(false);
1020
+ const getCircuitHash = (circuitJson2) => {
1021
+ return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
1022
+ };
1023
+ const circuitJsonKey = useMemo(
1024
+ () => getCircuitHash(circuitJson),
1025
+ [circuitJson]
1026
+ );
1027
+ const spiceString = useMemo(() => {
1028
+ if (!spiceSimulationEnabled) return null;
1029
+ try {
1030
+ return getSpiceFromCircuitJson(circuitJson);
1031
+ } catch (e) {
1032
+ console.error("Failed to generate SPICE string", e);
1033
+ return null;
1034
+ }
1035
+ }, [circuitJsonKey, spiceSimulationEnabled]);
1036
+ const {
1037
+ plotData,
1038
+ nodes,
1039
+ isLoading: isSpiceSimLoading,
1040
+ error: spiceSimError
1041
+ } = useSpiceSimulation(spiceString);
1042
+ const [editModeEnabled, setEditModeEnabled] = useState4(defaultEditMode);
1043
+ const [snapToGrid, setSnapToGrid] = useState4(true);
1044
+ const [isInteractionEnabled, setIsInteractionEnabled] = useState4(
497
1045
  !clickToInteractEnabled
498
1046
  );
499
1047
  const svgDivRef = useRef4(null);
@@ -517,12 +1065,9 @@ var SchematicViewer = ({
517
1065
  }
518
1066
  touchStartRef.current = null;
519
1067
  };
520
- const [internalEditEvents, setInternalEditEvents] = useState3([]);
1068
+ const [internalEditEvents, setInternalEditEvents] = useState4([]);
521
1069
  const circuitJsonRef = useRef4(circuitJson);
522
- const getCircuitHash = (circuitJson2) => {
523
- return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
524
- };
525
- useEffect5(() => {
1070
+ useEffect6(() => {
526
1071
  const circuitHash = getCircuitHash(circuitJson);
527
1072
  const circuitHashRef = getCircuitHash(circuitJsonRef.current);
528
1073
  if (circuitHash !== circuitHashRef) {
@@ -540,7 +1085,7 @@ var SchematicViewer = ({
540
1085
  svgDivRef.current.style.transform = transformToString(transform);
541
1086
  },
542
1087
  // @ts-ignore disabled is a valid prop but not typed
543
- enabled: isInteractionEnabled
1088
+ enabled: isInteractionEnabled && !showSpiceOverlay
544
1089
  });
545
1090
  const { containerWidth, containerHeight } = useResizeHandling(containerRef);
546
1091
  const svgString = useMemo(() => {
@@ -590,7 +1135,7 @@ var SchematicViewer = ({
590
1135
  svgToScreenProjection,
591
1136
  circuitJson,
592
1137
  editEvents: editEventsWithUnappliedEditEvents,
593
- enabled: editModeEnabled && isInteractionEnabled,
1138
+ enabled: editModeEnabled && isInteractionEnabled && !showSpiceOverlay,
594
1139
  snapToGrid
595
1140
  }
596
1141
  );
@@ -608,7 +1153,7 @@ var SchematicViewer = ({
608
1153
  editEvents: editEventsWithUnappliedEditEvents
609
1154
  });
610
1155
  const svgDiv = useMemo(
611
- () => /* @__PURE__ */ jsx3(
1156
+ () => /* @__PURE__ */ jsx7(
612
1157
  "div",
613
1158
  {
614
1159
  ref: svgDivRef,
@@ -621,7 +1166,7 @@ var SchematicViewer = ({
621
1166
  ),
622
1167
  [svgString, isInteractionEnabled, clickToInteractEnabled]
623
1168
  );
624
- return /* @__PURE__ */ jsxs2(
1169
+ return /* @__PURE__ */ jsxs4(
625
1170
  "div",
626
1171
  {
627
1172
  ref: containerRef,
@@ -629,10 +1174,15 @@ var SchematicViewer = ({
629
1174
  position: "relative",
630
1175
  backgroundColor: containerBackgroundColor,
631
1176
  overflow: "hidden",
632
- cursor: isDragging ? "grabbing" : clickToInteractEnabled && !isInteractionEnabled ? "pointer" : "grab",
1177
+ cursor: showSpiceOverlay ? "auto" : isDragging ? "grabbing" : clickToInteractEnabled && !isInteractionEnabled ? "pointer" : "grab",
633
1178
  minHeight: "300px",
634
1179
  ...containerStyle
635
1180
  },
1181
+ onWheelCapture: (e) => {
1182
+ if (showSpiceOverlay) {
1183
+ e.stopPropagation();
1184
+ }
1185
+ },
636
1186
  onMouseDown: (e) => {
637
1187
  if (clickToInteractEnabled && !isInteractionEnabled) {
638
1188
  e.preventDefault();
@@ -648,10 +1198,16 @@ var SchematicViewer = ({
648
1198
  return;
649
1199
  }
650
1200
  },
651
- onTouchStart: handleTouchStart,
652
- onTouchEnd: handleTouchEnd,
1201
+ onTouchStart: (e) => {
1202
+ if (showSpiceOverlay) return;
1203
+ handleTouchStart(e);
1204
+ },
1205
+ onTouchEnd: (e) => {
1206
+ if (showSpiceOverlay) return;
1207
+ handleTouchEnd(e);
1208
+ },
653
1209
  children: [
654
- !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx3(
1210
+ !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx7(
655
1211
  "div",
656
1212
  {
657
1213
  onClick: (e) => {
@@ -670,7 +1226,7 @@ var SchematicViewer = ({
670
1226
  pointerEvents: "all",
671
1227
  touchAction: "pan-x pan-y pinch-zoom"
672
1228
  },
673
- children: /* @__PURE__ */ jsx3(
1229
+ children: /* @__PURE__ */ jsx7(
674
1230
  "div",
675
1231
  {
676
1232
  style: {
@@ -687,20 +1243,32 @@ var SchematicViewer = ({
687
1243
  )
688
1244
  }
689
1245
  ),
690
- editingEnabled && /* @__PURE__ */ jsx3(
1246
+ editingEnabled && /* @__PURE__ */ jsx7(
691
1247
  EditIcon,
692
1248
  {
693
1249
  active: editModeEnabled,
694
1250
  onClick: () => setEditModeEnabled(!editModeEnabled)
695
1251
  }
696
1252
  ),
697
- editingEnabled && editModeEnabled && /* @__PURE__ */ jsx3(
1253
+ editingEnabled && editModeEnabled && /* @__PURE__ */ jsx7(
698
1254
  GridIcon,
699
1255
  {
700
1256
  active: snapToGrid,
701
1257
  onClick: () => setSnapToGrid(!snapToGrid)
702
1258
  }
703
1259
  ),
1260
+ spiceSimulationEnabled && /* @__PURE__ */ jsx7(SpiceSimulationIcon, { onClick: () => setShowSpiceOverlay(true) }),
1261
+ showSpiceOverlay && /* @__PURE__ */ jsx7(
1262
+ SpiceSimulationOverlay,
1263
+ {
1264
+ spiceString,
1265
+ onClose: () => setShowSpiceOverlay(false),
1266
+ plotData,
1267
+ nodes,
1268
+ isLoading: isSpiceSimLoading,
1269
+ error: spiceSimError
1270
+ }
1271
+ ),
704
1272
  svgDiv
705
1273
  ]
706
1274
  }