@tscircuit/cli 0.1.1289 → 0.1.1291

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/cli/main.js CHANGED
@@ -60611,6 +60611,616 @@ var init_dist_VYXUNJDT = __esm(() => {
60611
60611
  ];
60612
60612
  });
60613
60613
 
60614
+ // node_modules/circuit-json-trace-length-analysis/lib/analyze-circuit-json-trace-length.ts
60615
+ class Trace {
60616
+ id;
60617
+ label;
60618
+ connectionType;
60619
+ connectionTarget;
60620
+ connectionTargetPosition;
60621
+ connectedPins;
60622
+ pinPositions;
60623
+ requirements;
60624
+ points;
60625
+ lengthMm;
60626
+ straightLineDistanceMm;
60627
+ sourceTraceId;
60628
+ displayName;
60629
+ constructor(model) {
60630
+ this.id = model.id;
60631
+ this.label = model.label;
60632
+ this.connectionType = model.connectionType;
60633
+ this.connectionTarget = model.connectionTarget;
60634
+ this.connectionTargetPosition = model.connectionTargetPosition;
60635
+ this.connectedPins = model.connectedPins;
60636
+ this.pinPositions = model.pinPositions;
60637
+ this.requirements = model.requirements;
60638
+ this.points = model.points;
60639
+ this.lengthMm = model.lengthMm;
60640
+ this.straightLineDistanceMm = model.straightLineDistanceMm;
60641
+ this.sourceTraceId = model.sourceTraceId;
60642
+ this.displayName = model.displayName;
60643
+ }
60644
+ toString() {
60645
+ const lines = [
60646
+ `<Trace id="${escapeXml(this.id)}" label="${escapeXml(this.label)}" connectionType="${escapeXml(this.connectionType)}" lengthMm="${formatNumber3(this.lengthMm)}" straightLineDistanceMm="${formatNumber3(this.straightLineDistanceMm)}">`,
60647
+ ` <ConnectedPins>`,
60648
+ ...this.connectedPins.map((pin) => ` <Pin ref="${escapeXml(pin.ref)}" />`),
60649
+ ` </ConnectedPins>`,
60650
+ ` <PinPositions>`,
60651
+ ...this.pinPositions.map((pin) => renderPinPosition(pin, " ")),
60652
+ ` </PinPositions>`,
60653
+ renderConnection(this),
60654
+ renderRequirements(this.requirements, " "),
60655
+ `</Trace>`
60656
+ ];
60657
+ if (this.points.length > 0) {
60658
+ lines.splice(lines.length - 1, 0, ` <Path>`, ...this.points.map((point5) => ` <Point x="${formatNumber3(point5.x)}" y="${formatNumber3(point5.y)}" layer="${escapeXml(point5.layer)}" kind="${escapeXml(point5.kind)}" />`), ` </Path>`);
60659
+ }
60660
+ return lines.join(`
60661
+ `);
60662
+ }
60663
+ }
60664
+
60665
+ class TraceLengthAnalysis {
60666
+ requestedTarget;
60667
+ resolvedTarget;
60668
+ targetKind;
60669
+ totalLengthMm;
60670
+ totalStraightLineDistanceMm;
60671
+ traceCount;
60672
+ #traces;
60673
+ constructor(args) {
60674
+ this.requestedTarget = args.requestedTarget;
60675
+ this.resolvedTarget = args.resolvedTarget;
60676
+ this.targetKind = args.targetKind;
60677
+ this.#traces = [...args.traces];
60678
+ this.totalLengthMm = this.#traces.reduce((sum, trace) => sum + trace.lengthMm, 0);
60679
+ this.totalStraightLineDistanceMm = this.#traces.reduce((sum, trace) => sum + trace.straightLineDistanceMm, 0);
60680
+ this.traceCount = this.#traces.length;
60681
+ }
60682
+ listTraces() {
60683
+ return [...this.#traces];
60684
+ }
60685
+ toString() {
60686
+ const lines = [
60687
+ `<TraceLengthAnalysis requestedTarget="${escapeXml(this.requestedTarget)}" resolvedTarget="${escapeXml(this.resolvedTarget)}" targetKind="${escapeXml(this.targetKind)}" traceCount="${this.traceCount}" totalLengthMm="${formatNumber3(this.totalLengthMm)}" totalStraightLineDistanceMm="${formatNumber3(this.totalStraightLineDistanceMm)}">`,
60688
+ ...this.#traces.flatMap((trace) => trace.toString().split(`
60689
+ `).map((line2) => ` ${line2}`)),
60690
+ `</TraceLengthAnalysis>`
60691
+ ];
60692
+ return lines.join(`
60693
+ `);
60694
+ }
60695
+ }
60696
+ function analyzeCircuitJsonTraceLength(circuitJson, options) {
60697
+ const index = new CircuitJsonIndex(circuitJson);
60698
+ const target = resolveTarget(index, options.targetPinOrNet);
60699
+ const traces = target.kind === "pin" ? collectPinTraces(index, target) : collectNetTraces(index, target);
60700
+ return new TraceLengthAnalysis({
60701
+ requestedTarget: target.requestedTarget,
60702
+ resolvedTarget: target.resolvedTarget,
60703
+ targetKind: target.kind,
60704
+ traces
60705
+ });
60706
+ }
60707
+
60708
+ class CircuitJsonIndex {
60709
+ componentsById = new Map;
60710
+ componentsByName = new Map;
60711
+ portsById = new Map;
60712
+ portsByComponentId = new Map;
60713
+ netsById = new Map;
60714
+ netsByName = new Map;
60715
+ pcbPortsBySourcePortId = new Map;
60716
+ pcbTracesBySourceTraceId = new Map;
60717
+ pcbTracesByConnectionName = new Map;
60718
+ tracesById = new Map;
60719
+ tracesByPortId = new Map;
60720
+ tracesByNetId = new Map;
60721
+ constructor(circuitJson) {
60722
+ for (const item of circuitJson) {
60723
+ switch (item.type) {
60724
+ case "source_component":
60725
+ this.addComponent(item);
60726
+ break;
60727
+ case "source_port":
60728
+ this.addPort(item);
60729
+ break;
60730
+ case "source_net":
60731
+ this.addNet(item);
60732
+ break;
60733
+ case "pcb_port":
60734
+ this.addPcbPort(item);
60735
+ break;
60736
+ case "pcb_trace":
60737
+ this.addPcbTrace(item);
60738
+ break;
60739
+ case "source_trace":
60740
+ this.addTrace(item);
60741
+ break;
60742
+ default:
60743
+ break;
60744
+ }
60745
+ }
60746
+ }
60747
+ getComponentByName(name) {
60748
+ return this.lookupWithCaseFallback(this.componentsByName, name);
60749
+ }
60750
+ getNetByName(name) {
60751
+ return this.lookupWithCaseFallback(this.netsByName, name);
60752
+ }
60753
+ getPcbPort(sourcePortId) {
60754
+ return this.pcbPortsBySourcePortId.get(sourcePortId)?.[0];
60755
+ }
60756
+ getPortPosition(sourcePortId) {
60757
+ const pcbPort = this.getPcbPort(sourcePortId);
60758
+ if (!pcbPort || typeof pcbPort.x !== "number" || typeof pcbPort.y !== "number") {
60759
+ return null;
60760
+ }
60761
+ const layers = normalizeLayers(pcbPort.layers);
60762
+ return {
60763
+ x: pcbPort.x,
60764
+ y: pcbPort.y,
60765
+ layers
60766
+ };
60767
+ }
60768
+ getPortReference(sourcePortId) {
60769
+ const port = this.portsById.get(sourcePortId);
60770
+ if (!port) {
60771
+ return sourcePortId;
60772
+ }
60773
+ const component = this.componentsById.get(port.source_component_id);
60774
+ const componentName = component?.name ?? port.source_component_id;
60775
+ const portName = port.name ?? String(port.pin_number ?? port.source_port_id);
60776
+ return `${componentName}.${portName}`;
60777
+ }
60778
+ getPcbPathPoints(sourceTraceId) {
60779
+ const candidates = dedupePcbTraces([
60780
+ ...this.pcbTracesBySourceTraceId.get(sourceTraceId) ?? [],
60781
+ ...this.pcbTracesByConnectionName.get(sourceTraceId) ?? []
60782
+ ]);
60783
+ const pcbTraceIds = new Set(candidates.map((trace) => trace.pcb_trace_id));
60784
+ if (candidates.length === 0 || pcbTraceIds.size > 1) {
60785
+ return [];
60786
+ }
60787
+ const routePoints = candidates.flatMap((trace) => normalizePcbTraceRoute(trace));
60788
+ return convertPcbTraceRouteToPath(routePoints);
60789
+ }
60790
+ getConnectedPinsForNet(sourceNetId) {
60791
+ const traces = this.tracesByNetId.get(sourceNetId) ?? [];
60792
+ const sourcePortIds = new Set;
60793
+ for (const trace of traces) {
60794
+ for (const sourcePortId of trace.connected_source_port_ids ?? []) {
60795
+ sourcePortIds.add(sourcePortId);
60796
+ }
60797
+ }
60798
+ return [...sourcePortIds].map((sourcePortId) => this.toConnectedPin(sourcePortId));
60799
+ }
60800
+ toConnectedPin(sourcePortId) {
60801
+ const position2 = this.getPortPosition(sourcePortId);
60802
+ return {
60803
+ ref: this.getPortReference(sourcePortId),
60804
+ x: position2?.x ?? null,
60805
+ y: position2?.y ?? null,
60806
+ layers: position2?.layers ?? []
60807
+ };
60808
+ }
60809
+ addComponent(component) {
60810
+ this.componentsById.set(component.source_component_id, component);
60811
+ const name = component.name;
60812
+ if (!name) {
60813
+ return;
60814
+ }
60815
+ this.addLookup(this.componentsByName, name, component);
60816
+ }
60817
+ addPort(port) {
60818
+ this.portsById.set(port.source_port_id, port);
60819
+ this.addLookup(this.portsByComponentId, port.source_component_id, port);
60820
+ }
60821
+ addNet(net) {
60822
+ this.netsById.set(net.source_net_id, net);
60823
+ const name = net.name;
60824
+ if (!name) {
60825
+ return;
60826
+ }
60827
+ this.addLookup(this.netsByName, name, net);
60828
+ }
60829
+ addPcbPort(pcbPort) {
60830
+ this.addLookup(this.pcbPortsBySourcePortId, pcbPort.source_port_id, pcbPort);
60831
+ }
60832
+ addPcbTrace(pcbTrace) {
60833
+ if (pcbTrace.source_trace_id) {
60834
+ this.addLookup(this.pcbTracesBySourceTraceId, pcbTrace.source_trace_id, pcbTrace);
60835
+ }
60836
+ if (pcbTrace.connection_name) {
60837
+ this.addLookup(this.pcbTracesByConnectionName, pcbTrace.connection_name, pcbTrace);
60838
+ }
60839
+ }
60840
+ addTrace(trace) {
60841
+ this.tracesById.set(trace.source_trace_id, trace);
60842
+ for (const sourcePortId of trace.connected_source_port_ids ?? []) {
60843
+ this.addLookup(this.tracesByPortId, sourcePortId, trace);
60844
+ }
60845
+ for (const sourceNetId of trace.connected_source_net_ids ?? []) {
60846
+ this.addLookup(this.tracesByNetId, sourceNetId, trace);
60847
+ }
60848
+ }
60849
+ addLookup(map, key, value) {
60850
+ const existing = map.get(key) ?? [];
60851
+ existing.push(value);
60852
+ map.set(key, existing);
60853
+ }
60854
+ lookupWithCaseFallback(map, key) {
60855
+ const exactMatch = map.get(key);
60856
+ if (exactMatch?.length) {
60857
+ return exactMatch;
60858
+ }
60859
+ const loweredKey = key.toLowerCase();
60860
+ const fuzzyMatches = [];
60861
+ for (const [candidateKey, values] of map.entries()) {
60862
+ if (candidateKey.toLowerCase() === loweredKey) {
60863
+ fuzzyMatches.push(...values);
60864
+ }
60865
+ }
60866
+ return fuzzyMatches;
60867
+ }
60868
+ }
60869
+ function resolveTarget(index, targetPinOrNet) {
60870
+ if (targetPinOrNet.includes(".")) {
60871
+ if (targetPinOrNet.startsWith("net.")) {
60872
+ const netName = targetPinOrNet.slice("net.".length);
60873
+ const net = resolveNetByName(index, netName);
60874
+ return {
60875
+ kind: "net",
60876
+ requestedTarget: targetPinOrNet,
60877
+ resolvedTarget: `net.${net.name ?? net.source_net_id}`,
60878
+ net
60879
+ };
60880
+ }
60881
+ return resolvePinTarget(index, targetPinOrNet);
60882
+ }
60883
+ const matchingNet = resolveOptionalNetByName(index, targetPinOrNet);
60884
+ if (matchingNet) {
60885
+ const resolvedTarget = `net.${matchingNet.name ?? matchingNet.source_net_id}`;
60886
+ console.log(`inferring ${resolvedTarget}`);
60887
+ return {
60888
+ kind: "net",
60889
+ requestedTarget: targetPinOrNet,
60890
+ resolvedTarget,
60891
+ net: matchingNet
60892
+ };
60893
+ }
60894
+ const matchingPin = resolveOptionalPinByLabel(index, targetPinOrNet);
60895
+ if (matchingPin) {
60896
+ const resolvedTarget = index.getPortReference(matchingPin.source_port_id);
60897
+ console.log(`inferring ${resolvedTarget}`);
60898
+ return {
60899
+ kind: "pin",
60900
+ requestedTarget: targetPinOrNet,
60901
+ resolvedTarget,
60902
+ port: matchingPin
60903
+ };
60904
+ }
60905
+ throw new Error(`Unable to resolve target "${targetPinOrNet}" to a pin or net`);
60906
+ }
60907
+ function resolvePinTarget(index, targetPinOrNet) {
60908
+ const [componentName, ...pinParts] = targetPinOrNet.split(".");
60909
+ const pinLabelOrNumber = pinParts.join(".");
60910
+ if (!componentName || !pinLabelOrNumber) {
60911
+ throw new Error(`Invalid pin target "${targetPinOrNet}"`);
60912
+ }
60913
+ const components = index.getComponentByName(componentName);
60914
+ if (components.length !== 1) {
60915
+ throw new Error(components.length === 0 ? `Unable to find component "${componentName}"` : `Component "${componentName}" is ambiguous`);
60916
+ }
60917
+ const component = components[0];
60918
+ if (!component) {
60919
+ throw new Error(`Unable to find component "${componentName}"`);
60920
+ }
60921
+ const ports = index.portsByComponentId.get(component.source_component_id) ?? [];
60922
+ const matchingPorts = ports.filter((port2) => portMatchesLabel(port2, pinLabelOrNumber));
60923
+ if (matchingPorts.length !== 1) {
60924
+ throw new Error(matchingPorts.length === 0 ? `Unable to find pin "${pinLabelOrNumber}" on ${componentName}` : `Pin "${pinLabelOrNumber}" on ${componentName} is ambiguous`);
60925
+ }
60926
+ const port = matchingPorts[0];
60927
+ if (!port) {
60928
+ throw new Error(`Unable to find pin "${pinLabelOrNumber}" on ${componentName}`);
60929
+ }
60930
+ return {
60931
+ kind: "pin",
60932
+ requestedTarget: targetPinOrNet,
60933
+ resolvedTarget: index.getPortReference(port.source_port_id),
60934
+ port
60935
+ };
60936
+ }
60937
+ function resolveOptionalPinByLabel(index, pinLabelOrNumber) {
60938
+ const matchingPorts = [...index.portsById.values()].filter((port) => portMatchesLabel(port, pinLabelOrNumber));
60939
+ if (matchingPorts.length !== 1) {
60940
+ return null;
60941
+ }
60942
+ return matchingPorts[0] ?? null;
60943
+ }
60944
+ function resolveNetByName(index, netName) {
60945
+ const matchingNets = index.getNetByName(netName);
60946
+ if (matchingNets.length !== 1) {
60947
+ throw new Error(matchingNets.length === 0 ? `Unable to find net "${netName}"` : `Net "${netName}" is ambiguous`);
60948
+ }
60949
+ const net = matchingNets[0];
60950
+ if (!net) {
60951
+ throw new Error(`Unable to find net "${netName}"`);
60952
+ }
60953
+ return net;
60954
+ }
60955
+ function resolveOptionalNetByName(index, netName) {
60956
+ const matchingNets = index.getNetByName(netName);
60957
+ if (matchingNets.length !== 1) {
60958
+ return null;
60959
+ }
60960
+ return matchingNets[0] ?? null;
60961
+ }
60962
+ function collectPinTraces(index, target) {
60963
+ const traces = index.tracesByPortId.get(target.port.source_port_id) ?? [];
60964
+ return traces.flatMap((trace) => {
60965
+ const netIds = trace.connected_source_net_ids ?? [];
60966
+ if (netIds.length > 0) {
60967
+ return netIds.map((sourceNetId) => createNetTraceModel(index, {
60968
+ trace,
60969
+ sourceNetId,
60970
+ focusSourcePortId: target.port.source_port_id
60971
+ }));
60972
+ }
60973
+ return [
60974
+ createDirectTraceModel(index, {
60975
+ trace,
60976
+ focusSourcePortId: target.port.source_port_id
60977
+ })
60978
+ ];
60979
+ }).sort(compareTraceModels).map((model) => new Trace(model));
60980
+ }
60981
+ function collectNetTraces(index, target) {
60982
+ const traces = index.tracesByNetId.get(target.net.source_net_id) ?? [];
60983
+ return traces.flatMap((trace) => {
60984
+ const connectedSourcePortIds = trace.connected_source_port_ids ?? [];
60985
+ if (connectedSourcePortIds.length === 0) {
60986
+ return [];
60987
+ }
60988
+ return connectedSourcePortIds.map((focusSourcePortId) => createNetTraceModel(index, {
60989
+ trace,
60990
+ sourceNetId: target.net.source_net_id,
60991
+ focusSourcePortId
60992
+ }));
60993
+ }).sort(compareTraceModels).map((model) => new Trace(model));
60994
+ }
60995
+ function createDirectTraceModel(index, args) {
60996
+ const connectedSourcePortIds = args.trace.connected_source_port_ids ?? [];
60997
+ if (connectedSourcePortIds.length === 0) {
60998
+ throw new Error(`Trace ${args.trace.source_trace_id} has no connected ports`);
60999
+ }
61000
+ const orderedPortIds = prioritizeFocus(connectedSourcePortIds, args.focusSourcePortId);
61001
+ const firstPortId = orderedPortIds[0];
61002
+ const secondPortId = orderedPortIds[1] ?? orderedPortIds[0];
61003
+ if (!firstPortId || !secondPortId) {
61004
+ throw new Error(`Trace ${args.trace.source_trace_id} is missing connected ports`);
61005
+ }
61006
+ const firstPosition = requirePortPosition(index, firstPortId);
61007
+ const secondPosition = requirePortPosition(index, secondPortId);
61008
+ const secondLayer = primaryLayer(secondPosition.layers);
61009
+ const connectedPins = orderedPortIds.map((sourcePortId) => index.toConnectedPin(sourcePortId));
61010
+ const connectionTarget4 = index.getPortReference(secondPortId);
61011
+ const straightLineDistanceMm = distanceBetween(firstPosition, secondPosition);
61012
+ const points = index.getPcbPathPoints(args.trace.source_trace_id);
61013
+ const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
61014
+ return {
61015
+ id: args.trace.source_trace_id,
61016
+ label: `${index.getPortReference(firstPortId)} -> ${connectionTarget4}`,
61017
+ connectionType: "direct connection",
61018
+ connectionTarget: connectionTarget4,
61019
+ connectionTargetPosition: {
61020
+ x: secondPosition.x,
61021
+ y: secondPosition.y,
61022
+ layer: secondLayer
61023
+ },
61024
+ connectedPins,
61025
+ pinPositions: connectedPins,
61026
+ requirements: {
61027
+ maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
61028
+ },
61029
+ points,
61030
+ lengthMm,
61031
+ straightLineDistanceMm,
61032
+ sourceTraceId: args.trace.source_trace_id,
61033
+ displayName: args.trace.display_name ?? null
61034
+ };
61035
+ }
61036
+ function createNetTraceModel(index, args) {
61037
+ const net = index.netsById.get(args.sourceNetId);
61038
+ if (!net) {
61039
+ throw new Error(`Unable to find net ${args.sourceNetId}`);
61040
+ }
61041
+ const focusPosition = requirePortPosition(index, args.focusSourcePortId);
61042
+ const focusLayer = primaryLayer(focusPosition.layers);
61043
+ const connectedPins = index.getConnectedPinsForNet(args.sourceNetId).sort((a2, b) => a2.ref.localeCompare(b.ref));
61044
+ const hub = inferNetHub(connectedPins, focusLayer);
61045
+ const focusPin = index.toConnectedPin(args.focusSourcePortId);
61046
+ const points = index.getPcbPathPoints(args.trace.source_trace_id);
61047
+ const straightLineDistanceMm = distanceBetween(focusPosition, hub);
61048
+ const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
61049
+ return {
61050
+ id: `${args.trace.source_trace_id}:${args.focusSourcePortId}`,
61051
+ label: `${focusPin.ref} -> net.${net.name ?? net.source_net_id}`,
61052
+ connectionType: "via net",
61053
+ connectionTarget: `net.${net.name ?? net.source_net_id}`,
61054
+ connectionTargetPosition: hub,
61055
+ connectedPins: prioritizePin(focusPin, connectedPins),
61056
+ pinPositions: prioritizePin(focusPin, connectedPins),
61057
+ requirements: {
61058
+ maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
61059
+ },
61060
+ points,
61061
+ lengthMm,
61062
+ straightLineDistanceMm,
61063
+ sourceTraceId: args.trace.source_trace_id,
61064
+ displayName: args.trace.display_name ?? null
61065
+ };
61066
+ }
61067
+ function requirePortPosition(index, sourcePortId) {
61068
+ const position2 = index.getPortPosition(sourcePortId);
61069
+ if (!position2) {
61070
+ throw new Error(`Unable to analyze ${index.getPortReference(sourcePortId)} because it has no pcb_port position`);
61071
+ }
61072
+ return position2;
61073
+ }
61074
+ function inferNetHub(connectedPins, fallbackLayer) {
61075
+ const positionedPins = connectedPins.filter((pin) => typeof pin.x === "number" && typeof pin.y === "number");
61076
+ if (positionedPins.length === 0) {
61077
+ return {
61078
+ x: 0,
61079
+ y: 0,
61080
+ layer: fallbackLayer
61081
+ };
61082
+ }
61083
+ const x3 = positionedPins.reduce((sum, pin) => sum + pin.x, 0) / positionedPins.length;
61084
+ const y4 = positionedPins.reduce((sum, pin) => sum + pin.y, 0) / positionedPins.length;
61085
+ const layerCounts = new Map;
61086
+ for (const pin of positionedPins) {
61087
+ const layer2 = pin.layers[0] ?? fallbackLayer;
61088
+ layerCounts.set(layer2, (layerCounts.get(layer2) ?? 0) + 1);
61089
+ }
61090
+ const sortedLayers = [...layerCounts.entries()].sort((left, right) => {
61091
+ if (right[1] !== left[1]) {
61092
+ return right[1] - left[1];
61093
+ }
61094
+ return left[0].localeCompare(right[0]);
61095
+ });
61096
+ const layer = sortedLayers[0]?.[0] ?? fallbackLayer;
61097
+ return { x: x3, y: y4, layer };
61098
+ }
61099
+ function measurePathLength(points) {
61100
+ let lengthMm = 0;
61101
+ for (let index = 1;index < points.length; index += 1) {
61102
+ const previous = points[index - 1];
61103
+ const current2 = points[index];
61104
+ if (!previous || !current2) {
61105
+ continue;
61106
+ }
61107
+ lengthMm += distanceBetween(previous, current2);
61108
+ }
61109
+ return lengthMm;
61110
+ }
61111
+ function dedupePcbTraces(pcbTraces) {
61112
+ const seen = new Set;
61113
+ const deduped = [];
61114
+ for (const pcbTrace of pcbTraces) {
61115
+ if (seen.has(pcbTrace)) {
61116
+ continue;
61117
+ }
61118
+ seen.add(pcbTrace);
61119
+ deduped.push(pcbTrace);
61120
+ }
61121
+ return deduped;
61122
+ }
61123
+ function normalizePcbTraceRoute(pcbTrace) {
61124
+ if (!Array.isArray(pcbTrace.route)) {
61125
+ return [];
61126
+ }
61127
+ return pcbTrace.route.filter((point5) => typeof point5.x === "number" && typeof point5.y === "number").map((point5) => ({
61128
+ ...point5,
61129
+ layer: typeof point5.layer === "string" && point5.layer.length > 0 ? point5.layer : typeof pcbTrace.layer === "string" && pcbTrace.layer.length > 0 ? pcbTrace.layer : "top"
61130
+ }));
61131
+ }
61132
+ function convertPcbTraceRouteToPath(routePoints) {
61133
+ const deduped = routePoints.filter((point5, index) => {
61134
+ const previous = routePoints[index - 1];
61135
+ return !previous || previous.x !== point5.x || previous.y !== point5.y || previous.layer !== point5.layer;
61136
+ });
61137
+ return deduped.map((point5, index) => ({
61138
+ x: point5.x,
61139
+ y: point5.y,
61140
+ layer: point5.layer,
61141
+ kind: classifyPcbTracePoint(point5, index, deduped.length)
61142
+ }));
61143
+ }
61144
+ function classifyPcbTracePoint(point5, index, pointCount) {
61145
+ if (point5.route_type === "via") {
61146
+ return "via";
61147
+ }
61148
+ if (index === 0 || index === pointCount - 1 || point5.start_pcb_port_id || point5.end_pcb_port_id) {
61149
+ return "endpoint";
61150
+ }
61151
+ return "track";
61152
+ }
61153
+ function renderPinPosition(pin, indent) {
61154
+ if (typeof pin.x !== "number" || typeof pin.y !== "number") {
61155
+ return `${indent}<Pin ref="${escapeXml(pin.ref)}" position="unavailable" />`;
61156
+ }
61157
+ const layers = pin.layers.length > 0 ? pin.layers.join(",") : "unknown";
61158
+ return `${indent}<Pin ref="${escapeXml(pin.ref)}" x="${formatNumber3(pin.x)}" y="${formatNumber3(pin.y)}" layers="${escapeXml(layers)}" />`;
61159
+ }
61160
+ function renderConnection(trace) {
61161
+ if (trace.connectionTargetPosition) {
61162
+ return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" x="${formatNumber3(trace.connectionTargetPosition.x)}" y="${formatNumber3(trace.connectionTargetPosition.y)}" layer="${escapeXml(trace.connectionTargetPosition.layer)}" />`;
61163
+ }
61164
+ return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" />`;
61165
+ }
61166
+ function renderRequirements(requirements, indent) {
61167
+ if (typeof requirements.maxLengthMm === "number") {
61168
+ return [
61169
+ `${indent}<TraceRequirements>`,
61170
+ `${indent} <MaxLengthMm>${formatNumber3(requirements.maxLengthMm)}</MaxLengthMm>`,
61171
+ `${indent}</TraceRequirements>`
61172
+ ].join(`
61173
+ `);
61174
+ }
61175
+ return `${indent}<TraceRequirements none />`;
61176
+ }
61177
+ function portMatchesLabel(port, label) {
61178
+ const loweredLabel = label.toLowerCase();
61179
+ const portName = port.name?.toLowerCase();
61180
+ const pinNumber = String(port.pin_number ?? "").toLowerCase();
61181
+ const portHints2 = (port.port_hints ?? []).map((hint) => hint.toLowerCase());
61182
+ return portName === loweredLabel || pinNumber === loweredLabel || portHints2.includes(loweredLabel);
61183
+ }
61184
+ function prioritizeFocus(portIds, focusSourcePortId) {
61185
+ const remainder = portIds.filter((portId) => portId !== focusSourcePortId);
61186
+ return [focusSourcePortId, ...remainder];
61187
+ }
61188
+ function prioritizePin(focusPin, pins) {
61189
+ const remainder = pins.filter((pin) => pin.ref !== focusPin.ref);
61190
+ return [focusPin, ...remainder];
61191
+ }
61192
+ function compareTraceModels(left, right) {
61193
+ return left.label.localeCompare(right.label);
61194
+ }
61195
+ function normalizeLayers(layers) {
61196
+ if (!Array.isArray(layers)) {
61197
+ return ["top"];
61198
+ }
61199
+ const normalizedLayers = layers.filter((layer) => typeof layer === "string" && layer.length > 0);
61200
+ return normalizedLayers.length > 0 ? normalizedLayers : ["top"];
61201
+ }
61202
+ function primaryLayer(layers) {
61203
+ return layers[0] ?? "top";
61204
+ }
61205
+ function distanceBetween(left, right) {
61206
+ return Math.hypot(right.x - left.x, right.y - left.y);
61207
+ }
61208
+ function formatNumber3(value) {
61209
+ return value.toFixed(2);
61210
+ }
61211
+ function escapeXml(value) {
61212
+ return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
61213
+ }
61214
+
61215
+ // node_modules/circuit-json-trace-length-analysis/lib/index.ts
61216
+ var exports_lib = {};
61217
+ __export2(exports_lib, {
61218
+ analyzeCircuitJsonTraceLength: () => analyzeCircuitJsonTraceLength,
61219
+ TraceLengthAnalysis: () => TraceLengthAnalysis,
61220
+ Trace: () => Trace
61221
+ });
61222
+ var init_lib = () => {};
61223
+
60614
61224
  // node_modules/@edge-runtime/node-utils/dist/index.js
60615
61225
  var require_dist5 = __commonJS((exports2, module2) => {
60616
61226
  var __filename = "/home/runner/work/cli/cli/node_modules/@edge-runtime/node-utils/dist/index.js";
@@ -99964,7 +100574,7 @@ var import_perfect_cli = __toESM2(require_dist2(), 1);
99964
100574
  // lib/getVersion.ts
99965
100575
  import { createRequire as createRequire2 } from "node:module";
99966
100576
  // package.json
99967
- var version = "0.1.1288";
100577
+ var version = "0.1.1290";
99968
100578
  var package_default = {
99969
100579
  name: "@tscircuit/cli",
99970
100580
  version,
@@ -99978,6 +100588,7 @@ var package_default = {
99978
100588
  "@biomejs/biome": "^1.9.4",
99979
100589
  "@tscircuit/circuit-json-placement-analysis": "^0.0.6",
99980
100590
  "@tscircuit/circuit-json-routing-analysis": "^0.0.1",
100591
+ "@tscircuit/circuit-json-schematic-placement-analysis": "github:tscircuit/circuit-json-schematic-placement-analysis#d09c8d74f3085b29744bb0f1c9864c1154c69436",
99981
100592
  "@tscircuit/fake-snippets": "^0.0.182",
99982
100593
  "@tscircuit/file-server": "^0.0.32",
99983
100594
  "@tscircuit/image-utils": "^0.0.3",
@@ -109908,7 +110519,10 @@ var buildPreviewGltf = async ({
109908
110519
  // cli/build/build-preview-images.ts
109909
110520
  import fs30 from "node:fs";
109910
110521
  import path32 from "node:path";
109911
- import { convertCircuitJsonToGltf as convertCircuitJsonToGltf3 } from "circuit-json-to-gltf";
110522
+ import {
110523
+ convertCircuitJsonToGltf as convertCircuitJsonToGltf3,
110524
+ getBestCameraPosition
110525
+ } from "circuit-json-to-gltf";
109912
110526
  import {
109913
110527
  convertCircuitJsonToPcbSvg,
109914
110528
  convertCircuitJsonToSchematicSvg
@@ -110015,7 +110629,7 @@ var generatePreviewAssets = async ({
110015
110629
  const glbBuffer = await convertCircuitJsonToGltf3(circuitJsonWithFileUrls, getCircuitJsonToGltfOptions({ format: "glb" }));
110016
110630
  console.log(`${prefix}Rendering GLB to PNG buffer...`);
110017
110631
  const glbArrayBuffer = await normalizeToArrayBuffer(glbBuffer);
110018
- const pngBuffer = await renderGLTFToPNGBufferFromGLBBuffer(glbArrayBuffer);
110632
+ const pngBuffer = await renderGLTFToPNGBufferFromGLBBuffer(glbArrayBuffer, getBestCameraPosition(circuitJson));
110019
110633
  fs30.writeFileSync(path32.join(outputDir, "3d.png"), Buffer.from(normalizeToUint8Array2(pngBuffer)));
110020
110634
  console.log(`${prefix}Written 3d.png`);
110021
110635
  } catch (error) {
@@ -240617,607 +241231,268 @@ var registerCheckRouting = (program2) => {
240617
241231
  });
240618
241232
  };
240619
241233
 
240620
- // node_modules/circuit-json-trace-length-analysis/lib/analyze-circuit-json-trace-length.ts
240621
- class Trace {
240622
- id;
240623
- label;
240624
- connectionType;
240625
- connectionTarget;
240626
- connectionTargetPosition;
240627
- connectedPins;
240628
- pinPositions;
240629
- requirements;
240630
- points;
240631
- lengthMm;
240632
- straightLineDistanceMm;
240633
- sourceTraceId;
240634
- displayName;
240635
- constructor(model) {
240636
- this.id = model.id;
240637
- this.label = model.label;
240638
- this.connectionType = model.connectionType;
240639
- this.connectionTarget = model.connectionTarget;
240640
- this.connectionTargetPosition = model.connectionTargetPosition;
240641
- this.connectedPins = model.connectedPins;
240642
- this.pinPositions = model.pinPositions;
240643
- this.requirements = model.requirements;
240644
- this.points = model.points;
240645
- this.lengthMm = model.lengthMm;
240646
- this.straightLineDistanceMm = model.straightLineDistanceMm;
240647
- this.sourceTraceId = model.sourceTraceId;
240648
- this.displayName = model.displayName;
240649
- }
240650
- toString() {
240651
- const lines = [
240652
- `<Trace id="${escapeXml(this.id)}" label="${escapeXml(this.label)}" connectionType="${escapeXml(this.connectionType)}" lengthMm="${formatNumber3(this.lengthMm)}" straightLineDistanceMm="${formatNumber3(this.straightLineDistanceMm)}">`,
240653
- ` <ConnectedPins>`,
240654
- ...this.connectedPins.map((pin) => ` <Pin ref="${escapeXml(pin.ref)}" />`),
240655
- ` </ConnectedPins>`,
240656
- ` <PinPositions>`,
240657
- ...this.pinPositions.map((pin) => renderPinPosition(pin, " ")),
240658
- ` </PinPositions>`,
240659
- renderConnection(this),
240660
- renderRequirements(this.requirements, " "),
240661
- `</Trace>`
240662
- ];
240663
- if (this.points.length > 0) {
240664
- lines.splice(lines.length - 1, 0, ` <Path>`, ...this.points.map((point5) => ` <Point x="${formatNumber3(point5.x)}" y="${formatNumber3(point5.y)}" layer="${escapeXml(point5.layer)}" kind="${escapeXml(point5.kind)}" />`), ` </Path>`);
240665
- }
240666
- return lines.join(`
240667
- `);
240668
- }
240669
- }
241234
+ // node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/analyze-schematic-placement.ts
241235
+ import { cju as cju7 } from "@tscircuit/circuit-json-util";
240670
241236
 
240671
- class TraceLengthAnalysis {
240672
- requestedTarget;
240673
- resolvedTarget;
240674
- targetKind;
240675
- totalLengthMm;
240676
- totalStraightLineDistanceMm;
240677
- traceCount;
240678
- #traces;
240679
- constructor(args) {
240680
- this.requestedTarget = args.requestedTarget;
240681
- this.resolvedTarget = args.resolvedTarget;
240682
- this.targetKind = args.targetKind;
240683
- this.#traces = [...args.traces];
240684
- this.totalLengthMm = this.#traces.reduce((sum, trace) => sum + trace.lengthMm, 0);
240685
- this.totalStraightLineDistanceMm = this.#traces.reduce((sum, trace) => sum + trace.straightLineDistanceMm, 0);
240686
- this.traceCount = this.#traces.length;
240687
- }
240688
- listTraces() {
240689
- return [...this.#traces];
240690
- }
240691
- toString() {
240692
- const lines = [
240693
- `<TraceLengthAnalysis requestedTarget="${escapeXml(this.requestedTarget)}" resolvedTarget="${escapeXml(this.resolvedTarget)}" targetKind="${escapeXml(this.targetKind)}" traceCount="${this.traceCount}" totalLengthMm="${formatNumber3(this.totalLengthMm)}" totalStraightLineDistanceMm="${formatNumber3(this.totalStraightLineDistanceMm)}">`,
240694
- ...this.#traces.flatMap((trace) => trace.toString().split(`
240695
- `).map((line2) => ` ${line2}`)),
240696
- `</TraceLengthAnalysis>`
240697
- ];
240698
- return lines.join(`
240699
- `);
240700
- }
240701
- }
240702
- function analyzeCircuitJsonTraceLength(circuitJson, options) {
240703
- const index = new CircuitJsonIndex(circuitJson);
240704
- const target = resolveTarget(index, options.targetPinOrNet);
240705
- const traces = target.kind === "pin" ? collectPinTraces(index, target) : collectNetTraces(index, target);
240706
- return new TraceLengthAnalysis({
240707
- requestedTarget: target.requestedTarget,
240708
- resolvedTarget: target.resolvedTarget,
240709
- targetKind: target.kind,
240710
- traces
240711
- });
240712
- }
240713
-
240714
- class CircuitJsonIndex {
240715
- componentsById = new Map;
240716
- componentsByName = new Map;
240717
- portsById = new Map;
240718
- portsByComponentId = new Map;
240719
- netsById = new Map;
240720
- netsByName = new Map;
240721
- pcbPortsBySourcePortId = new Map;
240722
- pcbTracesBySourceTraceId = new Map;
240723
- pcbTracesByConnectionName = new Map;
240724
- tracesById = new Map;
240725
- tracesByPortId = new Map;
240726
- tracesByNetId = new Map;
240727
- constructor(circuitJson) {
240728
- for (const item of circuitJson) {
240729
- switch (item.type) {
240730
- case "source_component":
240731
- this.addComponent(item);
240732
- break;
240733
- case "source_port":
240734
- this.addPort(item);
240735
- break;
240736
- case "source_net":
240737
- this.addNet(item);
240738
- break;
240739
- case "pcb_port":
240740
- this.addPcbPort(item);
240741
- break;
240742
- case "pcb_trace":
240743
- this.addPcbTrace(item);
240744
- break;
240745
- case "source_trace":
240746
- this.addTrace(item);
240747
- break;
240748
- default:
240749
- break;
240750
- }
240751
- }
240752
- }
240753
- getComponentByName(name) {
240754
- return this.lookupWithCaseFallback(this.componentsByName, name);
240755
- }
240756
- getNetByName(name) {
240757
- return this.lookupWithCaseFallback(this.netsByName, name);
240758
- }
240759
- getPcbPort(sourcePortId) {
240760
- return this.pcbPortsBySourcePortId.get(sourcePortId)?.[0];
240761
- }
240762
- getPortPosition(sourcePortId) {
240763
- const pcbPort = this.getPcbPort(sourcePortId);
240764
- if (!pcbPort || typeof pcbPort.x !== "number" || typeof pcbPort.y !== "number") {
240765
- return null;
240766
- }
240767
- const layers = normalizeLayers(pcbPort.layers);
240768
- return {
240769
- x: pcbPort.x,
240770
- y: pcbPort.y,
240771
- layers
240772
- };
240773
- }
240774
- getPortReference(sourcePortId) {
240775
- const port = this.portsById.get(sourcePortId);
240776
- if (!port) {
240777
- return sourcePortId;
240778
- }
240779
- const component = this.componentsById.get(port.source_component_id);
240780
- const componentName = component?.name ?? port.source_component_id;
240781
- const portName = port.name ?? String(port.pin_number ?? port.source_port_id);
240782
- return `${componentName}.${portName}`;
240783
- }
240784
- getPcbPathPoints(sourceTraceId) {
240785
- const candidates = dedupePcbTraces([
240786
- ...this.pcbTracesBySourceTraceId.get(sourceTraceId) ?? [],
240787
- ...this.pcbTracesByConnectionName.get(sourceTraceId) ?? []
240788
- ]);
240789
- const pcbTraceIds = new Set(candidates.map((trace) => trace.pcb_trace_id));
240790
- if (candidates.length === 0 || pcbTraceIds.size > 1) {
240791
- return [];
240792
- }
240793
- const routePoints = candidates.flatMap((trace) => normalizePcbTraceRoute(trace));
240794
- return convertPcbTraceRouteToPath(routePoints);
240795
- }
240796
- getConnectedPinsForNet(sourceNetId) {
240797
- const traces = this.tracesByNetId.get(sourceNetId) ?? [];
240798
- const sourcePortIds = new Set;
240799
- for (const trace of traces) {
240800
- for (const sourcePortId of trace.connected_source_port_ids ?? []) {
240801
- sourcePortIds.add(sourcePortId);
240802
- }
240803
- }
240804
- return [...sourcePortIds].map((sourcePortId) => this.toConnectedPin(sourcePortId));
240805
- }
240806
- toConnectedPin(sourcePortId) {
240807
- const position2 = this.getPortPosition(sourcePortId);
240808
- return {
240809
- ref: this.getPortReference(sourcePortId),
240810
- x: position2?.x ?? null,
240811
- y: position2?.y ?? null,
240812
- layers: position2?.layers ?? []
240813
- };
240814
- }
240815
- addComponent(component) {
240816
- this.componentsById.set(component.source_component_id, component);
240817
- const name = component.name;
240818
- if (!name) {
240819
- return;
240820
- }
240821
- this.addLookup(this.componentsByName, name, component);
240822
- }
240823
- addPort(port) {
240824
- this.portsById.set(port.source_port_id, port);
240825
- this.addLookup(this.portsByComponentId, port.source_component_id, port);
240826
- }
240827
- addNet(net) {
240828
- this.netsById.set(net.source_net_id, net);
240829
- const name = net.name;
240830
- if (!name) {
240831
- return;
240832
- }
240833
- this.addLookup(this.netsByName, name, net);
240834
- }
240835
- addPcbPort(pcbPort) {
240836
- this.addLookup(this.pcbPortsBySourcePortId, pcbPort.source_port_id, pcbPort);
240837
- }
240838
- addPcbTrace(pcbTrace) {
240839
- if (pcbTrace.source_trace_id) {
240840
- this.addLookup(this.pcbTracesBySourceTraceId, pcbTrace.source_trace_id, pcbTrace);
240841
- }
240842
- if (pcbTrace.connection_name) {
240843
- this.addLookup(this.pcbTracesByConnectionName, pcbTrace.connection_name, pcbTrace);
240844
- }
240845
- }
240846
- addTrace(trace) {
240847
- this.tracesById.set(trace.source_trace_id, trace);
240848
- for (const sourcePortId of trace.connected_source_port_ids ?? []) {
240849
- this.addLookup(this.tracesByPortId, sourcePortId, trace);
240850
- }
240851
- for (const sourceNetId of trace.connected_source_net_ids ?? []) {
240852
- this.addLookup(this.tracesByNetId, sourceNetId, trace);
240853
- }
240854
- }
240855
- addLookup(map, key, value) {
240856
- const existing = map.get(key) ?? [];
240857
- existing.push(value);
240858
- map.set(key, existing);
240859
- }
240860
- lookupWithCaseFallback(map, key) {
240861
- const exactMatch = map.get(key);
240862
- if (exactMatch?.length) {
240863
- return exactMatch;
240864
- }
240865
- const loweredKey = key.toLowerCase();
240866
- const fuzzyMatches = [];
240867
- for (const [candidateKey, values] of map.entries()) {
240868
- if (candidateKey.toLowerCase() === loweredKey) {
240869
- fuzzyMatches.push(...values);
240870
- }
240871
- }
240872
- return fuzzyMatches;
240873
- }
240874
- }
240875
- function resolveTarget(index, targetPinOrNet) {
240876
- if (targetPinOrNet.includes(".")) {
240877
- if (targetPinOrNet.startsWith("net.")) {
240878
- const netName = targetPinOrNet.slice("net.".length);
240879
- const net = resolveNetByName(index, netName);
240880
- return {
240881
- kind: "net",
240882
- requestedTarget: targetPinOrNet,
240883
- resolvedTarget: `net.${net.name ?? net.source_net_id}`,
240884
- net
240885
- };
240886
- }
240887
- return resolvePinTarget(index, targetPinOrNet);
240888
- }
240889
- const matchingNet = resolveOptionalNetByName(index, targetPinOrNet);
240890
- if (matchingNet) {
240891
- const resolvedTarget = `net.${matchingNet.name ?? matchingNet.source_net_id}`;
240892
- console.log(`inferring ${resolvedTarget}`);
240893
- return {
240894
- kind: "net",
240895
- requestedTarget: targetPinOrNet,
240896
- resolvedTarget,
240897
- net: matchingNet
240898
- };
240899
- }
240900
- const matchingPin = resolveOptionalPinByLabel(index, targetPinOrNet);
240901
- if (matchingPin) {
240902
- const resolvedTarget = index.getPortReference(matchingPin.source_port_id);
240903
- console.log(`inferring ${resolvedTarget}`);
240904
- return {
240905
- kind: "pin",
240906
- requestedTarget: targetPinOrNet,
240907
- resolvedTarget,
240908
- port: matchingPin
240909
- };
240910
- }
240911
- throw new Error(`Unable to resolve target "${targetPinOrNet}" to a pin or net`);
240912
- }
240913
- function resolvePinTarget(index, targetPinOrNet) {
240914
- const [componentName, ...pinParts] = targetPinOrNet.split(".");
240915
- const pinLabelOrNumber = pinParts.join(".");
240916
- if (!componentName || !pinLabelOrNumber) {
240917
- throw new Error(`Invalid pin target "${targetPinOrNet}"`);
240918
- }
240919
- const components = index.getComponentByName(componentName);
240920
- if (components.length !== 1) {
240921
- throw new Error(components.length === 0 ? `Unable to find component "${componentName}"` : `Component "${componentName}" is ambiguous`);
240922
- }
240923
- const component = components[0];
240924
- if (!component) {
240925
- throw new Error(`Unable to find component "${componentName}"`);
240926
- }
240927
- const ports = index.portsByComponentId.get(component.source_component_id) ?? [];
240928
- const matchingPorts = ports.filter((port2) => portMatchesLabel(port2, pinLabelOrNumber));
240929
- if (matchingPorts.length !== 1) {
240930
- throw new Error(matchingPorts.length === 0 ? `Unable to find pin "${pinLabelOrNumber}" on ${componentName}` : `Pin "${pinLabelOrNumber}" on ${componentName} is ambiguous`);
240931
- }
240932
- const port = matchingPorts[0];
240933
- if (!port) {
240934
- throw new Error(`Unable to find pin "${pinLabelOrNumber}" on ${componentName}`);
240935
- }
241237
+ // node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/schematic-box-overlap.ts
241238
+ var getCenteredRectBounds = (box2) => {
241239
+ const halfWidth = box2.width / 2;
241240
+ const halfHeight = box2.height / 2;
240936
241241
  return {
240937
- kind: "pin",
240938
- requestedTarget: targetPinOrNet,
240939
- resolvedTarget: index.getPortReference(port.source_port_id),
240940
- port
240941
- };
240942
- }
240943
- function resolveOptionalPinByLabel(index, pinLabelOrNumber) {
240944
- const matchingPorts = [...index.portsById.values()].filter((port) => portMatchesLabel(port, pinLabelOrNumber));
240945
- if (matchingPorts.length !== 1) {
241242
+ left: box2.schX - halfWidth,
241243
+ right: box2.schX + halfWidth,
241244
+ top: box2.schY - halfHeight,
241245
+ bottom: box2.schY + halfHeight
241246
+ };
241247
+ };
241248
+ var getComponentOverlap = (firstComponent, secondComponent) => {
241249
+ const firstBounds = getCenteredRectBounds(firstComponent);
241250
+ const secondBounds = getCenteredRectBounds(secondComponent);
241251
+ const left = Math.max(firstBounds.left, secondBounds.left);
241252
+ const right = Math.min(firstBounds.right, secondBounds.right);
241253
+ const top = Math.max(firstBounds.top, secondBounds.top);
241254
+ const bottom = Math.min(firstBounds.bottom, secondBounds.bottom);
241255
+ const overlapWidth = right - left;
241256
+ const overlapHeight = bottom - top;
241257
+ if (overlapWidth <= 0 || overlapHeight <= 0) {
240946
241258
  return null;
240947
241259
  }
240948
- return matchingPorts[0] ?? null;
240949
- }
240950
- function resolveNetByName(index, netName) {
240951
- const matchingNets = index.getNetByName(netName);
240952
- if (matchingNets.length !== 1) {
240953
- throw new Error(matchingNets.length === 0 ? `Unable to find net "${netName}"` : `Net "${netName}" is ambiguous`);
240954
- }
240955
- const net = matchingNets[0];
240956
- if (!net) {
240957
- throw new Error(`Unable to find net "${netName}"`);
240958
- }
240959
- return net;
240960
- }
240961
- function resolveOptionalNetByName(index, netName) {
240962
- const matchingNets = index.getNetByName(netName);
240963
- if (matchingNets.length !== 1) {
240964
- return null;
240965
- }
240966
- return matchingNets[0] ?? null;
240967
- }
240968
- function collectPinTraces(index, target) {
240969
- const traces = index.tracesByPortId.get(target.port.source_port_id) ?? [];
240970
- return traces.flatMap((trace) => {
240971
- const netIds = trace.connected_source_net_ids ?? [];
240972
- if (netIds.length > 0) {
240973
- return netIds.map((sourceNetId) => createNetTraceModel(index, {
240974
- trace,
240975
- sourceNetId,
240976
- focusSourcePortId: target.port.source_port_id
240977
- }));
240978
- }
240979
- return [
240980
- createDirectTraceModel(index, {
240981
- trace,
240982
- focusSourcePortId: target.port.source_port_id
240983
- })
240984
- ];
240985
- }).sort(compareTraceModels).map((model) => new Trace(model));
240986
- }
240987
- function collectNetTraces(index, target) {
240988
- const traces = index.tracesByNetId.get(target.net.source_net_id) ?? [];
240989
- return traces.flatMap((trace) => {
240990
- const connectedSourcePortIds = trace.connected_source_port_ids ?? [];
240991
- if (connectedSourcePortIds.length === 0) {
240992
- return [];
240993
- }
240994
- return connectedSourcePortIds.map((focusSourcePortId) => createNetTraceModel(index, {
240995
- trace,
240996
- sourceNetId: target.net.source_net_id,
240997
- focusSourcePortId
240998
- }));
240999
- }).sort(compareTraceModels).map((model) => new Trace(model));
241000
- }
241001
- function createDirectTraceModel(index, args) {
241002
- const connectedSourcePortIds = args.trace.connected_source_port_ids ?? [];
241003
- if (connectedSourcePortIds.length === 0) {
241004
- throw new Error(`Trace ${args.trace.source_trace_id} has no connected ports`);
241005
- }
241006
- const orderedPortIds = prioritizeFocus(connectedSourcePortIds, args.focusSourcePortId);
241007
- const firstPortId = orderedPortIds[0];
241008
- const secondPortId = orderedPortIds[1] ?? orderedPortIds[0];
241009
- if (!firstPortId || !secondPortId) {
241010
- throw new Error(`Trace ${args.trace.source_trace_id} is missing connected ports`);
241011
- }
241012
- const firstPosition = requirePortPosition(index, firstPortId);
241013
- const secondPosition = requirePortPosition(index, secondPortId);
241014
- const secondLayer = primaryLayer(secondPosition.layers);
241015
- const connectedPins = orderedPortIds.map((sourcePortId) => index.toConnectedPin(sourcePortId));
241016
- const connectionTarget4 = index.getPortReference(secondPortId);
241017
- const straightLineDistanceMm = distanceBetween(firstPosition, secondPosition);
241018
- const points = index.getPcbPathPoints(args.trace.source_trace_id);
241019
- const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
241020
241260
  return {
241021
- id: args.trace.source_trace_id,
241022
- label: `${index.getPortReference(firstPortId)} -> ${connectionTarget4}`,
241023
- connectionType: "direct connection",
241024
- connectionTarget: connectionTarget4,
241025
- connectionTargetPosition: {
241026
- x: secondPosition.x,
241027
- y: secondPosition.y,
241028
- layer: secondLayer
241029
- },
241030
- connectedPins,
241031
- pinPositions: connectedPins,
241032
- requirements: {
241033
- maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
241034
- },
241035
- points,
241036
- lengthMm,
241037
- straightLineDistanceMm,
241038
- sourceTraceId: args.trace.source_trace_id,
241039
- displayName: args.trace.display_name ?? null
241261
+ lineItemType: "ComponentOverlap",
241262
+ firstComponent,
241263
+ secondComponent,
241264
+ overlapWidth,
241265
+ overlapHeight,
241266
+ correctionSuggestions: getOverlapCorrectionSuggestions({
241267
+ firstComponent,
241268
+ secondComponent,
241269
+ overlapWidth,
241270
+ overlapHeight
241271
+ })
241040
241272
  };
241041
- }
241042
- function createNetTraceModel(index, args) {
241043
- const net = index.netsById.get(args.sourceNetId);
241044
- if (!net) {
241045
- throw new Error(`Unable to find net ${args.sourceNetId}`);
241046
- }
241047
- const focusPosition = requirePortPosition(index, args.focusSourcePortId);
241048
- const focusLayer = primaryLayer(focusPosition.layers);
241049
- const connectedPins = index.getConnectedPinsForNet(args.sourceNetId).sort((a2, b) => a2.ref.localeCompare(b.ref));
241050
- const hub = inferNetHub(connectedPins, focusLayer);
241051
- const focusPin = index.toConnectedPin(args.focusSourcePortId);
241052
- const points = index.getPcbPathPoints(args.trace.source_trace_id);
241053
- const straightLineDistanceMm = distanceBetween(focusPosition, hub);
241054
- const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
241055
- return {
241056
- id: `${args.trace.source_trace_id}:${args.focusSourcePortId}`,
241057
- label: `${focusPin.ref} -> net.${net.name ?? net.source_net_id}`,
241058
- connectionType: "via net",
241059
- connectionTarget: `net.${net.name ?? net.source_net_id}`,
241060
- connectionTargetPosition: hub,
241061
- connectedPins: prioritizePin(focusPin, connectedPins),
241062
- pinPositions: prioritizePin(focusPin, connectedPins),
241063
- requirements: {
241064
- maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
241273
+ };
241274
+ var getOverlapCorrectionSuggestions = ({
241275
+ firstComponent,
241276
+ secondComponent,
241277
+ overlapWidth,
241278
+ overlapHeight
241279
+ }) => {
241280
+ const firstArea = firstComponent.width * firstComponent.height;
241281
+ const secondArea = secondComponent.width * secondComponent.height;
241282
+ const targetComponent = firstArea <= secondArea ? firstComponent : secondComponent;
241283
+ const otherComponent = targetComponent === firstComponent ? secondComponent : firstComponent;
241284
+ const deltaSchX = targetComponent.schX <= otherComponent.schX ? -overlapWidth : overlapWidth;
241285
+ const deltaSchY = targetComponent.schY <= otherComponent.schY ? -overlapHeight : overlapHeight;
241286
+ return [
241287
+ {
241288
+ targetComponentName: targetComponent.sourceComponentName,
241289
+ deltaSchX,
241290
+ deltaSchY: 0,
241291
+ newSchX: targetComponent.schX + deltaSchX,
241292
+ newSchY: targetComponent.schY
241065
241293
  },
241066
- points,
241067
- lengthMm,
241068
- straightLineDistanceMm,
241069
- sourceTraceId: args.trace.source_trace_id,
241070
- displayName: args.trace.display_name ?? null
241071
- };
241072
- }
241073
- function requirePortPosition(index, sourcePortId) {
241074
- const position2 = index.getPortPosition(sourcePortId);
241075
- if (!position2) {
241076
- throw new Error(`Unable to analyze ${index.getPortReference(sourcePortId)} because it has no pcb_port position`);
241077
- }
241078
- return position2;
241079
- }
241080
- function inferNetHub(connectedPins, fallbackLayer) {
241081
- const positionedPins = connectedPins.filter((pin) => typeof pin.x === "number" && typeof pin.y === "number");
241082
- if (positionedPins.length === 0) {
241083
- return {
241084
- x: 0,
241085
- y: 0,
241086
- layer: fallbackLayer
241087
- };
241088
- }
241089
- const x3 = positionedPins.reduce((sum, pin) => sum + pin.x, 0) / positionedPins.length;
241090
- const y4 = positionedPins.reduce((sum, pin) => sum + pin.y, 0) / positionedPins.length;
241091
- const layerCounts = new Map;
241092
- for (const pin of positionedPins) {
241093
- const layer2 = pin.layers[0] ?? fallbackLayer;
241094
- layerCounts.set(layer2, (layerCounts.get(layer2) ?? 0) + 1);
241095
- }
241096
- const sortedLayers = [...layerCounts.entries()].sort((left, right) => {
241097
- if (right[1] !== left[1]) {
241098
- return right[1] - left[1];
241099
- }
241100
- return left[0].localeCompare(right[0]);
241101
- });
241102
- const layer = sortedLayers[0]?.[0] ?? fallbackLayer;
241103
- return { x: x3, y: y4, layer };
241104
- }
241105
- function measurePathLength(points) {
241106
- let lengthMm = 0;
241107
- for (let index = 1;index < points.length; index += 1) {
241108
- const previous = points[index - 1];
241109
- const current2 = points[index];
241110
- if (!previous || !current2) {
241111
- continue;
241294
+ {
241295
+ targetComponentName: targetComponent.sourceComponentName,
241296
+ deltaSchX: 0,
241297
+ deltaSchY,
241298
+ newSchX: targetComponent.schX,
241299
+ newSchY: targetComponent.schY + deltaSchY
241112
241300
  }
241113
- lengthMm += distanceBetween(previous, current2);
241114
- }
241115
- return lengthMm;
241116
- }
241117
- function dedupePcbTraces(pcbTraces) {
241118
- const seen = new Set;
241119
- const deduped = [];
241120
- for (const pcbTrace of pcbTraces) {
241121
- if (seen.has(pcbTrace)) {
241122
- continue;
241301
+ ];
241302
+ };
241303
+ var generateSchematicPlacementIssues = (componentPlacements) => {
241304
+ const issues = [];
241305
+ for (let firstIndex = 0;firstIndex < componentPlacements.length; firstIndex++) {
241306
+ for (let secondIndex = firstIndex + 1;secondIndex < componentPlacements.length; secondIndex++) {
241307
+ const overlap = getComponentOverlap(componentPlacements[firstIndex], componentPlacements[secondIndex]);
241308
+ if (overlap) {
241309
+ issues.push(overlap);
241310
+ }
241123
241311
  }
241124
- seen.add(pcbTrace);
241125
- deduped.push(pcbTrace);
241126
241312
  }
241127
- return deduped;
241128
- }
241129
- function normalizePcbTraceRoute(pcbTrace) {
241130
- if (!Array.isArray(pcbTrace.route)) {
241131
- return [];
241313
+ return issues;
241314
+ };
241315
+
241316
+ // node_modules/@tscircuit/circuit-json-schematic-placement-analysis/lib/analyze-schematic-placement.ts
241317
+ var fmtNumber5 = (value) => {
241318
+ if (Number.isInteger(value))
241319
+ return String(value);
241320
+ return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
241321
+ };
241322
+ var fmtDelta = (value) => {
241323
+ const formattedValue = fmtNumber5(value);
241324
+ return value > 0 ? `+${formattedValue}` : formattedValue;
241325
+ };
241326
+ var isSchematicBox = (element) => element.type === "schematic_box";
241327
+ var isSchematicComponent = (element) => element.type === "schematic_component";
241328
+ var getSourceComponentName = (circuitJson, sourceComponentId) => {
241329
+ if (!sourceComponentId)
241330
+ return;
241331
+ return cju7(circuitJson).source_component.get(sourceComponentId)?.name;
241332
+ };
241333
+ var schematicComponentToLineItem = (schematicComponent, circuitJson, schematicBox) => ({
241334
+ lineItemType: "SchematicBoxPlacement",
241335
+ positionAnchor: "center",
241336
+ schX: schematicComponent.center.x,
241337
+ schY: schematicComponent.center.y,
241338
+ width: schematicBox?.width ?? schematicComponent.size.width,
241339
+ height: schematicBox?.height ?? schematicComponent.size.height,
241340
+ sourceComponentId: schematicComponent.source_component_id,
241341
+ sourceComponentName: getSourceComponentName(circuitJson, schematicComponent.source_component_id),
241342
+ schematicComponentId: schematicComponent.schematic_component_id,
241343
+ schematicSymbolId: schematicBox?.schematic_symbol_id ?? schematicComponent.schematic_symbol_id,
241344
+ subcircuitId: schematicComponent.subcircuit_id ?? schematicBox?.subcircuit_id
241345
+ });
241346
+ var schematicBoxToLineItem = (schematicBox, circuitJson) => ({
241347
+ lineItemType: "SchematicBoxPlacement",
241348
+ positionAnchor: "center",
241349
+ schX: schematicBox.x,
241350
+ schY: schematicBox.y,
241351
+ width: schematicBox.width,
241352
+ height: schematicBox.height,
241353
+ ...getSourceComponentMetadata(schematicBox, circuitJson),
241354
+ schematicComponentId: schematicBox.schematic_component_id,
241355
+ schematicSymbolId: schematicBox.schematic_symbol_id,
241356
+ subcircuitId: schematicBox.subcircuit_id
241357
+ });
241358
+ var getSourceComponentMetadata = (schematicBox, circuitJson) => {
241359
+ if (!schematicBox.schematic_component_id)
241360
+ return {};
241361
+ const circuitJsonUtil = cju7(circuitJson);
241362
+ const schematicComponent = circuitJsonUtil.schematic_component.get(schematicBox.schematic_component_id);
241363
+ if (!schematicComponent?.source_component_id)
241364
+ return {};
241365
+ const sourceComponent = circuitJsonUtil.source_component.get(schematicComponent.source_component_id);
241366
+ return {
241367
+ sourceComponentId: schematicComponent.source_component_id,
241368
+ sourceComponentName: sourceComponent?.name
241369
+ };
241370
+ };
241371
+ var addAttr = (attrs, key, value, options) => {
241372
+ if (value === undefined)
241373
+ return;
241374
+ attrs.push(`${key}="${typeof value === "number" ? options?.formatDelta ? fmtDelta(value) : fmtNumber5(value) : escapeAttr3(value)}"`);
241375
+ };
241376
+ var lineItemToString3 = (lineItem) => {
241377
+ const attrs = [];
241378
+ addAttr(attrs, "componentName", lineItem.sourceComponentName);
241379
+ addAttr(attrs, "positionAnchor", lineItem.positionAnchor);
241380
+ addAttr(attrs, "schX", lineItem.schX);
241381
+ addAttr(attrs, "schY", lineItem.schY);
241382
+ addAttr(attrs, "width", lineItem.width);
241383
+ addAttr(attrs, "height", lineItem.height);
241384
+ return `<SchematicBoxPlacement ${attrs.join(" ")} />`;
241385
+ };
241386
+ var overlapIssueToString = (issue) => {
241387
+ const attrs = [];
241388
+ addAttr(attrs, "component1Name", issue.firstComponent.sourceComponentName);
241389
+ addAttr(attrs, "component2Name", issue.secondComponent.sourceComponentName);
241390
+ addAttr(attrs, "component1SchX", issue.firstComponent.schX);
241391
+ addAttr(attrs, "component1SchY", issue.firstComponent.schY);
241392
+ addAttr(attrs, "component2SchX", issue.secondComponent.schX);
241393
+ addAttr(attrs, "component2SchY", issue.secondComponent.schY);
241394
+ addAttr(attrs, "overlapWidth", issue.overlapWidth);
241395
+ addAttr(attrs, "overlapHeight", issue.overlapHeight);
241396
+ return [
241397
+ `<ComponentOverlap ${attrs.join(" ")}>`,
241398
+ ...issue.correctionSuggestions.map(correctionSuggestionToString),
241399
+ "</ComponentOverlap>"
241400
+ ].join(`
241401
+ `);
241402
+ };
241403
+ var correctionSuggestionToString = (suggestion) => {
241404
+ const attrs = [];
241405
+ addAttr(attrs, "target", suggestion.targetComponentName);
241406
+ if (suggestion.deltaSchX !== 0) {
241407
+ addAttr(attrs, "newSchX", suggestion.newSchX);
241408
+ addAttr(attrs, "deltaSchX", suggestion.deltaSchX, { formatDelta: true });
241132
241409
  }
241133
- return pcbTrace.route.filter((point5) => typeof point5.x === "number" && typeof point5.y === "number").map((point5) => ({
241134
- ...point5,
241135
- layer: typeof point5.layer === "string" && point5.layer.length > 0 ? point5.layer : typeof pcbTrace.layer === "string" && pcbTrace.layer.length > 0 ? pcbTrace.layer : "top"
241136
- }));
241137
- }
241138
- function convertPcbTraceRouteToPath(routePoints) {
241139
- const deduped = routePoints.filter((point5, index) => {
241140
- const previous = routePoints[index - 1];
241141
- return !previous || previous.x !== point5.x || previous.y !== point5.y || previous.layer !== point5.layer;
241142
- });
241143
- return deduped.map((point5, index) => ({
241144
- x: point5.x,
241145
- y: point5.y,
241146
- layer: point5.layer,
241147
- kind: classifyPcbTracePoint(point5, index, deduped.length)
241148
- }));
241149
- }
241150
- function classifyPcbTracePoint(point5, index, pointCount) {
241151
- if (point5.route_type === "via") {
241152
- return "via";
241410
+ if (suggestion.deltaSchY !== 0) {
241411
+ addAttr(attrs, "newSchY", suggestion.newSchY);
241412
+ addAttr(attrs, "deltaSchY", suggestion.deltaSchY, { formatDelta: true });
241153
241413
  }
241154
- if (index === 0 || index === pointCount - 1 || point5.start_pcb_port_id || point5.end_pcb_port_id) {
241155
- return "endpoint";
241414
+ return `<OverlapCorrectionSuggestion ${attrs.join(" ")} />`;
241415
+ };
241416
+ var escapeAttr3 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
241417
+
241418
+ class SchematicPlacementAnalysis {
241419
+ lineItems;
241420
+ constructor(lineItems) {
241421
+ this.lineItems = lineItems;
241156
241422
  }
241157
- return "track";
241158
- }
241159
- function renderPinPosition(pin, indent) {
241160
- if (typeof pin.x !== "number" || typeof pin.y !== "number") {
241161
- return `${indent}<Pin ref="${escapeXml(pin.ref)}" position="unavailable" />`;
241423
+ getLineItems() {
241424
+ return this.lineItems;
241162
241425
  }
241163
- const layers = pin.layers.length > 0 ? pin.layers.join(",") : "unknown";
241164
- return `${indent}<Pin ref="${escapeXml(pin.ref)}" x="${formatNumber3(pin.x)}" y="${formatNumber3(pin.y)}" layers="${escapeXml(layers)}" />`;
241165
- }
241166
- function renderConnection(trace) {
241167
- if (trace.connectionTargetPosition) {
241168
- return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" x="${formatNumber3(trace.connectionTargetPosition.x)}" y="${formatNumber3(trace.connectionTargetPosition.y)}" layer="${escapeXml(trace.connectionTargetPosition.layer)}" />`;
241426
+ getString() {
241427
+ return this.toString();
241169
241428
  }
241170
- return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" />`;
241171
- }
241172
- function renderRequirements(requirements, indent) {
241173
- if (typeof requirements.maxLengthMm === "number") {
241429
+ toString() {
241430
+ const schematicBoxPlacements = this.lineItems.filter((lineItem) => lineItem.lineItemType === "SchematicBoxPlacement");
241431
+ const issues = this.lineItems.flatMap((lineItem) => lineItem.lineItemType === "SchematicPlacementIssues" ? lineItem.issues : []);
241174
241432
  return [
241175
- `${indent}<TraceRequirements>`,
241176
- `${indent} <MaxLengthMm>${formatNumber3(requirements.maxLengthMm)}</MaxLengthMm>`,
241177
- `${indent}</TraceRequirements>`
241433
+ "<SchematicBoxPositions>",
241434
+ ...schematicBoxPlacements.map(lineItemToString3),
241435
+ "</SchematicBoxPositions>",
241436
+ ...issues.length > 0 ? [
241437
+ "<SchematicPlacementIssues>",
241438
+ ...issues.map((issue) => {
241439
+ switch (issue.lineItemType) {
241440
+ case "ComponentOverlap":
241441
+ return overlapIssueToString(issue);
241442
+ default:
241443
+ return "";
241444
+ }
241445
+ }),
241446
+ "</SchematicPlacementIssues>"
241447
+ ] : []
241178
241448
  ].join(`
241179
241449
  `);
241180
241450
  }
241181
- return `${indent}<TraceRequirements none />`;
241182
- }
241183
- function portMatchesLabel(port, label) {
241184
- const loweredLabel = label.toLowerCase();
241185
- const portName = port.name?.toLowerCase();
241186
- const pinNumber = String(port.pin_number ?? "").toLowerCase();
241187
- const portHints2 = (port.port_hints ?? []).map((hint) => hint.toLowerCase());
241188
- return portName === loweredLabel || pinNumber === loweredLabel || portHints2.includes(loweredLabel);
241189
- }
241190
- function prioritizeFocus(portIds, focusSourcePortId) {
241191
- const remainder = portIds.filter((portId) => portId !== focusSourcePortId);
241192
- return [focusSourcePortId, ...remainder];
241193
- }
241194
- function prioritizePin(focusPin, pins) {
241195
- const remainder = pins.filter((pin) => pin.ref !== focusPin.ref);
241196
- return [focusPin, ...remainder];
241197
- }
241198
- function compareTraceModels(left, right) {
241199
- return left.label.localeCompare(right.label);
241200
- }
241201
- function normalizeLayers(layers) {
241202
- if (!Array.isArray(layers)) {
241203
- return ["top"];
241204
- }
241205
- const normalizedLayers = layers.filter((layer) => typeof layer === "string" && layer.length > 0);
241206
- return normalizedLayers.length > 0 ? normalizedLayers : ["top"];
241207
- }
241208
- function primaryLayer(layers) {
241209
- return layers[0] ?? "top";
241210
- }
241211
- function distanceBetween(left, right) {
241212
- return Math.hypot(right.x - left.x, right.y - left.y);
241213
- }
241214
- function formatNumber3(value) {
241215
- return value.toFixed(2);
241216
- }
241217
- function escapeXml(value) {
241218
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
241219
241451
  }
241452
+ var analyzeSchematicPlacement = (circuitJson) => {
241453
+ const schematicBoxes = circuitJson.filter(isSchematicBox);
241454
+ const schematicComponentIds = new Set(circuitJson.filter(isSchematicComponent).map((schematicComponent) => schematicComponent.schematic_component_id));
241455
+ const lineItems = [
241456
+ ...circuitJson.filter(isSchematicComponent).map((schematicComponent) => schematicComponentToLineItem(schematicComponent, circuitJson, schematicBoxes.find((schematicBox) => schematicBox.schematic_component_id === schematicComponent.schematic_component_id))),
241457
+ ...schematicBoxes.filter((schematicBox) => !schematicBox.schematic_component_id || !schematicComponentIds.has(schematicBox.schematic_component_id)).map((schematicBox) => schematicBoxToLineItem(schematicBox, circuitJson))
241458
+ ];
241459
+ const issues = generateSchematicPlacementIssues(lineItems);
241460
+ return new SchematicPlacementAnalysis([
241461
+ ...lineItems,
241462
+ ...issues.length > 0 ? [{ lineItemType: "SchematicPlacementIssues", issues }] : []
241463
+ ]);
241464
+ };
241465
+ // cli/check/schematic-placement/register.ts
241466
+ var checkSchematicPlacement = async (file) => {
241467
+ const resolvedInputFilePath = await resolveCheckInputFilePath(file);
241468
+ const circuitJson = await getCircuitJsonForCheck({
241469
+ filePath: resolvedInputFilePath,
241470
+ platformConfig: {
241471
+ pcbDisabled: true,
241472
+ routingDisabled: true,
241473
+ placementDrcChecksDisabled: true
241474
+ },
241475
+ allowPrebuiltCircuitJson: true
241476
+ });
241477
+ return analyzeSchematicPlacement(circuitJson).getString();
241478
+ };
241479
+ var registerCheckSchematicPlacement = (program2) => {
241480
+ program2.commands.find((c3) => c3.name() === "check").command("schematic-placement").description("Analyze schematic component placement").argument("[file]", "Path to the entry file").action(async (file) => {
241481
+ try {
241482
+ const output = await checkSchematicPlacement(file);
241483
+ console.log(output);
241484
+ } catch (error) {
241485
+ console.error(error instanceof Error ? error.message : String(error));
241486
+ process.exit(1);
241487
+ }
241488
+ });
241489
+ };
241490
+
241220
241491
  // cli/check/trace-length/register.ts
241492
+ var loadTraceLengthAnalyzer = async () => {
241493
+ const mod = await Promise.resolve().then(() => (init_lib(), exports_lib));
241494
+ return mod.analyzeCircuitJsonTraceLength;
241495
+ };
241221
241496
  var checkTraceLength = async (pinOrNetRef, file) => {
241222
241497
  const resolvedInputFilePath = await resolveCheckInputFilePath(file);
241223
241498
  const circuitJson = await getCircuitJsonForCheck({
@@ -241228,7 +241503,8 @@ var checkTraceLength = async (pinOrNetRef, file) => {
241228
241503
  },
241229
241504
  allowPrebuiltCircuitJson: true
241230
241505
  });
241231
- const analysis = analyzeCircuitJsonTraceLength(circuitJson, {
241506
+ const analyzeCircuitJsonTraceLength2 = await loadTraceLengthAnalyzer();
241507
+ const analysis = analyzeCircuitJsonTraceLength2(circuitJson, {
241232
241508
  targetPinOrNet: pinOrNetRef
241233
241509
  });
241234
241510
  return analysis.toString();
@@ -246291,8 +246567,8 @@ var warnIfTsconfigMissingTscircuitType = (projectDir) => {
246291
246567
  }
246292
246568
  try {
246293
246569
  const tsconfig = JSON.parse(fs51.readFileSync(tsconfigPath, "utf-8"));
246294
- const types = tsconfig?.compilerOptions?.types;
246295
- if (!Array.isArray(types) || !types.includes("tscircuit")) {
246570
+ const types2 = tsconfig?.compilerOptions?.types;
246571
+ if (!Array.isArray(types2) || !types2.includes("tscircuit")) {
246296
246572
  console.warn(kleur_default.yellow('Warning: "tscircuit" is missing from tsconfig.json compilerOptions.types. Add it (e.g. "types": ["tscircuit"]) to ensure CLI-provided types work correctly.'));
246297
246573
  }
246298
246574
  } catch {}
@@ -255424,9 +255700,9 @@ var ZodUnion2 = class extends ZodType2 {
255424
255700
  return this._def.options;
255425
255701
  }
255426
255702
  };
255427
- ZodUnion2.create = (types, params2) => {
255703
+ ZodUnion2.create = (types2, params2) => {
255428
255704
  return new ZodUnion2({
255429
- options: types,
255705
+ options: types2,
255430
255706
  typeName: ZodFirstPartyTypeKind3.ZodUnion,
255431
255707
  ...processCreateParams2(params2)
255432
255708
  });
@@ -260055,7 +260331,7 @@ function buildSubtree2(soup, opts) {
260055
260331
  }
260056
260332
  return soup.filter((e4) => included.has(e4));
260057
260333
  }
260058
- var cju7 = (circuitJsonInput, options = {}) => {
260334
+ var cju8 = (circuitJsonInput, options = {}) => {
260059
260335
  const circuitJson = circuitJsonInput;
260060
260336
  let internalStore = circuitJson._internal_store;
260061
260337
  if (!internalStore) {
@@ -260087,7 +260363,7 @@ var cju7 = (circuitJsonInput, options = {}) => {
260087
260363
  return internalStore.editCount;
260088
260364
  }
260089
260365
  if (prop === "subtree") {
260090
- return (opts) => cju7(buildSubtree2(circuitJson, opts), options);
260366
+ return (opts) => cju8(buildSubtree2(circuitJson, opts), options);
260091
260367
  }
260092
260368
  if (prop === "insert") {
260093
260369
  return (elm) => {
@@ -260197,8 +260473,8 @@ var cju7 = (circuitJsonInput, options = {}) => {
260197
260473
  });
260198
260474
  return su24;
260199
260475
  };
260200
- cju7.unparsed = cju7;
260201
- var su9 = cju7;
260476
+ cju8.unparsed = cju8;
260477
+ var su9 = cju8;
260202
260478
  function createIdKey2(element) {
260203
260479
  const type = element.type;
260204
260480
  return `${type}:${element[`${type}_id`]}`;
@@ -266614,7 +266890,7 @@ function buildSubtree3(soup, opts) {
266614
266890
  }
266615
266891
  return soup.filter((e4) => included.has(e4));
266616
266892
  }
266617
- var cju8 = (circuitJsonInput, options = {}) => {
266893
+ var cju9 = (circuitJsonInput, options = {}) => {
266618
266894
  const circuitJson = circuitJsonInput;
266619
266895
  let internalStore = circuitJson._internal_store;
266620
266896
  if (!internalStore) {
@@ -266646,7 +266922,7 @@ var cju8 = (circuitJsonInput, options = {}) => {
266646
266922
  return internalStore.editCount;
266647
266923
  }
266648
266924
  if (prop === "subtree") {
266649
- return (opts) => cju8(buildSubtree3(circuitJson, opts), options);
266925
+ return (opts) => cju9(buildSubtree3(circuitJson, opts), options);
266650
266926
  }
266651
266927
  if (prop === "insert") {
266652
266928
  return (elm) => {
@@ -266756,7 +267032,7 @@ var cju8 = (circuitJsonInput, options = {}) => {
266756
267032
  });
266757
267033
  return su24;
266758
267034
  };
266759
- cju8.unparsed = cju8;
267035
+ cju9.unparsed = cju9;
266760
267036
  function createIdKey3(element) {
266761
267037
  const type = element.type;
266762
267038
  return `${type}:${element[`${type}_id`]}`;
@@ -272512,7 +272788,7 @@ import fs63 from "node:fs";
272512
272788
  import path66 from "node:path";
272513
272789
  import {
272514
272790
  convertCircuitJsonToGltf as convertCircuitJsonToGltf5,
272515
- getBestCameraPosition
272791
+ getBestCameraPosition as getBestCameraPosition2
272516
272792
  } from "circuit-json-to-gltf";
272517
272793
  import {
272518
272794
  convertCircuitJsonToPcbSvg as convertCircuitJsonToPcbSvg4,
@@ -275323,7 +275599,7 @@ var processSnapshotFile = async ({
275323
275599
  if (!(glbBuffer instanceof ArrayBuffer)) {
275324
275600
  throw new Error("Expected ArrayBuffer from convertCircuitJsonToGltf with glb format");
275325
275601
  }
275326
- let cameraOptions = getBestCameraPosition(circuitJson);
275602
+ let cameraOptions = getBestCameraPosition2(circuitJson);
275327
275603
  if (cameraPreset) {
275328
275604
  cameraOptions = applyCameraPreset(cameraPreset, cameraOptions);
275329
275605
  }
@@ -275748,6 +276024,7 @@ registerCheckPinSpecification(program2);
275748
276024
  registerCheckPlacement(program2);
275749
276025
  registerCheckRoutingDifficulty(program2);
275750
276026
  registerCheckRouting(program2);
276027
+ registerCheckSchematicPlacement(program2);
275751
276028
  registerCheckTraceLength(program2);
275752
276029
  registerRegistry(program2);
275753
276030
  registerRegistryPackages(program2);