@tscircuit/cli 0.1.1175 → 0.1.1176

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
@@ -98409,7 +98409,7 @@ var import_perfect_cli = __toESM2(require_dist2(), 1);
98409
98409
  // lib/getVersion.ts
98410
98410
  import { createRequire as createRequire2 } from "node:module";
98411
98411
  // package.json
98412
- var version = "0.1.1173";
98412
+ var version = "0.1.1175";
98413
98413
  var package_default = {
98414
98414
  name: "@tscircuit/cli",
98415
98415
  version,
@@ -98421,7 +98421,7 @@ var package_default = {
98421
98421
  devDependencies: {
98422
98422
  "@babel/standalone": "^7.26.9",
98423
98423
  "@biomejs/biome": "^1.9.4",
98424
- "@tscircuit/circuit-json-placement-analysis": "^0.0.4",
98424
+ "@tscircuit/circuit-json-placement-analysis": "^0.0.5",
98425
98425
  "@tscircuit/circuit-json-routing-analysis": "^0.0.1",
98426
98426
  "@tscircuit/fake-snippets": "^0.0.182",
98427
98427
  "@tscircuit/file-server": "^0.0.32",
@@ -110751,6 +110751,673 @@ var analyzeComponentPlacement = (circuitJson, componentName) => {
110751
110751
  `)
110752
110752
  };
110753
110753
  };
110754
+ var TOP_ISSUE_LIMIT = 5;
110755
+ var CENTER_ANCHOR2 = "center";
110756
+ var ISSUE_TYPE_ORDER = [
110757
+ "pad_overlap",
110758
+ "off_board",
110759
+ "courtyard_collision",
110760
+ "connector_body_intrusion",
110761
+ "footprint_intrusion"
110762
+ ];
110763
+ var toNumber22 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
110764
+ var fmtNumber2 = (value) => {
110765
+ if (Number.isInteger(value))
110766
+ return String(value);
110767
+ return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
110768
+ };
110769
+ var fmtMm2 = (value) => `${fmtNumber2(value)}mm`;
110770
+ var getBoundsFromCenterAndSize = (centerX, centerY, width, height) => ({
110771
+ min_x: centerX - width / 2,
110772
+ max_x: centerX + width / 2,
110773
+ min_y: centerY - height / 2,
110774
+ max_y: centerY + height / 2,
110775
+ width,
110776
+ height
110777
+ });
110778
+ var getBoundsFromPoints = (points) => {
110779
+ const xs = [];
110780
+ const ys = [];
110781
+ for (const point of points) {
110782
+ const x = toNumber22(point.x);
110783
+ const y = toNumber22(point.y);
110784
+ if (x === null || y === null)
110785
+ continue;
110786
+ xs.push(x);
110787
+ ys.push(y);
110788
+ }
110789
+ if (xs.length === 0 || ys.length === 0)
110790
+ return null;
110791
+ const min_x = Math.min(...xs);
110792
+ const max_x = Math.max(...xs);
110793
+ const min_y = Math.min(...ys);
110794
+ const max_y = Math.max(...ys);
110795
+ return {
110796
+ min_x,
110797
+ max_x,
110798
+ min_y,
110799
+ max_y,
110800
+ width: max_x - min_x,
110801
+ height: max_y - min_y
110802
+ };
110803
+ };
110804
+ var getOverlap = (a, b) => {
110805
+ const overlapX = Math.min(a.max_x, b.max_x) - Math.max(a.min_x, b.min_x);
110806
+ const overlapY = Math.min(a.max_y, b.max_y) - Math.max(a.min_y, b.min_y);
110807
+ if (overlapX <= 0 || overlapY <= 0)
110808
+ return null;
110809
+ return {
110810
+ overlapX,
110811
+ overlapY,
110812
+ clearance: -Math.min(overlapX, overlapY)
110813
+ };
110814
+ };
110815
+ var layersIntersect = (a, b) => {
110816
+ const bSet = new Set(b);
110817
+ return a.some((layer) => bSet.has(layer));
110818
+ };
110819
+ var getArea = (bounds) => {
110820
+ if (!bounds)
110821
+ return Number.POSITIVE_INFINITY;
110822
+ return bounds.width * bounds.height;
110823
+ };
110824
+ var stripNumericSuffix = (name) => {
110825
+ const stripped = name.replace(/\d+$/, "");
110826
+ return stripped.length >= 2 ? stripped : name;
110827
+ };
110828
+ var getSummaryLabel = (type, count) => {
110829
+ if (count === 0)
110830
+ return null;
110831
+ switch (type) {
110832
+ case "pad_overlap":
110833
+ return `${count} pad overlap${count === 1 ? "" : "s"}`;
110834
+ case "off_board":
110835
+ return `${count} off-board`;
110836
+ case "courtyard_collision":
110837
+ return `${count} courtyard collision${count === 1 ? "" : "s"}`;
110838
+ case "connector_body_intrusion":
110839
+ return `${count} connector-body intrusion${count === 1 ? "" : "s"}`;
110840
+ case "footprint_intrusion":
110841
+ return `${count} footprint intrusion${count === 1 ? "" : "s"}`;
110842
+ }
110843
+ };
110844
+ var getMoveDirectionForEdge = (edge) => {
110845
+ switch (edge) {
110846
+ case "left":
110847
+ return "right";
110848
+ case "right":
110849
+ return "left";
110850
+ case "top":
110851
+ return "down";
110852
+ case "bottom":
110853
+ return "up";
110854
+ }
110855
+ };
110856
+ var getBoardEdgeStatus = (bounds, boardBounds, componentName) => {
110857
+ if (!bounds || !boardBounds)
110858
+ return null;
110859
+ const clearances = [
110860
+ { edge: "left", clearance: bounds.min_x - boardBounds.min_x },
110861
+ { edge: "right", clearance: boardBounds.max_x - bounds.max_x },
110862
+ { edge: "top", clearance: bounds.min_y - boardBounds.min_y },
110863
+ {
110864
+ edge: "bottom",
110865
+ clearance: boardBounds.max_y - bounds.max_y
110866
+ }
110867
+ ];
110868
+ const outside = clearances.filter((entry) => entry.clearance < 0).sort((a, b) => a.clearance - b.clearance);
110869
+ const selected = outside[0] ?? [...clearances].sort((a, b) => a.clearance - b.clearance)[0] ?? null;
110870
+ if (!selected)
110871
+ return null;
110872
+ return {
110873
+ component_name: componentName,
110874
+ edge: selected.edge,
110875
+ status: selected.clearance < 0 ? "outside" : "inside",
110876
+ distance: Math.abs(selected.clearance)
110877
+ };
110878
+ };
110879
+ var formatBoardEdgeStatus = (status) => `${fmtMm2(status.distance)} ${status.status} ${status.edge} edge`;
110880
+ var chooseMover = (a, b, preferredMover) => {
110881
+ if (preferredMover === a.name)
110882
+ return { mover: a, anchor: b };
110883
+ if (preferredMover === b.name)
110884
+ return { mover: b, anchor: a };
110885
+ if (a.isConnectorLike && !b.isConnectorLike)
110886
+ return { mover: b, anchor: a };
110887
+ if (b.isConnectorLike && !a.isConnectorLike)
110888
+ return { mover: a, anchor: b };
110889
+ const areaA = getArea(a.bounds);
110890
+ const areaB = getArea(b.bounds);
110891
+ if (areaA < areaB)
110892
+ return { mover: a, anchor: b };
110893
+ if (areaB < areaA)
110894
+ return { mover: b, anchor: a };
110895
+ return a.order > b.order ? { mover: a, anchor: b } : { mover: b, anchor: a };
110896
+ };
110897
+ var getMoveDirectionBetweenComponents = (mover, anchor, axis) => {
110898
+ if (axis === "x") {
110899
+ if ((mover.centerX ?? 0) >= (anchor.centerX ?? 0))
110900
+ return "right";
110901
+ return "left";
110902
+ }
110903
+ if ((mover.centerY ?? 0) >= (anchor.centerY ?? 0))
110904
+ return "down";
110905
+ return "up";
110906
+ };
110907
+ var getSeparationSuggestion = (a, b, overlapX, overlapY, preferredMover) => {
110908
+ if (a.centerX === null || a.centerY === null || b.centerX === null || b.centerY === null) {
110909
+ return;
110910
+ }
110911
+ const { mover, anchor } = chooseMover(a, b, preferredMover);
110912
+ const axis = overlapX <= overlapY ? "x" : "y";
110913
+ const distance = axis === "x" ? overlapX : overlapY;
110914
+ const direction = getMoveDirectionBetweenComponents(mover, anchor, axis);
110915
+ return `move ${mover.name} ${fmtMm2(distance)} ${direction}`;
110916
+ };
110917
+ var getCountainmentBonus = (a, b) => {
110918
+ if (!a.bounds || !b.bounds || a.centerX === null || a.centerY === null)
110919
+ return 0;
110920
+ const centerInsideB = a.centerX >= b.bounds.min_x && a.centerX <= b.bounds.max_x && a.centerY >= b.bounds.min_y && a.centerY <= b.bounds.max_y;
110921
+ return centerInsideB ? 25 : 0;
110922
+ };
110923
+ var createIssue = (issue) => issue;
110924
+ var getComponentByPcbId = (componentsByPcbId, pcbComponentId) => {
110925
+ if (typeof pcbComponentId !== "string")
110926
+ return null;
110927
+ return componentsByPcbId.get(pcbComponentId) ?? null;
110928
+ };
110929
+ var getLayers = (value) => {
110930
+ if (!Array.isArray(value))
110931
+ return [];
110932
+ return value.filter((layer) => typeof layer === "string");
110933
+ };
110934
+ var buildComponentContexts = (circuitJson) => {
110935
+ const components = [];
110936
+ const sourceComponentsById = /* @__PURE__ */ new Map;
110937
+ const componentByName = /* @__PURE__ */ new Map;
110938
+ const componentsByPcbId = /* @__PURE__ */ new Map;
110939
+ let order = 0;
110940
+ for (const el of circuitJson) {
110941
+ if (el.type !== "source_component" || typeof el.source_component_id !== "string" || typeof el.name !== "string") {
110942
+ continue;
110943
+ }
110944
+ if (componentByName.has(el.name))
110945
+ continue;
110946
+ const context = {
110947
+ name: el.name,
110948
+ sourceComponent: el,
110949
+ sourceComponentId: el.source_component_id,
110950
+ pcbComponent: null,
110951
+ pcbComponentId: null,
110952
+ centerX: null,
110953
+ centerY: null,
110954
+ layer: null,
110955
+ width: null,
110956
+ height: null,
110957
+ bounds: null,
110958
+ anchorAlignment: CENTER_ANCHOR2,
110959
+ placementMode: "none",
110960
+ pads: [],
110961
+ courtyards: [],
110962
+ isConnectorLike: false,
110963
+ order: order++
110964
+ };
110965
+ const xDefinitionRaw = el.pcbX ?? el.pcb_x ?? el.x;
110966
+ const yDefinitionRaw = el.pcbY ?? el.pcb_y ?? el.y;
110967
+ context.xDefinition = xDefinitionRaw === undefined ? undefined : String(xDefinitionRaw);
110968
+ context.yDefinition = yDefinitionRaw === undefined ? undefined : String(yDefinitionRaw);
110969
+ if (context.xDefinition !== undefined || context.yDefinition !== undefined) {
110970
+ context.placementMode = "props_set";
110971
+ } else if (el.placement_mode === "auto") {
110972
+ context.placementMode = "auto";
110973
+ }
110974
+ components.push(context);
110975
+ sourceComponentsById.set(context.sourceComponentId, context);
110976
+ componentByName.set(context.name, context);
110977
+ }
110978
+ for (const el of circuitJson) {
110979
+ if (el.type !== "pcb_component" || typeof el.source_component_id !== "string" || typeof el.pcb_component_id !== "string") {
110980
+ continue;
110981
+ }
110982
+ const context = sourceComponentsById.get(el.source_component_id);
110983
+ if (!context)
110984
+ continue;
110985
+ context.pcbComponent = el;
110986
+ context.pcbComponentId = el.pcb_component_id;
110987
+ componentsByPcbId.set(el.pcb_component_id, context);
110988
+ const center = typeof el.center === "object" && el.center ? el.center : null;
110989
+ context.centerX = center ? toNumber22(center.x) : null;
110990
+ context.centerY = center ? toNumber22(center.y) : null;
110991
+ context.layer = typeof el.layer === "string" ? el.layer : null;
110992
+ context.width = toNumber22(el.width);
110993
+ context.height = toNumber22(el.height);
110994
+ if (context.centerX !== null && context.centerY !== null && context.width !== null && context.height !== null) {
110995
+ context.bounds = getBoundsFromCenterAndSize(context.centerX, context.centerY, context.width, context.height);
110996
+ }
110997
+ if (context.placementMode === "none" && el.position_mode === "auto") {
110998
+ context.placementMode = "auto";
110999
+ }
111000
+ if (context.sourceComponent.ftype === "simple_pin_header" && context.width !== null && context.height !== null) {
111001
+ context.orientation = context.width >= context.height ? "horizontal" : "vertical";
111002
+ }
111003
+ }
111004
+ for (const el of circuitJson) {
111005
+ if (el.type === "pcb_silkscreen_text" && typeof el.anchor_alignment === "string") {
111006
+ const context = getComponentByPcbId(componentsByPcbId, el.pcb_component_id);
111007
+ if (!context)
111008
+ continue;
111009
+ context.anchorAlignment = el.anchor_alignment;
111010
+ continue;
111011
+ }
111012
+ if (el.type === "pcb_smtpad") {
111013
+ const context = getComponentByPcbId(componentsByPcbId, el.pcb_component_id);
111014
+ if (!context)
111015
+ continue;
111016
+ const x = toNumber22(el.x);
111017
+ const y = toNumber22(el.y);
111018
+ const width = toNumber22(el.width);
111019
+ const height = toNumber22(el.height);
111020
+ const layer = typeof el.layer === "string" ? el.layer : null;
111021
+ if (x === null || y === null || width === null || height === null || !layer)
111022
+ continue;
111023
+ context.pads.push({
111024
+ bounds: getBoundsFromCenterAndSize(x, y, width, height),
111025
+ layers: [layer]
111026
+ });
111027
+ continue;
111028
+ }
111029
+ if (el.type === "pcb_plated_hole") {
111030
+ const context = getComponentByPcbId(componentsByPcbId, el.pcb_component_id);
111031
+ if (!context)
111032
+ continue;
111033
+ const x = toNumber22(el.x);
111034
+ const y = toNumber22(el.y);
111035
+ const layers = getLayers(el.layers);
111036
+ if (x === null || y === null || layers.length === 0)
111037
+ continue;
111038
+ let width = null;
111039
+ let height = null;
111040
+ if (el.shape === "circle") {
111041
+ const diameter = toNumber22(el.outer_diameter) ?? toNumber22(el.hole_diameter);
111042
+ width = diameter;
111043
+ height = diameter;
111044
+ } else if (el.shape === "circular_hole_with_rect_pad" || el.shape === "pill_hole_with_rect_pad" || el.shape === "rotated_pill_hole_with_rect_pad") {
111045
+ width = toNumber22(el.rect_pad_width);
111046
+ height = toNumber22(el.rect_pad_height);
111047
+ } else if (el.shape === "hole_with_polygon_pad") {
111048
+ const padOutline = Array.isArray(el.pad_outline) ? el.pad_outline : [];
111049
+ const padBounds = getBoundsFromPoints(padOutline.map((point) => ({
111050
+ x: (toNumber22(point.x) ?? 0) + x,
111051
+ y: (toNumber22(point.y) ?? 0) + y
111052
+ })));
111053
+ if (padBounds) {
111054
+ context.pads.push({
111055
+ bounds: padBounds,
111056
+ layers
111057
+ });
111058
+ }
111059
+ continue;
111060
+ }
111061
+ if (width === null || height === null)
111062
+ continue;
111063
+ context.pads.push({
111064
+ bounds: getBoundsFromCenterAndSize(x, y, width, height),
111065
+ layers
111066
+ });
111067
+ continue;
111068
+ }
111069
+ if (el.type === "pcb_courtyard_rect") {
111070
+ const context = getComponentByPcbId(componentsByPcbId, el.pcb_component_id);
111071
+ if (!context)
111072
+ continue;
111073
+ const center = typeof el.center === "object" && el.center ? el.center : null;
111074
+ const centerX = center ? toNumber22(center.x) : null;
111075
+ const centerY = center ? toNumber22(center.y) : null;
111076
+ const width = toNumber22(el.width);
111077
+ const height = toNumber22(el.height);
111078
+ if (centerX === null || centerY === null || width === null || height === null)
111079
+ continue;
111080
+ context.courtyards.push(getBoundsFromCenterAndSize(centerX, centerY, width, height));
111081
+ continue;
111082
+ }
111083
+ if (el.type === "pcb_courtyard_outline") {
111084
+ const context = getComponentByPcbId(componentsByPcbId, el.pcb_component_id);
111085
+ if (!context)
111086
+ continue;
111087
+ const outline = Array.isArray(el.outline) ? el.outline : [];
111088
+ const bounds = getBoundsFromPoints(outline);
111089
+ if (bounds)
111090
+ context.courtyards.push(bounds);
111091
+ continue;
111092
+ }
111093
+ if (el.type === "pcb_courtyard_polygon") {
111094
+ const context = getComponentByPcbId(componentsByPcbId, el.pcb_component_id);
111095
+ if (!context)
111096
+ continue;
111097
+ const points = Array.isArray(el.points) ? el.points : [];
111098
+ const bounds = getBoundsFromPoints(points);
111099
+ if (bounds)
111100
+ context.courtyards.push(bounds);
111101
+ }
111102
+ }
111103
+ for (const context of components) {
111104
+ const nameUpper = context.name.toUpperCase();
111105
+ const ftype = String(context.sourceComponent.ftype ?? "").toUpperCase();
111106
+ const manufacturerPartNumber = String(context.sourceComponent.manufacturer_part_number ?? context.sourceComponent.manufacturerPartNumber ?? "").toUpperCase();
111107
+ const platedHoleCount = context.pads.filter((pad) => pad.layers.includes("bottom")).length;
111108
+ context.isConnectorLike = ftype === "SIMPLE_PIN_HEADER" || nameUpper.startsWith("USB") || /^J[A-Z0-9_]*\d*$/.test(nameUpper) || /USB|CONN|CONNECTOR|HEADER|SOCKET|JST|HDMI|RJ|BARREL/.test(manufacturerPartNumber) || platedHoleCount >= 4 && context.bounds !== null && Math.max(context.bounds.width, context.bounds.height) >= 4;
111109
+ }
111110
+ const pcbBoard = circuitJson.find((el) => el.type === "pcb_board");
111111
+ const boardCenter = pcbBoard && typeof pcbBoard.center === "object" && pcbBoard.center ? pcbBoard.center : null;
111112
+ const boardCenterX = boardCenter ? toNumber22(boardCenter.x) : null;
111113
+ const boardCenterY = boardCenter ? toNumber22(boardCenter.y) : null;
111114
+ const boardWidth = toNumber22(pcbBoard?.width);
111115
+ const boardHeight = toNumber22(pcbBoard?.height);
111116
+ const boardBounds = boardCenterX !== null && boardCenterY !== null && boardWidth !== null && boardHeight !== null ? getBoundsFromCenterAndSize(boardCenterX, boardCenterY, boardWidth, boardHeight) : null;
111117
+ return {
111118
+ components,
111119
+ componentByName,
111120
+ boardBounds
111121
+ };
111122
+ };
111123
+ var buildIssues = (components, boardBounds) => {
111124
+ const issues = [];
111125
+ for (const component of components) {
111126
+ const boardEdgeStatus = getBoardEdgeStatus(component.bounds, boardBounds, component.name);
111127
+ if (!boardEdgeStatus || boardEdgeStatus.status !== "outside")
111128
+ continue;
111129
+ const moveDirection = getMoveDirectionForEdge(boardEdgeStatus.edge);
111130
+ issues.push(createIssue({
111131
+ type: "off_board",
111132
+ componentA: component.name,
111133
+ clearance: -boardEdgeStatus.distance,
111134
+ severity: 220 + boardEdgeStatus.distance * 100,
111135
+ summary: `${component.name} is ${fmtMm2(boardEdgeStatus.distance)} outside ${boardEdgeStatus.edge} edge`,
111136
+ suggested_move: `move ${component.name} ${fmtMm2(boardEdgeStatus.distance)} ${moveDirection} to clear ${boardEdgeStatus.edge} edge`
111137
+ }));
111138
+ }
111139
+ for (let i = 0;i < components.length; i += 1) {
111140
+ const a = components[i];
111141
+ if (!a)
111142
+ continue;
111143
+ for (let j = i + 1;j < components.length; j += 1) {
111144
+ const b = components[j];
111145
+ if (!b)
111146
+ continue;
111147
+ if (!a.bounds || !b.bounds)
111148
+ continue;
111149
+ if (a.layer !== null && b.layer !== null && a.layer !== b.layer)
111150
+ continue;
111151
+ let strongestPadOverlap = null;
111152
+ for (const padA of a.pads) {
111153
+ for (const padB of b.pads) {
111154
+ if (!layersIntersect(padA.layers, padB.layers))
111155
+ continue;
111156
+ const overlap = getOverlap(padA.bounds, padB.bounds);
111157
+ if (!overlap)
111158
+ continue;
111159
+ if (!strongestPadOverlap || overlap.clearance < strongestPadOverlap.clearance) {
111160
+ strongestPadOverlap = overlap;
111161
+ }
111162
+ }
111163
+ }
111164
+ const bodyOverlap = getOverlap(a.bounds, b.bounds);
111165
+ let strongestCourtyardOverlap = null;
111166
+ if (!strongestPadOverlap) {
111167
+ for (const courtyardA of a.courtyards) {
111168
+ for (const courtyardB of b.courtyards) {
111169
+ const overlap = getOverlap(courtyardA, courtyardB);
111170
+ if (!overlap)
111171
+ continue;
111172
+ if (!strongestCourtyardOverlap || overlap.clearance < strongestCourtyardOverlap.clearance) {
111173
+ strongestCourtyardOverlap = overlap;
111174
+ }
111175
+ }
111176
+ }
111177
+ }
111178
+ if (strongestPadOverlap) {
111179
+ issues.push(createIssue({
111180
+ type: "pad_overlap",
111181
+ componentA: a.name,
111182
+ componentB: b.name,
111183
+ clearance: strongestPadOverlap.clearance,
111184
+ severity: 300 + Math.abs(strongestPadOverlap.clearance) * 120,
111185
+ summary: `${a.name} and ${b.name} pad overlap by ${fmtMm2(Math.abs(strongestPadOverlap.clearance))}`,
111186
+ suggested_move: getSeparationSuggestion(a, b, strongestPadOverlap.overlapX, strongestPadOverlap.overlapY)
111187
+ }));
111188
+ }
111189
+ if (bodyOverlap) {
111190
+ if (a.isConnectorLike || b.isConnectorLike) {
111191
+ const connector = a.isConnectorLike ? a : b;
111192
+ const intruder = connector === a ? b : a;
111193
+ const overlapX = bodyOverlap.overlapX;
111194
+ const overlapY = bodyOverlap.overlapY;
111195
+ const containmentBonus = getCountainmentBonus(intruder, connector);
111196
+ issues.push(createIssue({
111197
+ type: "connector_body_intrusion",
111198
+ componentA: intruder.name,
111199
+ componentB: connector.name,
111200
+ clearance: bodyOverlap.clearance,
111201
+ severity: 260 + Math.abs(bodyOverlap.clearance) * 100 + containmentBonus,
111202
+ summary: `${intruder.name} intrudes ${fmtMm2(Math.abs(bodyOverlap.clearance))} into ${connector.name} connector body`,
111203
+ suggested_move: getSeparationSuggestion(a, b, overlapX, overlapY, intruder.name)
111204
+ }));
111205
+ } else if (!strongestPadOverlap) {
111206
+ const containmentBonus = getCountainmentBonus(a, b) + getCountainmentBonus(b, a);
111207
+ issues.push(createIssue({
111208
+ type: "footprint_intrusion",
111209
+ componentA: a.name,
111210
+ componentB: b.name,
111211
+ clearance: bodyOverlap.clearance,
111212
+ severity: 180 + Math.abs(bodyOverlap.clearance) * 80 + containmentBonus,
111213
+ summary: `${a.name} and ${b.name} footprint intrusion by ${fmtMm2(Math.abs(bodyOverlap.clearance))}`,
111214
+ suggested_move: getSeparationSuggestion(a, b, bodyOverlap.overlapX, bodyOverlap.overlapY)
111215
+ }));
111216
+ }
111217
+ } else if (strongestCourtyardOverlap) {
111218
+ issues.push(createIssue({
111219
+ type: "courtyard_collision",
111220
+ componentA: a.name,
111221
+ componentB: b.name,
111222
+ clearance: strongestCourtyardOverlap.clearance,
111223
+ severity: 120 + Math.abs(strongestCourtyardOverlap.clearance) * 80,
111224
+ summary: `${a.name} and ${b.name} courtyard collision by ${fmtMm2(Math.abs(strongestCourtyardOverlap.clearance))}`,
111225
+ suggested_move: getSeparationSuggestion(a, b, strongestCourtyardOverlap.overlapX, strongestCourtyardOverlap.overlapY)
111226
+ }));
111227
+ }
111228
+ }
111229
+ }
111230
+ return issues.sort((a, b) => b.severity - a.severity);
111231
+ };
111232
+ var buildClusters = (components, issues) => {
111233
+ const componentsByName = new Map(components.map((component) => [component.name, component]));
111234
+ const adjacency = /* @__PURE__ */ new Map;
111235
+ const incidentSeverity = /* @__PURE__ */ new Map;
111236
+ for (const component of components) {
111237
+ adjacency.set(component.name, /* @__PURE__ */ new Set);
111238
+ incidentSeverity.set(component.name, 0);
111239
+ }
111240
+ for (const issue of issues) {
111241
+ if (!issue.componentB)
111242
+ continue;
111243
+ adjacency.get(issue.componentA)?.add(issue.componentB);
111244
+ adjacency.get(issue.componentB)?.add(issue.componentA);
111245
+ incidentSeverity.set(issue.componentA, (incidentSeverity.get(issue.componentA) ?? 0) + issue.severity);
111246
+ incidentSeverity.set(issue.componentB, (incidentSeverity.get(issue.componentB) ?? 0) + issue.severity);
111247
+ }
111248
+ const visited = /* @__PURE__ */ new Set;
111249
+ const clusters = [];
111250
+ for (const component of components) {
111251
+ if (visited.has(component.name))
111252
+ continue;
111253
+ const queue = [component.name];
111254
+ const members = [];
111255
+ while (queue.length > 0) {
111256
+ const current = queue.shift();
111257
+ if (visited.has(current))
111258
+ continue;
111259
+ visited.add(current);
111260
+ members.push(current);
111261
+ for (const neighbor of adjacency.get(current) ?? []) {
111262
+ if (!visited.has(neighbor))
111263
+ queue.push(neighbor);
111264
+ }
111265
+ }
111266
+ if (members.length < 3)
111267
+ continue;
111268
+ const memberSet = new Set(members);
111269
+ const severity = issues.filter((issue) => issue.componentB && memberSet.has(issue.componentA) && memberSet.has(issue.componentB)).reduce((sum, issue) => sum + issue.severity, 0);
111270
+ if (severity === 0)
111271
+ continue;
111272
+ const sortedMembers = [...members].sort((a, b) => {
111273
+ const severityA = incidentSeverity.get(a) ?? 0;
111274
+ const severityB = incidentSeverity.get(b) ?? 0;
111275
+ if (severityA !== severityB)
111276
+ return severityB - severityA;
111277
+ const orderA = componentsByName.get(a)?.order ?? 0;
111278
+ const orderB = componentsByName.get(b)?.order ?? 0;
111279
+ return orderA - orderB;
111280
+ });
111281
+ const labelComponent = sortedMembers.find((name) => componentsByName.get(name)?.isConnectorLike) ?? sortedMembers[0];
111282
+ if (!labelComponent)
111283
+ continue;
111284
+ clusters.push({
111285
+ clusterName: `${stripNumericSuffix(labelComponent)} cluster`,
111286
+ componentNames: sortedMembers,
111287
+ severity
111288
+ });
111289
+ }
111290
+ return clusters.sort((a, b) => b.severity - a.severity);
111291
+ };
111292
+ var buildCountsByType = (issues) => {
111293
+ const counts = {};
111294
+ for (const issue of issues) {
111295
+ counts[issue.type] = (counts[issue.type] ?? 0) + 1;
111296
+ }
111297
+ return counts;
111298
+ };
111299
+ var buildComponentStatuses = (components, boardBounds, issues) => components.map((component) => {
111300
+ const componentIssues = issues.filter((issue) => issue.componentA === component.name || issue.componentB === component.name);
111301
+ return {
111302
+ componentName: component.name,
111303
+ placementMode: component.placementMode,
111304
+ sourcePlacement: {
111305
+ xDefinition: component.xDefinition,
111306
+ yDefinition: component.yDefinition
111307
+ },
111308
+ resolvedPlacement: {
111309
+ center: component.centerX !== null && component.centerY !== null && component.layer !== null ? {
111310
+ x: component.centerX,
111311
+ y: component.centerY,
111312
+ layer: component.layer
111313
+ } : undefined,
111314
+ bounds: component.bounds ? {
111315
+ width: component.bounds.width,
111316
+ height: component.bounds.height,
111317
+ min_x: component.bounds.min_x,
111318
+ max_x: component.bounds.max_x,
111319
+ min_y: component.bounds.min_y,
111320
+ max_y: component.bounds.max_y
111321
+ } : undefined,
111322
+ anchorAlignment: component.anchorAlignment,
111323
+ orientation: component.orientation
111324
+ },
111325
+ boardEdgeStatus: getBoardEdgeStatus(component.bounds, boardBounds, component.name),
111326
+ issues: componentIssues
111327
+ };
111328
+ });
111329
+ var formatSourcePlacement = (component) => {
111330
+ const bits = [`placement_mode=${component.placementMode}`];
111331
+ if (component.sourcePlacement.xDefinition !== undefined) {
111332
+ bits.push(`x=${component.sourcePlacement.xDefinition}`);
111333
+ }
111334
+ if (component.sourcePlacement.yDefinition !== undefined) {
111335
+ bits.push(`y=${component.sourcePlacement.yDefinition}`);
111336
+ }
111337
+ return bits.join(", ");
111338
+ };
111339
+ var formatResolvedPlacement = (component) => {
111340
+ const bits = [];
111341
+ const center = component.resolvedPlacement.center;
111342
+ const bounds = component.resolvedPlacement.bounds;
111343
+ if (center) {
111344
+ bits.push(`center=(${fmtMm2(center.x)}, ${fmtMm2(center.y)}) on ${center.layer}`);
111345
+ }
111346
+ if (bounds) {
111347
+ bits.push(`bounds=(minX=${fmtMm2(bounds.min_x)}, maxX=${fmtMm2(bounds.max_x)}, minY=${fmtMm2(bounds.min_y)}, maxY=${fmtMm2(bounds.max_y)})`);
111348
+ bits.push(`size=(width=${fmtMm2(bounds.width)}, height=${fmtMm2(bounds.height)})`);
111349
+ }
111350
+ bits.push(`anchor_alignment="${component.resolvedPlacement.anchorAlignment}"`);
111351
+ if (component.resolvedPlacement.orientation) {
111352
+ bits.push(`orientation=${component.resolvedPlacement.orientation}`);
111353
+ }
111354
+ return bits.join("; ");
111355
+ };
111356
+ var formatIssue = (issue) => {
111357
+ const suffix = issue.suggested_move ? ` Suggested move: ${issue.suggested_move}.` : "";
111358
+ return `${issue.summary}.${suffix}`;
111359
+ };
111360
+ var formatPlacementAnalysisReport = (report) => {
111361
+ const lines = [];
111362
+ const summaryBits = ISSUE_TYPE_ORDER.map((type) => getSummaryLabel(type, report.summary.countsByType[type] ?? 0)).filter((entry) => Boolean(entry));
111363
+ lines.push(summaryBits.length > 0 ? `placement summary: ${summaryBits.join(", ")}` : "placement summary: no placement issues");
111364
+ if (report.summary.topIssues.length > 0) {
111365
+ lines.push("");
111366
+ lines.push("worst issues:");
111367
+ report.summary.topIssues.forEach((issue, index) => {
111368
+ lines.push(`${index + 1}. ${formatIssue(issue)}`);
111369
+ });
111370
+ }
111371
+ if (report.summary.likelyBadClusters.length > 0) {
111372
+ lines.push("");
111373
+ lines.push("likely bad clusters:");
111374
+ for (const cluster of report.summary.likelyBadClusters) {
111375
+ lines.push(`- ${cluster.clusterName}: ${cluster.componentNames.join(", ")}`);
111376
+ }
111377
+ }
111378
+ lines.push("");
111379
+ lines.push("board-edge status:");
111380
+ for (const component of report.components) {
111381
+ if (!component.boardEdgeStatus)
111382
+ continue;
111383
+ lines.push(`- ${component.componentName}: ${formatBoardEdgeStatus(component.boardEdgeStatus)}`);
111384
+ }
111385
+ const flaggedComponents = report.components.filter((component) => component.issues.length > 0);
111386
+ if (flaggedComponents.length > 0) {
111387
+ lines.push("");
111388
+ lines.push("flagged components:");
111389
+ for (const component of flaggedComponents) {
111390
+ lines.push(`- ${component.componentName}`);
111391
+ lines.push(` source placement: ${formatSourcePlacement(component)}`);
111392
+ lines.push(` resolved placement: ${formatResolvedPlacement(component)}`);
111393
+ if (component.boardEdgeStatus) {
111394
+ lines.push(` board edge status: ${formatBoardEdgeStatus(component.boardEdgeStatus)}`);
111395
+ }
111396
+ lines.push(" issues:");
111397
+ for (const issue of component.issues) {
111398
+ lines.push(` - ${formatIssue(issue)}`);
111399
+ }
111400
+ }
111401
+ }
111402
+ return lines.join(`
111403
+ `);
111404
+ };
111405
+ var buildPlacementAnalysisReport = (circuitJson) => {
111406
+ const { components, boardBounds } = buildComponentContexts(circuitJson);
111407
+ const issues = buildIssues(components, boardBounds);
111408
+ const clusters = buildClusters(components, issues);
111409
+ const countsByType = buildCountsByType(issues);
111410
+ return {
111411
+ summary: {
111412
+ totalIssueCount: issues.length,
111413
+ countsByType,
111414
+ topIssues: issues.slice(0, TOP_ISSUE_LIMIT),
111415
+ likelyBadClusters: clusters
111416
+ },
111417
+ components: buildComponentStatuses(components, boardBounds, issues),
111418
+ issues
111419
+ };
111420
+ };
110754
111421
  var analyzeAllPlacements = (circuitJson) => {
110755
111422
  const componentNames = [];
110756
111423
  const seenNames = /* @__PURE__ */ new Set;
@@ -110767,10 +111434,12 @@ var analyzeAllPlacements = (circuitJson) => {
110767
111434
  analysis: analyzeComponentPlacement(circuitJson, componentName)
110768
111435
  }));
110769
111436
  const lineItems = analyses.flatMap(({ analysis }) => analysis.getLineItems());
111437
+ const report = buildPlacementAnalysisReport(circuitJson);
110770
111438
  return {
110771
111439
  getLineItems: () => lineItems,
110772
- getString: () => analyses.map(({ analysis }) => analysis.getString()).join(`
110773
- `)
111440
+ getString: () => formatPlacementAnalysisReport(report),
111441
+ getIssues: () => report.issues,
111442
+ getReport: () => report
110774
111443
  };
110775
111444
  };
110776
111445
 
@@ -110785,8 +111454,10 @@ var checkPlacement = async (file, refdes) => {
110785
111454
  },
110786
111455
  allowPrebuiltCircuitJson: true
110787
111456
  });
110788
- const analysis = refdes ? analyzeComponentPlacement(circuitJson, refdes) : analyzeAllPlacements(circuitJson);
110789
- return analysis.getString();
111457
+ if (refdes) {
111458
+ return analyzeComponentPlacement(circuitJson, refdes).getString();
111459
+ }
111460
+ return analyzeAllPlacements(circuitJson).getString();
110790
111461
  };
110791
111462
  var registerCheckPlacement = (program2) => {
110792
111463
  program2.commands.find((c) => c.name() === "check").command("placement").description("Partially build and validate the placement").argument("[file]", "Path to the entry file").argument("[refdes]", "Optional refdes to scope the check").action(async (file, refdes) => {
@@ -133568,7 +134239,7 @@ var getDirectionalFreeSpace = (spatialIndex, componentIndex, directions) => {
133568
134239
  }
133569
134240
  return freeSpaceByDirection;
133570
134241
  };
133571
- var fmtMm2 = (value) => `${value.toFixed(1)}mm`;
134242
+ var fmtMm3 = (value) => `${value.toFixed(1)}mm`;
133572
134243
  var roundsToZeroMm = (value) => Math.abs(value) < 0.05;
133573
134244
  var toNumber3 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
133574
134245
  var getPcbComponentBounds = (element) => {
@@ -133612,10 +134283,10 @@ var getPlacedComponents = (circuitJson) => {
133612
134283
  }
133613
134284
  return placedComponents;
133614
134285
  };
133615
- var getOffsetFromTopOfRegion = (componentBounds, regionBounds) => fmtMm2(regionBounds.maxY - componentBounds.maxY);
133616
- var getOffsetFromBottomOfRegion = (componentBounds, regionBounds) => fmtMm2(componentBounds.minY - regionBounds.minY);
133617
- var getOffsetFromLeftOfRegion = (componentBounds, regionBounds) => fmtMm2(componentBounds.minX - regionBounds.minX);
133618
- var getOffsetFromRightOfRegion = (componentBounds, regionBounds) => fmtMm2(regionBounds.maxX - componentBounds.maxX);
134286
+ var getOffsetFromTopOfRegion = (componentBounds, regionBounds) => fmtMm3(regionBounds.maxY - componentBounds.maxY);
134287
+ var getOffsetFromBottomOfRegion = (componentBounds, regionBounds) => fmtMm3(componentBounds.minY - regionBounds.minY);
134288
+ var getOffsetFromLeftOfRegion = (componentBounds, regionBounds) => fmtMm3(componentBounds.minX - regionBounds.minX);
134289
+ var getOffsetFromRightOfRegion = (componentBounds, regionBounds) => fmtMm3(regionBounds.maxX - componentBounds.maxX);
133619
134290
  var addEdgeAlignedOffsets = (nearbyComponent, componentBounds, regionBounds) => {
133620
134291
  if (nearbyComponent.onLeftEdgeOfRegion || nearbyComponent.onRightEdgeOfRegion) {
133621
134292
  nearbyComponent.distToTopOfRegion = getOffsetFromTopOfRegion(componentBounds, regionBounds);
@@ -133747,28 +134418,28 @@ var getOutsideComponentCandidate = (component, componentIndex, regionBounds) =>
133747
134418
  if (roundsToZeroMm(candidate.distance)) {
133748
134419
  nearbyComponentCandidate.onLeftEdgeOfRegion = true;
133749
134420
  } else {
133750
- nearbyComponentCandidate.distToLeftEdgeOfRegion = fmtMm2(candidate.distance);
134421
+ nearbyComponentCandidate.distToLeftEdgeOfRegion = fmtMm3(candidate.distance);
133751
134422
  }
133752
134423
  break;
133753
134424
  case "right":
133754
134425
  if (roundsToZeroMm(candidate.distance)) {
133755
134426
  nearbyComponentCandidate.onRightEdgeOfRegion = true;
133756
134427
  } else {
133757
- nearbyComponentCandidate.distToRightEdgeOfRegion = fmtMm2(candidate.distance);
134428
+ nearbyComponentCandidate.distToRightEdgeOfRegion = fmtMm3(candidate.distance);
133758
134429
  }
133759
134430
  break;
133760
134431
  case "top":
133761
134432
  if (roundsToZeroMm(candidate.distance)) {
133762
134433
  nearbyComponentCandidate.onTopEdgeOfRegion = true;
133763
134434
  } else {
133764
- nearbyComponentCandidate.distToTopOfRegion = fmtMm2(candidate.distance);
134435
+ nearbyComponentCandidate.distToTopOfRegion = fmtMm3(candidate.distance);
133765
134436
  }
133766
134437
  break;
133767
134438
  case "bottom":
133768
134439
  if (roundsToZeroMm(candidate.distance)) {
133769
134440
  nearbyComponentCandidate.onBottomEdgeOfRegion = true;
133770
134441
  } else {
133771
- nearbyComponentCandidate.distToBottomOfRegion = fmtMm2(candidate.distance);
134442
+ nearbyComponentCandidate.distToBottomOfRegion = fmtMm3(candidate.distance);
133772
134443
  }
133773
134444
  break;
133774
134445
  }
@@ -168821,7 +169492,7 @@ function normalizeDegrees(angle) {
168821
169492
  var doBoundsOverlap = (bounds1, bounds2) => {
168822
169493
  return !(bounds1.maxX < bounds2.minX || bounds2.maxX < bounds1.minX || bounds1.maxY < bounds2.minY || bounds2.maxY < bounds1.minY);
168823
169494
  };
168824
- var getBoundsFromPoints = (points) => {
169495
+ var getBoundsFromPoints2 = (points) => {
168825
169496
  if (points.length === 0) {
168826
169497
  return null;
168827
169498
  }
@@ -198646,8 +199317,8 @@ function checkCourtyardOverlap(circuitJson) {
198646
199317
  for (const b22 of byComponent.get(idB)) {
198647
199318
  const polyA = getCourtyardPolygon(a2);
198648
199319
  const polyB = getCourtyardPolygon(b22);
198649
- const boundsA = getBoundsFromPoints(polyA);
198650
- const boundsB = getBoundsFromPoints(polyB);
199320
+ const boundsA = getBoundsFromPoints2(polyA);
199321
+ const boundsB = getBoundsFromPoints2(polyB);
198651
199322
  if (!boundsA || !boundsB)
198652
199323
  continue;
198653
199324
  if (areBoundsOverlappingPolygon(boundsA, polyB) || areBoundsOverlappingPolygon(boundsB, polyA)) {
@@ -219679,7 +220350,7 @@ var CourtyardOutline = class extends PrimitiveComponent2 {
219679
220350
  if (!props.outline || props.outline.length === 0) {
219680
220351
  return { width: 0, height: 0 };
219681
220352
  }
219682
- const bounds = getBoundsFromPoints(props.outline);
220353
+ const bounds = getBoundsFromPoints2(props.outline);
219683
220354
  if (!bounds) {
219684
220355
  return { width: 0, height: 0 };
219685
220356
  }
@@ -223588,7 +224259,7 @@ var NormalComponent__getMinimumFlexContainerSize = (component) => {
223588
224259
  if (!pcbGroup)
223589
224260
  return null;
223590
224261
  if (pcbGroup.outline && pcbGroup.outline.length > 0) {
223591
- const bounds = getBoundsFromPoints(pcbGroup.outline);
224262
+ const bounds = getBoundsFromPoints2(pcbGroup.outline);
223592
224263
  if (!bounds)
223593
224264
  return null;
223594
224265
  return {
@@ -226857,7 +227528,7 @@ function Group_doInitialPcbComponentAnchorAlignment(group) {
226857
227528
  let height = pcbGroup.height;
226858
227529
  const { center: center2 } = pcbGroup;
226859
227530
  if (pcbGroup.outline && pcbGroup.outline.length > 0) {
226860
- const bounds2 = getBoundsFromPoints(pcbGroup.outline);
227531
+ const bounds2 = getBoundsFromPoints2(pcbGroup.outline);
226861
227532
  if (bounds2) {
226862
227533
  width = bounds2.maxX - bounds2.minX;
226863
227534
  height = bounds2.maxY - bounds2.minY;
@@ -230167,7 +230838,7 @@ var Group6 = class extends NormalComponent3 {
230167
230838
  x: distance.parse(point6.x),
230168
230839
  y: distance.parse(point6.y)
230169
230840
  }));
230170
- const outlineBounds = getBoundsFromPoints(numericOutline);
230841
+ const outlineBounds = getBoundsFromPoints2(numericOutline);
230171
230842
  if (!outlineBounds)
230172
230843
  return;
230173
230844
  const centerX2 = (outlineBounds.minX + outlineBounds.maxX) / 2;
@@ -232099,7 +232770,7 @@ var Board = class extends Group6 {
232099
232770
  let width = dbBoard?.width ?? props.width;
232100
232771
  let height = dbBoard?.height ?? props.height;
232101
232772
  if ((width == null || height == null) && props.outline?.length) {
232102
- const outlineBounds = getBoundsFromPoints(props.outline);
232773
+ const outlineBounds = getBoundsFromPoints2(props.outline);
232103
232774
  if (outlineBounds) {
232104
232775
  width ??= outlineBounds.maxX - outlineBounds.minX;
232105
232776
  height ??= outlineBounds.maxY - outlineBounds.minY;
@@ -232155,7 +232826,7 @@ var Board = class extends Group6 {
232155
232826
  let width = pcbGroup.width ?? 0;
232156
232827
  let height = pcbGroup.height ?? 0;
232157
232828
  if (pcbGroup.outline && pcbGroup.outline.length > 0) {
232158
- const bounds = getBoundsFromPoints(pcbGroup.outline);
232829
+ const bounds = getBoundsFromPoints2(pcbGroup.outline);
232159
232830
  if (bounds) {
232160
232831
  width = bounds.maxX - bounds.minX;
232161
232832
  height = bounds.maxY - bounds.minY;
@@ -232306,7 +232977,7 @@ var Board = class extends Group6 {
232306
232977
  }
232307
232978
  let outlineTranslation = { x: 0, y: 0 };
232308
232979
  if (outline && outline.length > 0 && (this.parent?.lowercaseComponentName === "panel" || this.parent?.lowercaseComponentName === "subpanel")) {
232309
- const outlineBounds = getBoundsFromPoints(outline);
232980
+ const outlineBounds = getBoundsFromPoints2(outline);
232310
232981
  if (outlineBounds) {
232311
232982
  const outlineCenterX = (outlineBounds.minX + outlineBounds.maxX) / 2;
232312
232983
  const outlineCenterY = (outlineBounds.minY + outlineBounds.maxY) / 2;
@@ -232421,7 +233092,7 @@ var Board = class extends Group6 {
232421
233092
  if (this.pcb_board_id) {
232422
233093
  db.pcb_board.update(this.pcb_board_id, { center: position2 });
232423
233094
  if (pcbBoard?.outline) {
232424
- const outlineBounds = getBoundsFromPoints(pcbBoard.outline);
233095
+ const outlineBounds = getBoundsFromPoints2(pcbBoard.outline);
232425
233096
  if (outlineBounds) {
232426
233097
  const oldOutlineCenter = {
232427
233098
  x: (outlineBounds.minX + outlineBounds.maxX) / 2,
@@ -232724,7 +233395,7 @@ var getBoardDimensionsFromProps = (board) => {
232724
233395
  let width = props.width != null ? distance.parse(props.width) : undefined;
232725
233396
  let height = props.height != null ? distance.parse(props.height) : undefined;
232726
233397
  if ((width === undefined || height === undefined) && props.outline?.length) {
232727
- const outlineBounds = getBoundsFromPoints(props.outline);
233398
+ const outlineBounds = getBoundsFromPoints2(props.outline);
232728
233399
  if (outlineBounds) {
232729
233400
  width ??= outlineBounds.maxX - outlineBounds.minX;
232730
233401
  height ??= outlineBounds.maxY - outlineBounds.minY;
@@ -236890,13 +237561,13 @@ var solveForGlobalCapacityNodes = async (circuitJson) => {
236890
237561
  await solver.solveUntilPhase("highDensityRouteSolver");
236891
237562
  return solver.uniformPortDistributionSolver?.getOutput() ?? [];
236892
237563
  };
236893
- var fmtNumber2 = (value) => {
237564
+ var fmtNumber3 = (value) => {
236894
237565
  if (Number.isInteger(value))
236895
237566
  return String(value);
236896
237567
  return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
236897
237568
  };
236898
237569
  var fmtMm22 = (value) => `${value.toFixed(1)}mm`;
236899
- var fmtPercent = (value) => `${fmtNumber2(value * 100)}%`;
237570
+ var fmtPercent = (value) => `${fmtNumber3(value * 100)}%`;
236900
237571
  var isCrampedPortPoint = (portPointId) => portPointId?.includes("_cramped") ?? false;
236901
237572
  var clamp012 = (value) => Math.max(0, Math.min(1, value));
236902
237573
  var roundProbability = (value) => Number.parseFloat(value.toFixed(3));
package/dist/lib/index.js CHANGED
@@ -60678,7 +60678,7 @@ var getNodeHandler = (winterSpec, { port, middleware = [] }) => {
60678
60678
  }));
60679
60679
  };
60680
60680
  // package.json
60681
- var version = "0.1.1173";
60681
+ var version = "0.1.1175";
60682
60682
  var package_default = {
60683
60683
  name: "@tscircuit/cli",
60684
60684
  version,
@@ -60690,7 +60690,7 @@ var package_default = {
60690
60690
  devDependencies: {
60691
60691
  "@babel/standalone": "^7.26.9",
60692
60692
  "@biomejs/biome": "^1.9.4",
60693
- "@tscircuit/circuit-json-placement-analysis": "^0.0.4",
60693
+ "@tscircuit/circuit-json-placement-analysis": "^0.0.5",
60694
60694
  "@tscircuit/circuit-json-routing-analysis": "^0.0.1",
60695
60695
  "@tscircuit/fake-snippets": "^0.0.182",
60696
60696
  "@tscircuit/file-server": "^0.0.32",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/cli",
3
- "version": "0.1.1175",
3
+ "version": "0.1.1176",
4
4
  "main": "dist/cli/main.js",
5
5
  "exports": {
6
6
  ".": "./dist/cli/main.js",
@@ -9,7 +9,7 @@
9
9
  "devDependencies": {
10
10
  "@babel/standalone": "^7.26.9",
11
11
  "@biomejs/biome": "^1.9.4",
12
- "@tscircuit/circuit-json-placement-analysis": "^0.0.4",
12
+ "@tscircuit/circuit-json-placement-analysis": "^0.0.5",
13
13
  "@tscircuit/circuit-json-routing-analysis": "^0.0.1",
14
14
  "@tscircuit/fake-snippets": "^0.0.182",
15
15
  "@tscircuit/file-server": "^0.0.32",