@tscircuit/schematic-viewer 2.0.41 → 2.0.43

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
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  convertCircuitJsonToSchematicSvg
4
4
  } from "circuit-to-svg";
5
+ import { su as su6 } from "@tscircuit/soup-util";
5
6
 
6
7
  // lib/hooks/useChangeSchematicComponentLocationsInSvg.ts
7
8
  import "@tscircuit/soup-util";
@@ -469,7 +470,7 @@ var enableDebug = () => {
469
470
  var debug_default = debug;
470
471
 
471
472
  // lib/components/SchematicViewer.tsx
472
- import { useEffect as useEffect8, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
473
+ import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef7, useState as useState6 } from "react";
473
474
  import {
474
475
  fromString,
475
476
  identity,
@@ -691,7 +692,8 @@ var zIndexMap = {
691
692
  viewMenuIcon: 48,
692
693
  viewMenu: 55,
693
694
  viewMenuBackdrop: 54,
694
- clickToInteractOverlay: 100
695
+ clickToInteractOverlay: 100,
696
+ schematicComponentHoverOutline: 47
695
697
  };
696
698
 
697
699
  // lib/components/EditIcon.tsx
@@ -846,7 +848,7 @@ import { su as su5 } from "@tscircuit/soup-util";
846
848
  // package.json
847
849
  var package_default = {
848
850
  name: "@tscircuit/schematic-viewer",
849
- version: "2.0.40",
851
+ version: "2.0.42",
850
852
  main: "dist/index.js",
851
853
  type: "module",
852
854
  scripts: {
@@ -1148,7 +1150,8 @@ var SpicePlot = ({
1148
1150
  plotData,
1149
1151
  nodes,
1150
1152
  isLoading,
1151
- error
1153
+ error,
1154
+ hasRun
1152
1155
  }) => {
1153
1156
  const yAxisLabel = useMemo2(() => {
1154
1157
  const hasVoltage = nodes.some((n) => n.toLowerCase().startsWith("v("));
@@ -1173,6 +1176,21 @@ var SpicePlot = ({
1173
1176
  }
1174
1177
  );
1175
1178
  }
1179
+ if (!hasRun) {
1180
+ return /* @__PURE__ */ jsx7(
1181
+ "div",
1182
+ {
1183
+ style: {
1184
+ height: "300px",
1185
+ width: "100%",
1186
+ display: "flex",
1187
+ alignItems: "center",
1188
+ justifyContent: "center"
1189
+ },
1190
+ children: 'Click "Run" to start the simulation.'
1191
+ }
1192
+ );
1193
+ }
1176
1194
  if (error) {
1177
1195
  return /* @__PURE__ */ jsxs4(
1178
1196
  "div",
@@ -1294,7 +1312,8 @@ var SpiceSimulationOverlay = ({
1294
1312
  isLoading,
1295
1313
  error,
1296
1314
  simOptions,
1297
- onSimOptionsChange
1315
+ onSimOptionsChange,
1316
+ hasRun
1298
1317
  }) => {
1299
1318
  const [startTimeDraft, setStartTimeDraft] = useState3(
1300
1319
  String(simOptions.startTime)
@@ -1397,7 +1416,8 @@ var SpiceSimulationOverlay = ({
1397
1416
  plotData,
1398
1417
  nodes: filteredNodes,
1399
1418
  isLoading,
1400
- error
1419
+ error,
1420
+ hasRun
1401
1421
  }
1402
1422
  ) }),
1403
1423
  /* @__PURE__ */ jsxs5(
@@ -1501,7 +1521,7 @@ var SpiceSimulationOverlay = ({
1501
1521
  backgroundColor: "#f0f0f0",
1502
1522
  cursor: "pointer"
1503
1523
  },
1504
- children: "Rerun"
1524
+ children: hasRun ? "Rerun" : "Run"
1505
1525
  }
1506
1526
  )
1507
1527
  ] })
@@ -1758,8 +1778,349 @@ var setStoredBoolean = (key, value) => {
1758
1778
  }
1759
1779
  };
1760
1780
 
1781
+ // lib/components/MouseTracker.tsx
1782
+ import {
1783
+ createContext,
1784
+ useCallback as useCallback3,
1785
+ useContext,
1786
+ useEffect as useEffect8,
1787
+ useMemo as useMemo3,
1788
+ useRef as useRef4
1789
+ } from "react";
1790
+ import { Fragment as Fragment2, jsx as jsx9 } from "react/jsx-runtime";
1791
+ var MouseTrackerContext = createContext(null);
1792
+ var boundsAreEqual = (a, b) => {
1793
+ if (!a && !b) return true;
1794
+ if (!a || !b) return false;
1795
+ return a.minX === b.minX && a.maxX === b.maxX && a.minY === b.minY && a.maxY === b.maxY;
1796
+ };
1797
+ var MouseTracker = ({ children }) => {
1798
+ const existingContext = useContext(MouseTrackerContext);
1799
+ if (existingContext) {
1800
+ return /* @__PURE__ */ jsx9(Fragment2, { children });
1801
+ }
1802
+ const storeRef = useRef4({
1803
+ pointer: null,
1804
+ boundingBoxes: /* @__PURE__ */ new Map(),
1805
+ hoveringIds: /* @__PURE__ */ new Set(),
1806
+ subscribers: /* @__PURE__ */ new Set()
1807
+ });
1808
+ const notifySubscribers = useCallback3(() => {
1809
+ for (const callback of storeRef.current.subscribers) {
1810
+ callback();
1811
+ }
1812
+ }, []);
1813
+ const updateHovering = useCallback3(() => {
1814
+ const pointer = storeRef.current.pointer;
1815
+ const newHovering = /* @__PURE__ */ new Set();
1816
+ if (pointer) {
1817
+ for (const [id, registration] of storeRef.current.boundingBoxes) {
1818
+ const bounds = registration.bounds;
1819
+ if (!bounds) continue;
1820
+ if (pointer.x >= bounds.minX && pointer.x <= bounds.maxX && pointer.y >= bounds.minY && pointer.y <= bounds.maxY) {
1821
+ newHovering.add(id);
1822
+ }
1823
+ }
1824
+ }
1825
+ const prevHovering = storeRef.current.hoveringIds;
1826
+ if (newHovering.size === prevHovering.size && [...newHovering].every((id) => prevHovering.has(id))) {
1827
+ return;
1828
+ }
1829
+ storeRef.current.hoveringIds = newHovering;
1830
+ notifySubscribers();
1831
+ }, [notifySubscribers]);
1832
+ const registerBoundingBox = useCallback3(
1833
+ (id, registration) => {
1834
+ storeRef.current.boundingBoxes.set(id, registration);
1835
+ updateHovering();
1836
+ },
1837
+ [updateHovering]
1838
+ );
1839
+ const updateBoundingBox = useCallback3(
1840
+ (id, registration) => {
1841
+ const existing = storeRef.current.boundingBoxes.get(id);
1842
+ if (existing && boundsAreEqual(existing.bounds, registration.bounds) && existing.onClick === registration.onClick) {
1843
+ return;
1844
+ }
1845
+ storeRef.current.boundingBoxes.set(id, registration);
1846
+ updateHovering();
1847
+ },
1848
+ [updateHovering]
1849
+ );
1850
+ const unregisterBoundingBox = useCallback3(
1851
+ (id) => {
1852
+ const removed = storeRef.current.boundingBoxes.delete(id);
1853
+ if (removed) {
1854
+ updateHovering();
1855
+ }
1856
+ },
1857
+ [updateHovering]
1858
+ );
1859
+ const subscribe = useCallback3((listener) => {
1860
+ storeRef.current.subscribers.add(listener);
1861
+ return () => {
1862
+ storeRef.current.subscribers.delete(listener);
1863
+ };
1864
+ }, []);
1865
+ const isHovering = useCallback3((id) => {
1866
+ return storeRef.current.hoveringIds.has(id);
1867
+ }, []);
1868
+ useEffect8(() => {
1869
+ const handlePointerPosition = (event) => {
1870
+ const { clientX, clientY } = event;
1871
+ const pointer = storeRef.current.pointer;
1872
+ if (pointer && pointer.x === clientX && pointer.y === clientY) {
1873
+ return;
1874
+ }
1875
+ storeRef.current.pointer = { x: clientX, y: clientY };
1876
+ updateHovering();
1877
+ };
1878
+ const handlePointerLeave = () => {
1879
+ if (storeRef.current.pointer === null) return;
1880
+ storeRef.current.pointer = null;
1881
+ updateHovering();
1882
+ };
1883
+ const handleClick = (event) => {
1884
+ const { clientX, clientY } = event;
1885
+ for (const registration of storeRef.current.boundingBoxes.values()) {
1886
+ const bounds = registration.bounds;
1887
+ if (!bounds) continue;
1888
+ if (clientX >= bounds.minX && clientX <= bounds.maxX && clientY >= bounds.minY && clientY <= bounds.maxY) {
1889
+ registration.onClick?.(event);
1890
+ }
1891
+ }
1892
+ };
1893
+ window.addEventListener("pointermove", handlePointerPosition, {
1894
+ passive: true
1895
+ });
1896
+ window.addEventListener("pointerdown", handlePointerPosition, {
1897
+ passive: true
1898
+ });
1899
+ window.addEventListener("pointerup", handlePointerPosition, {
1900
+ passive: true
1901
+ });
1902
+ window.addEventListener("pointerleave", handlePointerLeave);
1903
+ window.addEventListener("pointercancel", handlePointerLeave);
1904
+ window.addEventListener("blur", handlePointerLeave);
1905
+ window.addEventListener("click", handleClick, { passive: true });
1906
+ return () => {
1907
+ window.removeEventListener("pointermove", handlePointerPosition);
1908
+ window.removeEventListener("pointerdown", handlePointerPosition);
1909
+ window.removeEventListener("pointerup", handlePointerPosition);
1910
+ window.removeEventListener("pointerleave", handlePointerLeave);
1911
+ window.removeEventListener("pointercancel", handlePointerLeave);
1912
+ window.removeEventListener("blur", handlePointerLeave);
1913
+ window.removeEventListener("click", handleClick);
1914
+ };
1915
+ }, [updateHovering]);
1916
+ const value = useMemo3(
1917
+ () => ({
1918
+ registerBoundingBox,
1919
+ updateBoundingBox,
1920
+ unregisterBoundingBox,
1921
+ subscribe,
1922
+ isHovering
1923
+ }),
1924
+ [
1925
+ registerBoundingBox,
1926
+ updateBoundingBox,
1927
+ unregisterBoundingBox,
1928
+ subscribe,
1929
+ isHovering
1930
+ ]
1931
+ );
1932
+ return /* @__PURE__ */ jsx9(MouseTrackerContext.Provider, { value, children });
1933
+ };
1934
+
1935
+ // lib/components/SchematicComponentMouseTarget.tsx
1936
+ import { useCallback as useCallback4, useEffect as useEffect10, useRef as useRef6, useState as useState5 } from "react";
1937
+
1938
+ // lib/hooks/useMouseEventsOverBoundingBox.ts
1939
+ import {
1940
+ useContext as useContext2,
1941
+ useEffect as useEffect9,
1942
+ useId,
1943
+ useMemo as useMemo4,
1944
+ useRef as useRef5,
1945
+ useSyncExternalStore
1946
+ } from "react";
1947
+ var useMouseEventsOverBoundingBox = (options) => {
1948
+ const context = useContext2(MouseTrackerContext);
1949
+ if (!context) {
1950
+ throw new Error(
1951
+ "useMouseEventsOverBoundingBox must be used within a MouseTracker"
1952
+ );
1953
+ }
1954
+ const id = useId();
1955
+ const latestOptionsRef = useRef5(options);
1956
+ latestOptionsRef.current = options;
1957
+ const handleClick = useMemo4(
1958
+ () => (event) => {
1959
+ latestOptionsRef.current.onClick?.(event);
1960
+ },
1961
+ []
1962
+ );
1963
+ useEffect9(() => {
1964
+ context.registerBoundingBox(id, {
1965
+ bounds: latestOptionsRef.current.bounds,
1966
+ onClick: latestOptionsRef.current.onClick ? handleClick : void 0
1967
+ });
1968
+ return () => {
1969
+ context.unregisterBoundingBox(id);
1970
+ };
1971
+ }, [context, handleClick, id]);
1972
+ useEffect9(() => {
1973
+ context.updateBoundingBox(id, {
1974
+ bounds: latestOptionsRef.current.bounds,
1975
+ onClick: latestOptionsRef.current.onClick ? handleClick : void 0
1976
+ });
1977
+ }, [
1978
+ context,
1979
+ handleClick,
1980
+ id,
1981
+ options.bounds?.minX,
1982
+ options.bounds?.maxX,
1983
+ options.bounds?.minY,
1984
+ options.bounds?.maxY,
1985
+ options.onClick
1986
+ ]);
1987
+ const hovering = useSyncExternalStore(
1988
+ context.subscribe,
1989
+ () => context.isHovering(id),
1990
+ () => false
1991
+ );
1992
+ return { hovering };
1993
+ };
1994
+
1995
+ // lib/components/SchematicComponentMouseTarget.tsx
1996
+ import { jsx as jsx10 } from "react/jsx-runtime";
1997
+ var areMeasurementsEqual = (a, b) => {
1998
+ if (!a && !b) return true;
1999
+ if (!a || !b) return false;
2000
+ return Math.abs(a.bounds.minX - b.bounds.minX) < 0.5 && Math.abs(a.bounds.maxX - b.bounds.maxX) < 0.5 && Math.abs(a.bounds.minY - b.bounds.minY) < 0.5 && Math.abs(a.bounds.maxY - b.bounds.maxY) < 0.5 && Math.abs(a.rect.left - b.rect.left) < 0.5 && Math.abs(a.rect.top - b.rect.top) < 0.5 && Math.abs(a.rect.width - b.rect.width) < 0.5 && Math.abs(a.rect.height - b.rect.height) < 0.5;
2001
+ };
2002
+ var SchematicComponentMouseTarget = ({
2003
+ componentId,
2004
+ svgDivRef,
2005
+ containerRef,
2006
+ onComponentClick,
2007
+ showOutline,
2008
+ circuitJsonKey
2009
+ }) => {
2010
+ const [measurement, setMeasurement] = useState5(null);
2011
+ const frameRef = useRef6(null);
2012
+ const measure = useCallback4(() => {
2013
+ frameRef.current = null;
2014
+ const svgDiv = svgDivRef.current;
2015
+ const container = containerRef.current;
2016
+ if (!svgDiv || !container) {
2017
+ setMeasurement((prev) => prev ? null : prev);
2018
+ return;
2019
+ }
2020
+ const element = svgDiv.querySelector(
2021
+ `[data-schematic-component-id="${componentId}"]`
2022
+ );
2023
+ if (!element) {
2024
+ setMeasurement((prev) => prev ? null : prev);
2025
+ return;
2026
+ }
2027
+ const elementRect = element.getBoundingClientRect();
2028
+ const containerRect = container.getBoundingClientRect();
2029
+ const nextMeasurement = {
2030
+ bounds: {
2031
+ minX: elementRect.left,
2032
+ maxX: elementRect.right,
2033
+ minY: elementRect.top,
2034
+ maxY: elementRect.bottom
2035
+ },
2036
+ rect: {
2037
+ left: elementRect.left - containerRect.left,
2038
+ top: elementRect.top - containerRect.top,
2039
+ width: elementRect.width,
2040
+ height: elementRect.height
2041
+ }
2042
+ };
2043
+ setMeasurement(
2044
+ (prev) => areMeasurementsEqual(prev, nextMeasurement) ? prev : nextMeasurement
2045
+ );
2046
+ }, [componentId, containerRef, svgDivRef]);
2047
+ const scheduleMeasure = useCallback4(() => {
2048
+ if (frameRef.current !== null) return;
2049
+ frameRef.current = window.requestAnimationFrame(measure);
2050
+ }, [measure]);
2051
+ useEffect10(() => {
2052
+ scheduleMeasure();
2053
+ }, [scheduleMeasure, circuitJsonKey]);
2054
+ useEffect10(() => {
2055
+ scheduleMeasure();
2056
+ const svgDiv = svgDivRef.current;
2057
+ const container = containerRef.current;
2058
+ if (!svgDiv || !container) return;
2059
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2060
+ scheduleMeasure();
2061
+ }) : null;
2062
+ resizeObserver?.observe(container);
2063
+ resizeObserver?.observe(svgDiv);
2064
+ const mutationObserver = typeof MutationObserver !== "undefined" ? new MutationObserver(() => {
2065
+ scheduleMeasure();
2066
+ }) : null;
2067
+ mutationObserver?.observe(svgDiv, {
2068
+ attributes: true,
2069
+ attributeFilter: ["style", "transform"],
2070
+ subtree: true,
2071
+ childList: true
2072
+ });
2073
+ window.addEventListener("scroll", scheduleMeasure, true);
2074
+ window.addEventListener("resize", scheduleMeasure);
2075
+ return () => {
2076
+ resizeObserver?.disconnect();
2077
+ mutationObserver?.disconnect();
2078
+ window.removeEventListener("scroll", scheduleMeasure, true);
2079
+ window.removeEventListener("resize", scheduleMeasure);
2080
+ if (frameRef.current !== null) {
2081
+ cancelAnimationFrame(frameRef.current);
2082
+ frameRef.current = null;
2083
+ }
2084
+ };
2085
+ }, [scheduleMeasure, svgDivRef, containerRef]);
2086
+ const handleClick = useCallback4(
2087
+ (event) => {
2088
+ if (onComponentClick) {
2089
+ onComponentClick(componentId, event);
2090
+ }
2091
+ },
2092
+ [componentId, onComponentClick]
2093
+ );
2094
+ const bounds = measurement?.bounds ?? null;
2095
+ const { hovering } = useMouseEventsOverBoundingBox({
2096
+ bounds,
2097
+ onClick: onComponentClick ? handleClick : void 0
2098
+ });
2099
+ if (!measurement || !hovering || !showOutline) {
2100
+ return null;
2101
+ }
2102
+ const rect = measurement.rect;
2103
+ return /* @__PURE__ */ jsx10(
2104
+ "div",
2105
+ {
2106
+ style: {
2107
+ position: "absolute",
2108
+ left: rect.left,
2109
+ top: rect.top,
2110
+ width: rect.width,
2111
+ height: rect.height,
2112
+ border: "1.5px solid rgba(51, 153, 255, 0.9)",
2113
+ borderRadius: "6px",
2114
+ pointerEvents: "none",
2115
+ zIndex: zIndexMap.schematicComponentHoverOutline,
2116
+ boxShadow: "0 0 6px rgba(51, 153, 255, 0.35)"
2117
+ }
2118
+ }
2119
+ );
2120
+ };
2121
+
1761
2122
  // lib/components/SchematicViewer.tsx
1762
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2123
+ import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
1763
2124
  var SchematicViewer = ({
1764
2125
  circuitJson,
1765
2126
  containerStyle,
@@ -1772,13 +2133,14 @@ var SchematicViewer = ({
1772
2133
  clickToInteractEnabled = false,
1773
2134
  colorOverrides,
1774
2135
  spiceSimulationEnabled = false,
1775
- disableGroups = false
2136
+ disableGroups = false,
2137
+ onSchematicComponentClicked
1776
2138
  }) => {
1777
2139
  if (debug3) {
1778
2140
  enableDebug();
1779
2141
  }
1780
- const [showSpiceOverlay, setShowSpiceOverlay] = useState5(false);
1781
- const [spiceSimOptions, setSpiceSimOptions] = useState5({
2142
+ const [showSpiceOverlay, setShowSpiceOverlay] = useState6(false);
2143
+ const [spiceSimOptions, setSpiceSimOptions] = useState6({
1782
2144
  showVoltage: true,
1783
2145
  showCurrent: false,
1784
2146
  startTime: 0,
@@ -1789,11 +2151,11 @@ var SchematicViewer = ({
1789
2151
  const getCircuitHash = (circuitJson2) => {
1790
2152
  return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
1791
2153
  };
1792
- const circuitJsonKey = useMemo3(
2154
+ const circuitJsonKey = useMemo5(
1793
2155
  () => getCircuitHash(circuitJson),
1794
2156
  [circuitJson]
1795
2157
  );
1796
- const spiceString = useMemo3(() => {
2158
+ const spiceString = useMemo5(() => {
1797
2159
  if (!spiceSimulationEnabled) return null;
1798
2160
  try {
1799
2161
  return getSpiceFromCircuitJson(circuitJson, spiceSimOptions);
@@ -1807,24 +2169,36 @@ var SchematicViewer = ({
1807
2169
  spiceSimOptions.startTime,
1808
2170
  spiceSimOptions.duration
1809
2171
  ]);
2172
+ const [hasSpiceSimRun, setHasSpiceSimRun] = useState6(false);
2173
+ useEffect11(() => {
2174
+ setHasSpiceSimRun(false);
2175
+ }, [circuitJsonKey]);
1810
2176
  const {
1811
2177
  plotData,
1812
2178
  nodes,
1813
2179
  isLoading: isSpiceSimLoading,
1814
2180
  error: spiceSimError
1815
- } = useSpiceSimulation(spiceString);
1816
- const [editModeEnabled, setEditModeEnabled] = useState5(defaultEditMode);
1817
- const [snapToGrid, setSnapToGrid] = useState5(true);
1818
- const [isInteractionEnabled, setIsInteractionEnabled] = useState5(
2181
+ } = useSpiceSimulation(hasSpiceSimRun ? spiceString : null);
2182
+ const [editModeEnabled, setEditModeEnabled] = useState6(defaultEditMode);
2183
+ const [snapToGrid, setSnapToGrid] = useState6(true);
2184
+ const [isInteractionEnabled, setIsInteractionEnabled] = useState6(
1819
2185
  !clickToInteractEnabled
1820
2186
  );
1821
- const [showViewMenu, setShowViewMenu] = useState5(false);
1822
- const [showSchematicGroups, setShowSchematicGroups] = useState5(() => {
2187
+ const [showViewMenu, setShowViewMenu] = useState6(false);
2188
+ const [showSchematicGroups, setShowSchematicGroups] = useState6(() => {
1823
2189
  if (disableGroups) return false;
1824
2190
  return getStoredBoolean("schematic_viewer_show_groups", false);
1825
2191
  });
1826
- const svgDivRef = useRef4(null);
1827
- const touchStartRef = useRef4(null);
2192
+ const svgDivRef = useRef7(null);
2193
+ const touchStartRef = useRef7(null);
2194
+ const schematicComponentIds = useMemo5(() => {
2195
+ try {
2196
+ return su6(circuitJson).schematic_component?.list()?.map((component) => component.schematic_component_id) ?? [];
2197
+ } catch (err) {
2198
+ console.error("Failed to derive schematic component ids", err);
2199
+ return [];
2200
+ }
2201
+ }, [circuitJsonKey, circuitJson]);
1828
2202
  const handleTouchStart = (e) => {
1829
2203
  const touch = e.touches[0];
1830
2204
  touchStartRef.current = {
@@ -1844,9 +2218,9 @@ var SchematicViewer = ({
1844
2218
  }
1845
2219
  touchStartRef.current = null;
1846
2220
  };
1847
- const [internalEditEvents, setInternalEditEvents] = useState5([]);
1848
- const circuitJsonRef = useRef4(circuitJson);
1849
- useEffect8(() => {
2221
+ const [internalEditEvents, setInternalEditEvents] = useState6([]);
2222
+ const circuitJsonRef = useRef7(circuitJson);
2223
+ useEffect11(() => {
1850
2224
  const circuitHash = getCircuitHash(circuitJson);
1851
2225
  const circuitHashRef = getCircuitHash(circuitJsonRef.current);
1852
2226
  if (circuitHash !== circuitHashRef) {
@@ -1867,7 +2241,7 @@ var SchematicViewer = ({
1867
2241
  enabled: isInteractionEnabled && !showSpiceOverlay
1868
2242
  });
1869
2243
  const { containerWidth, containerHeight } = useResizeHandling(containerRef);
1870
- const svgString = useMemo3(() => {
2244
+ const svgString = useMemo5(() => {
1871
2245
  if (!containerWidth || !containerHeight) return "";
1872
2246
  return convertCircuitJsonToSchematicSvg(circuitJson, {
1873
2247
  width: containerWidth,
@@ -1879,13 +2253,13 @@ var SchematicViewer = ({
1879
2253
  colorOverrides
1880
2254
  });
1881
2255
  }, [circuitJsonKey, containerWidth, containerHeight]);
1882
- const containerBackgroundColor = useMemo3(() => {
2256
+ const containerBackgroundColor = useMemo5(() => {
1883
2257
  const match = svgString.match(
1884
2258
  /<svg[^>]*style="[^"]*background-color:\s*([^;\"]+)/i
1885
2259
  );
1886
2260
  return match?.[1] ?? "transparent";
1887
2261
  }, [svgString]);
1888
- const realToSvgProjection = useMemo3(() => {
2262
+ const realToSvgProjection = useMemo5(() => {
1889
2263
  if (!svgString) return identity();
1890
2264
  const transformString = svgString.match(
1891
2265
  /data-real-to-screen-transform="([^"]+)"/
@@ -1903,7 +2277,7 @@ var SchematicViewer = ({
1903
2277
  onEditEvent(event);
1904
2278
  }
1905
2279
  };
1906
- const editEventsWithUnappliedEditEvents = useMemo3(() => {
2280
+ const editEventsWithUnappliedEditEvents = useMemo5(() => {
1907
2281
  return [...unappliedEditEvents, ...internalEditEvents];
1908
2282
  }, [unappliedEditEvents, internalEditEvents]);
1909
2283
  const {
@@ -1940,12 +2314,12 @@ var SchematicViewer = ({
1940
2314
  circuitJsonKey,
1941
2315
  showGroups: showSchematicGroups && !disableGroups
1942
2316
  });
1943
- const handleComponentTouchStartRef = useRef4(handleComponentTouchStart);
1944
- useEffect8(() => {
2317
+ const handleComponentTouchStartRef = useRef7(handleComponentTouchStart);
2318
+ useEffect11(() => {
1945
2319
  handleComponentTouchStartRef.current = handleComponentTouchStart;
1946
2320
  }, [handleComponentTouchStart]);
1947
- const svgDiv = useMemo3(
1948
- () => /* @__PURE__ */ jsx9(
2321
+ const svgDiv = useMemo5(
2322
+ () => /* @__PURE__ */ jsx11(
1949
2323
  "div",
1950
2324
  {
1951
2325
  ref: svgDivRef,
@@ -1969,7 +2343,7 @@ var SchematicViewer = ({
1969
2343
  showSpiceOverlay
1970
2344
  ]
1971
2345
  );
1972
- return /* @__PURE__ */ jsxs6(
2346
+ return /* @__PURE__ */ jsx11(MouseTracker, { children: /* @__PURE__ */ jsxs6(
1973
2347
  "div",
1974
2348
  {
1975
2349
  ref: containerRef,
@@ -2010,7 +2384,7 @@ var SchematicViewer = ({
2010
2384
  handleTouchEnd(e);
2011
2385
  },
2012
2386
  children: [
2013
- !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx9(
2387
+ !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx11(
2014
2388
  "div",
2015
2389
  {
2016
2390
  onClick: (e) => {
@@ -2029,7 +2403,7 @@ var SchematicViewer = ({
2029
2403
  pointerEvents: "all",
2030
2404
  touchAction: "pan-x pan-y pinch-zoom"
2031
2405
  },
2032
- children: /* @__PURE__ */ jsx9(
2406
+ children: /* @__PURE__ */ jsx11(
2033
2407
  "div",
2034
2408
  {
2035
2409
  style: {
@@ -2046,28 +2420,28 @@ var SchematicViewer = ({
2046
2420
  )
2047
2421
  }
2048
2422
  ),
2049
- /* @__PURE__ */ jsx9(
2423
+ /* @__PURE__ */ jsx11(
2050
2424
  ViewMenuIcon,
2051
2425
  {
2052
2426
  active: showViewMenu,
2053
2427
  onClick: () => setShowViewMenu(!showViewMenu)
2054
2428
  }
2055
2429
  ),
2056
- editingEnabled && /* @__PURE__ */ jsx9(
2430
+ editingEnabled && /* @__PURE__ */ jsx11(
2057
2431
  EditIcon,
2058
2432
  {
2059
2433
  active: editModeEnabled,
2060
2434
  onClick: () => setEditModeEnabled(!editModeEnabled)
2061
2435
  }
2062
2436
  ),
2063
- editingEnabled && editModeEnabled && /* @__PURE__ */ jsx9(
2437
+ editingEnabled && editModeEnabled && /* @__PURE__ */ jsx11(
2064
2438
  GridIcon,
2065
2439
  {
2066
2440
  active: snapToGrid,
2067
2441
  onClick: () => setSnapToGrid(!snapToGrid)
2068
2442
  }
2069
2443
  ),
2070
- /* @__PURE__ */ jsx9(
2444
+ /* @__PURE__ */ jsx11(
2071
2445
  ViewMenu,
2072
2446
  {
2073
2447
  circuitJson,
@@ -2083,8 +2457,8 @@ var SchematicViewer = ({
2083
2457
  }
2084
2458
  }
2085
2459
  ),
2086
- spiceSimulationEnabled && /* @__PURE__ */ jsx9(SpiceSimulationIcon, { onClick: () => setShowSpiceOverlay(true) }),
2087
- showSpiceOverlay && /* @__PURE__ */ jsx9(
2460
+ spiceSimulationEnabled && /* @__PURE__ */ jsx11(SpiceSimulationIcon, { onClick: () => setShowSpiceOverlay(true) }),
2461
+ showSpiceOverlay && /* @__PURE__ */ jsx11(
2088
2462
  SpiceSimulationOverlay,
2089
2463
  {
2090
2464
  spiceString,
@@ -2094,15 +2468,38 @@ var SchematicViewer = ({
2094
2468
  isLoading: isSpiceSimLoading,
2095
2469
  error: spiceSimError,
2096
2470
  simOptions: spiceSimOptions,
2097
- onSimOptionsChange: setSpiceSimOptions
2471
+ onSimOptionsChange: (options) => {
2472
+ setHasSpiceSimRun(true);
2473
+ setSpiceSimOptions(options);
2474
+ },
2475
+ hasRun: hasSpiceSimRun
2098
2476
  }
2099
2477
  ),
2478
+ onSchematicComponentClicked && schematicComponentIds.map((componentId) => /* @__PURE__ */ jsx11(
2479
+ SchematicComponentMouseTarget,
2480
+ {
2481
+ componentId,
2482
+ svgDivRef,
2483
+ containerRef,
2484
+ showOutline: true,
2485
+ circuitJsonKey,
2486
+ onComponentClick: (id, event) => {
2487
+ onSchematicComponentClicked?.({
2488
+ schematicComponentId: id,
2489
+ event
2490
+ });
2491
+ }
2492
+ },
2493
+ componentId
2494
+ )),
2100
2495
  svgDiv
2101
2496
  ]
2102
2497
  }
2103
- );
2498
+ ) });
2104
2499
  };
2105
2500
  export {
2106
- SchematicViewer
2501
+ MouseTracker,
2502
+ SchematicViewer,
2503
+ useMouseEventsOverBoundingBox
2107
2504
  };
2108
2505
  //# sourceMappingURL=index.js.map