@tscircuit/schematic-viewer 2.0.48 → 2.0.50

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/bun.lockb CHANGED
Binary file
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ManualEditEvent } from '@tscircuit/props';
4
4
  import { CircuitJson } from 'circuit-json';
5
5
  import { ReactNode } from 'react';
6
6
 
7
- interface Props {
7
+ interface Props$1 {
8
8
  circuitJson: CircuitJson;
9
9
  containerStyle?: React.CSSProperties;
10
10
  editEvents?: ManualEditEvent[];
@@ -22,7 +22,7 @@ interface Props {
22
22
  event: MouseEvent;
23
23
  }) => void;
24
24
  }
25
- declare const SchematicViewer: ({ circuitJson, containerStyle, editEvents: unappliedEditEvents, onEditEvent, defaultEditMode, debugGrid, editingEnabled, debug, clickToInteractEnabled, colorOverrides, spiceSimulationEnabled, disableGroups, onSchematicComponentClicked, }: Props) => react_jsx_runtime.JSX.Element;
25
+ declare const SchematicViewer: ({ circuitJson, containerStyle, editEvents: unappliedEditEvents, onEditEvent, defaultEditMode, debugGrid, editingEnabled, debug, clickToInteractEnabled, colorOverrides, spiceSimulationEnabled, disableGroups, onSchematicComponentClicked, }: Props$1) => react_jsx_runtime.JSX.Element;
26
26
 
27
27
  interface BoundingBoxBounds {
28
28
  minX: number;
@@ -42,4 +42,14 @@ declare const useMouseEventsOverBoundingBox: (options: UseMouseEventsOverBoundin
42
42
  hovering: boolean;
43
43
  };
44
44
 
45
- export { MouseTracker, SchematicViewer, useMouseEventsOverBoundingBox };
45
+ interface Props {
46
+ circuitJson: CircuitJson;
47
+ containerStyle?: React.CSSProperties;
48
+ colorOverrides?: ColorOverrides;
49
+ width?: number;
50
+ height?: number;
51
+ className?: string;
52
+ }
53
+ declare const AnalogSimulationViewer: ({ circuitJson: inputCircuitJson, containerStyle, colorOverrides, width, height, className, }: Props) => react_jsx_runtime.JSX.Element;
54
+
55
+ export { AnalogSimulationViewer, MouseTracker, SchematicViewer, useMouseEventsOverBoundingBox };
package/dist/index.js CHANGED
@@ -711,6 +711,7 @@ var EditIcon = ({
711
711
  {
712
712
  onClick: handleInteraction,
713
713
  onTouchEnd: handleInteraction,
714
+ title: active ? "Disable edit mode" : "Enable edit mode",
714
715
  style: {
715
716
  position: "absolute",
716
717
  top: "16px",
@@ -760,6 +761,7 @@ var GridIcon = ({
760
761
  {
761
762
  onClick: handleInteraction,
762
763
  onTouchEnd: handleInteraction,
764
+ title: active ? "Hide grid" : "Show grid",
763
765
  style: {
764
766
  position: "absolute",
765
767
  top: "56px",
@@ -806,6 +808,7 @@ var ViewMenuIcon = ({
806
808
  {
807
809
  onClick: handleInteraction,
808
810
  onTouchEnd: handleInteraction,
811
+ title: active ? "Hide view menu" : "Show view menu",
809
812
  style: {
810
813
  position: "absolute",
811
814
  top: "16px",
@@ -848,7 +851,7 @@ import { su as su5 } from "@tscircuit/soup-util";
848
851
  // package.json
849
852
  var package_default = {
850
853
  name: "@tscircuit/schematic-viewer",
851
- version: "2.0.47",
854
+ version: "2.0.49",
852
855
  main: "dist/index.js",
853
856
  type: "module",
854
857
  scripts: {
@@ -875,7 +878,7 @@ var package_default = {
875
878
  "react-dom": "^19.1.0",
876
879
  "react-reconciler": "^0.31.0",
877
880
  semver: "^7.7.2",
878
- tscircuit: "^0.0.611",
881
+ tscircuit: "^0.0.1037",
879
882
  tsup: "^8.3.5",
880
883
  vite: "^6.0.3"
881
884
  },
@@ -885,7 +888,7 @@ var package_default = {
885
888
  },
886
889
  dependencies: {
887
890
  "chart.js": "^4.5.0",
888
- "circuit-json-to-spice": "^0.0.10",
891
+ "circuit-json-to-spice": "^0.0.30",
889
892
  debug: "^4.4.0",
890
893
  "performance-now": "^2.1.0",
891
894
  "react-chartjs-2": "^5.3.0",
@@ -1079,6 +1082,7 @@ var SpiceSimulationIcon = ({
1079
1082
  "div",
1080
1083
  {
1081
1084
  onClick,
1085
+ title: "Run SPICE simulation",
1082
1086
  style: {
1083
1087
  position: "absolute",
1084
1088
  top: "16px",
@@ -2541,7 +2545,246 @@ var SchematicViewer = ({
2541
2545
  )
2542
2546
  ] });
2543
2547
  };
2548
+
2549
+ // lib/components/AnalogSimulationViewer.tsx
2550
+ import {
2551
+ convertCircuitJsonToSchematicSimulationSvg
2552
+ } from "circuit-to-svg";
2553
+ import { useEffect as useEffect12, useState as useState7, useMemo as useMemo6, useRef as useRef8 } from "react";
2554
+ import { useMouseMatrixTransform as useMouseMatrixTransform2 } from "use-mouse-matrix-transform";
2555
+ import { toString as transformToString2 } from "transformation-matrix";
2556
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
2557
+ var AnalogSimulationViewer = ({
2558
+ circuitJson: inputCircuitJson,
2559
+ containerStyle,
2560
+ colorOverrides,
2561
+ width,
2562
+ height,
2563
+ className
2564
+ }) => {
2565
+ const [circuitJson, setCircuitJson] = useState7(null);
2566
+ const [isLoading, setIsLoading] = useState7(true);
2567
+ const [error, setError] = useState7(null);
2568
+ const [svgObjectUrl, setSvgObjectUrl] = useState7(null);
2569
+ const containerRef = useRef8(null);
2570
+ const imgRef = useRef8(null);
2571
+ const { containerWidth, containerHeight } = useResizeHandling(
2572
+ containerRef
2573
+ );
2574
+ const [isDragging, setIsDragging] = useState7(false);
2575
+ const {
2576
+ ref: transformRef,
2577
+ cancelDrag: _cancelDrag,
2578
+ transform: _svgToScreenProjection
2579
+ } = useMouseMatrixTransform2({
2580
+ onSetTransform(transform) {
2581
+ if (imgRef.current) {
2582
+ imgRef.current.style.transform = transformToString2(transform);
2583
+ }
2584
+ }
2585
+ });
2586
+ const effectiveWidth = width || containerWidth || 1e3;
2587
+ const effectiveHeight = height || containerHeight || 600;
2588
+ useEffect12(() => {
2589
+ setIsLoading(true);
2590
+ setError(null);
2591
+ setCircuitJson(inputCircuitJson);
2592
+ setIsLoading(false);
2593
+ }, [inputCircuitJson]);
2594
+ const simulationExperimentId = useMemo6(() => {
2595
+ if (!circuitJson) return null;
2596
+ const simulationElement = circuitJson.find(
2597
+ (el) => el.type === "simulation_experiment"
2598
+ );
2599
+ return simulationElement?.simulation_experiment_id || null;
2600
+ }, [circuitJson]);
2601
+ const simulationGraphIds = useMemo6(() => {
2602
+ if (!circuitJson) return [];
2603
+ return circuitJson.filter((el) => el.type === "simulation_transient_voltage_graph").map((el) => el.simulation_transient_voltage_graph_id);
2604
+ }, [circuitJson]);
2605
+ const simulationSvg = useMemo6(() => {
2606
+ if (!circuitJson || !effectiveWidth || !effectiveHeight || !simulationExperimentId)
2607
+ return "";
2608
+ try {
2609
+ return convertCircuitJsonToSchematicSimulationSvg({
2610
+ circuitJson,
2611
+ simulation_experiment_id: simulationExperimentId,
2612
+ simulation_transient_voltage_graph_ids: simulationGraphIds,
2613
+ width: effectiveWidth,
2614
+ height: effectiveHeight,
2615
+ schematicOptions: { colorOverrides }
2616
+ });
2617
+ } catch (fallbackErr) {
2618
+ console.error("Failed to generate fallback schematic SVG:", fallbackErr);
2619
+ return "";
2620
+ }
2621
+ }, [
2622
+ circuitJson,
2623
+ effectiveWidth,
2624
+ effectiveHeight,
2625
+ colorOverrides,
2626
+ simulationExperimentId,
2627
+ simulationGraphIds
2628
+ ]);
2629
+ useEffect12(() => {
2630
+ if (!simulationSvg) {
2631
+ setSvgObjectUrl(null);
2632
+ return;
2633
+ }
2634
+ try {
2635
+ const blob = new Blob([simulationSvg], { type: "image/svg+xml" });
2636
+ const url = URL.createObjectURL(blob);
2637
+ setSvgObjectUrl(url);
2638
+ return () => {
2639
+ URL.revokeObjectURL(url);
2640
+ };
2641
+ } catch (error2) {
2642
+ console.error("Failed to create SVG object URL:", error2);
2643
+ setSvgObjectUrl(null);
2644
+ }
2645
+ }, [simulationSvg]);
2646
+ const containerBackgroundColor = useMemo6(() => {
2647
+ if (!simulationSvg) return "transparent";
2648
+ const match = simulationSvg.match(
2649
+ /<svg[^>]*style="[^"]*background-color:\s*([^;\"]+)/i
2650
+ );
2651
+ return match?.[1] ?? "transparent";
2652
+ }, [simulationSvg]);
2653
+ const handleMouseDown = (_e) => {
2654
+ setIsDragging(true);
2655
+ };
2656
+ const handleTouchStart = (_e) => {
2657
+ setIsDragging(true);
2658
+ };
2659
+ useEffect12(() => {
2660
+ const handleMouseUp = () => {
2661
+ setIsDragging(false);
2662
+ };
2663
+ const handleTouchEnd = () => {
2664
+ setIsDragging(false);
2665
+ };
2666
+ window.addEventListener("mouseup", handleMouseUp);
2667
+ window.addEventListener("touchend", handleTouchEnd);
2668
+ return () => {
2669
+ window.removeEventListener("mouseup", handleMouseUp);
2670
+ window.removeEventListener("touchend", handleTouchEnd);
2671
+ };
2672
+ }, []);
2673
+ if (isLoading) {
2674
+ return /* @__PURE__ */ jsx12(
2675
+ "div",
2676
+ {
2677
+ style: {
2678
+ display: "flex",
2679
+ alignItems: "center",
2680
+ justifyContent: "center",
2681
+ backgroundColor: "#f5f5f5",
2682
+ minHeight: "300px",
2683
+ fontFamily: "sans-serif",
2684
+ fontSize: "16px",
2685
+ color: "#666",
2686
+ ...containerStyle
2687
+ },
2688
+ className,
2689
+ children: "Loading circuit..."
2690
+ }
2691
+ );
2692
+ }
2693
+ if (error) {
2694
+ return /* @__PURE__ */ jsx12(
2695
+ "div",
2696
+ {
2697
+ style: {
2698
+ display: "flex",
2699
+ alignItems: "center",
2700
+ justifyContent: "center",
2701
+ backgroundColor: "#fef2f2",
2702
+ minHeight: "300px",
2703
+ fontFamily: "sans-serif",
2704
+ fontSize: "16px",
2705
+ color: "#dc2626",
2706
+ ...containerStyle
2707
+ },
2708
+ className,
2709
+ children: /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center", padding: "20px" }, children: [
2710
+ /* @__PURE__ */ jsx12("div", { style: { fontWeight: "bold", marginBottom: "8px" }, children: "Circuit Conversion Error" }),
2711
+ /* @__PURE__ */ jsx12("div", { style: { fontSize: "14px" }, children: error })
2712
+ ] })
2713
+ }
2714
+ );
2715
+ }
2716
+ if (!simulationSvg) {
2717
+ return /* @__PURE__ */ jsx12(
2718
+ "div",
2719
+ {
2720
+ style: {
2721
+ display: "flex",
2722
+ alignItems: "center",
2723
+ justifyContent: "center",
2724
+ backgroundColor: "#fef2f2",
2725
+ minHeight: "300px",
2726
+ fontFamily: "sans-serif",
2727
+ fontSize: "16px",
2728
+ color: "#dc2626",
2729
+ ...containerStyle
2730
+ },
2731
+ className,
2732
+ children: "Failed to generate simulation SVG"
2733
+ }
2734
+ );
2735
+ }
2736
+ return /* @__PURE__ */ jsx12(
2737
+ "div",
2738
+ {
2739
+ ref: (node) => {
2740
+ containerRef.current = node;
2741
+ transformRef.current = node;
2742
+ },
2743
+ style: {
2744
+ position: "relative",
2745
+ backgroundColor: containerBackgroundColor,
2746
+ overflow: "hidden",
2747
+ minHeight: "300px",
2748
+ cursor: isDragging ? "grabbing" : "grab",
2749
+ ...containerStyle
2750
+ },
2751
+ className,
2752
+ onMouseDown: handleMouseDown,
2753
+ onTouchStart: handleTouchStart,
2754
+ children: svgObjectUrl ? /* @__PURE__ */ jsx12(
2755
+ "img",
2756
+ {
2757
+ ref: imgRef,
2758
+ src: svgObjectUrl,
2759
+ alt: "Circuit Simulation",
2760
+ style: {
2761
+ transformOrigin: "0 0",
2762
+ width: "100%",
2763
+ height: "100%",
2764
+ display: "block",
2765
+ objectFit: "contain"
2766
+ }
2767
+ }
2768
+ ) : /* @__PURE__ */ jsx12(
2769
+ "div",
2770
+ {
2771
+ style: {
2772
+ display: "flex",
2773
+ alignItems: "center",
2774
+ justifyContent: "center",
2775
+ height: "100%",
2776
+ minHeight: "300px",
2777
+ color: "#666",
2778
+ fontFamily: "sans-serif"
2779
+ },
2780
+ children: "Failed to render SVG"
2781
+ }
2782
+ )
2783
+ }
2784
+ );
2785
+ };
2544
2786
  export {
2787
+ AnalogSimulationViewer,
2545
2788
  MouseTracker,
2546
2789
  SchematicViewer,
2547
2790
  useMouseEventsOverBoundingBox