diff --git a/lib/components/SchematicViewer.tsx b/lib/components/SchematicViewer.tsx index 3697732..a2a5813 100644 --- a/lib/components/SchematicViewer.tsx +++ b/lib/components/SchematicViewer.tsx @@ -4,6 +4,7 @@ import { } from "circuit-to-svg" import { su } from "@tscircuit/soup-util" import { useChangeSchematicComponentLocationsInSvg } from "lib/hooks/useChangeSchematicComponentLocationsInSvg" +import { useHighlightConnectedSchematicTraces } from "lib/hooks/useHighlightConnectedSchematicTraces" import { useChangeSchematicTracesForMovedComponents } from "lib/hooks/useChangeSchematicTracesForMovedComponents" import { useSchematicGroupsOverlay } from "lib/hooks/useSchematicGroupsOverlay" import { enableDebug } from "lib/utils/debug" @@ -351,6 +352,13 @@ export const SchematicViewer = ({ editEvents: editEventsWithUnappliedEditEvents, }) + useHighlightConnectedSchematicTraces({ + svgDivRef, + circuitJson, + circuitJsonKey, + svgString, + }) + // Add group overlays when enabled useSchematicGroupsOverlay({ svgDivRef, diff --git a/lib/hooks/useHighlightConnectedSchematicTraces.ts b/lib/hooks/useHighlightConnectedSchematicTraces.ts new file mode 100644 index 0000000..b5fea23 --- /dev/null +++ b/lib/hooks/useHighlightConnectedSchematicTraces.ts @@ -0,0 +1,85 @@ +import { useEffect } from "react" +import { su } from "@tscircuit/soup-util" +import type { CircuitJson } from "circuit-json" + +interface UseHighlightConnectedSchematicTracesOptions { + svgDivRef: React.RefObject + circuitJson: CircuitJson + circuitJsonKey: string + svgString: string +} + +export const useHighlightConnectedSchematicTraces = ({ + svgDivRef, + circuitJson, + circuitJsonKey, + svgString, +}: UseHighlightConnectedSchematicTracesOptions) => { + useEffect(() => { + const svgDiv = svgDivRef.current + const svg = svgDiv?.querySelector("svg") + if (!svg) return + + const schematicTraces = su(circuitJson).schematic_trace?.list() ?? [] + const netKeyToTraceIds = new Map>() + + for (const trace of schematicTraces) { + const traceId = trace.schematic_trace_id + const netKey = (trace as any).subcircuit_connectivity_map_key + if (!traceId || !netKey) continue + + if (!netKeyToTraceIds.has(netKey)) { + netKeyToTraceIds.set(netKey, new Set()) + } + netKeyToTraceIds.get(netKey)?.add(traceId) + } + + if (netKeyToTraceIds.size === 0) return + + const appliedClasses: Array<{ + traceGroup: SVGGElement + className: string + }> = [] + const cssRules: string[] = [] + + Array.from(netKeyToTraceIds.entries()).forEach(([, traceIds], netIndex) => { + const className = `schematic-trace-net-${netIndex}` + + for (const traceId of traceIds) { + const traceGroups = svg.querySelectorAll( + `[data-schematic-trace-id="${traceId}"]`, + ) + for (const traceGroup of Array.from(traceGroups)) { + traceGroup.classList.add(className) + appliedClasses.push({ traceGroup, className }) + } + } + + cssRules.push(` + .${className}:hover, + svg:has(.${className}:hover) .${className} { + filter: invert(1); + } + + .${className}:hover .trace-crossing-outline, + svg:has(.${className}:hover) .${className} .trace-crossing-outline { + opacity: 0; + } + `) + }) + + const style = document.createElementNS( + "http://www.w3.org/2000/svg", + "style", + ) + style.textContent = cssRules.join("\n") + svg.prepend(style) + + return () => { + for (const { traceGroup, className } of appliedClasses) { + traceGroup.classList.remove(className) + } + style.remove() + } + }, [svgDivRef, circuitJson, circuitJsonKey, svgString]) +}