@tscircuit/schematic-trace-solver 0.0.149 → 0.0.150

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.
Files changed (158) hide show
  1. package/.agents/skills/schematic-trace-visualization/SKILL.md +7 -2
  2. package/dist/index.d.ts +28 -1
  3. package/dist/index.js +26 -4
  4. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +14 -4
  5. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +17 -1
  6. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +2 -0
  7. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +2 -0
  8. package/lib/types/InputProblem.ts +27 -1
  9. package/package.json +8 -1
  10. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +751 -5
  11. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +660 -5
  12. package/tests/bug-reports/bug-report-20260707T020342Z/__snapshots__/bug-report-20260707T020342Z.snap.svg +44 -5
  13. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +394 -5
  14. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +170 -5
  15. package/tests/bug-reports/bug-report-20260707T134722Z/__snapshots__/bug-report-20260707T134722Z.snap.svg +79 -5
  16. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +219 -5
  17. package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +65 -5
  18. package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +142 -5
  19. package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +100 -5
  20. package/tests/bug-reports/bug-report-20260708T053736Z/__snapshots__/bug-report-20260708T053736Z.snap.svg +23 -5
  21. package/tests/bug-reports/bug-report-20260708T055430Z/__snapshots__/bug-report-20260708T055430Z.snap.svg +58 -5
  22. package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +86 -5
  23. package/tests/bug-reports/bug-report-20260716T144856Z/__snapshots__/bug-report-20260716T144856Z.snap.svg +226 -5
  24. package/tests/bug-reports/bug-report-20260717T022934Z/__snapshots__/bug-report-20260717T022934Z.snap.svg +191 -5
  25. package/tests/bug-reports/bug-report-20260717T031704Z/__snapshots__/bug-report-20260717T031704Z.snap.svg +30 -5
  26. package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +37 -5
  27. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +580 -2
  28. package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +44 -5
  29. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +107 -5
  30. package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +114 -5
  31. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +121 -5
  32. package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +51 -5
  33. package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +212 -5
  34. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +569 -5
  35. package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +37 -5
  36. package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +289 -5
  37. package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +289 -5
  38. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +149 -5
  39. package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +93 -5
  40. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +79 -5
  41. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +244 -2
  42. package/tests/examples/__snapshots__/example01.snap.svg +44 -5
  43. package/tests/examples/__snapshots__/example02.snap.svg +51 -5
  44. package/tests/examples/__snapshots__/example03.snap.svg +86 -5
  45. package/tests/examples/__snapshots__/example04.snap.svg +30 -5
  46. package/tests/examples/__snapshots__/example05.snap.svg +30 -5
  47. package/tests/examples/__snapshots__/example06.snap.svg +30 -5
  48. package/tests/examples/__snapshots__/example07.snap.svg +58 -5
  49. package/tests/examples/__snapshots__/example08.snap.svg +37 -5
  50. package/tests/examples/__snapshots__/example09.snap.svg +149 -5
  51. package/tests/examples/__snapshots__/example10.snap.svg +65 -5
  52. package/tests/examples/__snapshots__/example11.snap.svg +51 -5
  53. package/tests/examples/__snapshots__/example12.snap.svg +51 -5
  54. package/tests/examples/__snapshots__/example13.snap.svg +72 -5
  55. package/tests/examples/__snapshots__/example14.snap.svg +79 -5
  56. package/tests/examples/__snapshots__/example15.snap.svg +86 -5
  57. package/tests/examples/__snapshots__/example16.snap.svg +51 -5
  58. package/tests/examples/__snapshots__/example17.snap.svg +51 -5
  59. package/tests/examples/__snapshots__/example18.snap.svg +58 -5
  60. package/tests/examples/__snapshots__/example19.snap.svg +44 -5
  61. package/tests/examples/__snapshots__/example20.snap.svg +44 -5
  62. package/tests/examples/__snapshots__/example21.snap.svg +72 -5
  63. package/tests/examples/__snapshots__/example22.snap.svg +51 -5
  64. package/tests/examples/__snapshots__/example23.snap.svg +51 -5
  65. package/tests/examples/__snapshots__/example24.snap.svg +51 -5
  66. package/tests/examples/__snapshots__/example25.snap.svg +86 -5
  67. package/tests/examples/__snapshots__/example26.snap.svg +51 -5
  68. package/tests/examples/__snapshots__/example27.snap.svg +72 -5
  69. package/tests/examples/__snapshots__/example28.snap.svg +44 -5
  70. package/tests/examples/__snapshots__/example29.snap.svg +324 -5
  71. package/tests/examples/__snapshots__/example30.snap.svg +107 -5
  72. package/tests/examples/__snapshots__/example31.snap.svg +37 -5
  73. package/tests/examples/__snapshots__/example32.snap.svg +149 -5
  74. package/tests/examples/__snapshots__/example33.snap.svg +93 -5
  75. package/tests/examples/__snapshots__/example34.snap.svg +51 -5
  76. package/tests/examples/__snapshots__/example35.snap.svg +58 -5
  77. package/tests/examples/__snapshots__/example36.snap.svg +44 -5
  78. package/tests/examples/__snapshots__/example37.snap.svg +72 -5
  79. package/tests/examples/__snapshots__/example38.snap.svg +30 -5
  80. package/tests/examples/__snapshots__/example39.snap.svg +37 -5
  81. package/tests/examples/__snapshots__/example40.snap.svg +100 -5
  82. package/tests/examples/__snapshots__/example41.snap.svg +44 -5
  83. package/tests/examples/__snapshots__/example42.snap.svg +65 -5
  84. package/tests/examples/__snapshots__/example43.snap.svg +58 -5
  85. package/tests/examples/__snapshots__/example44.snap.svg +107 -5
  86. package/tests/examples/__snapshots__/example45.snap.svg +51 -5
  87. package/tests/examples/__snapshots__/example46.snap.svg +93 -5
  88. package/tests/examples/__snapshots__/example47.snap.svg +93 -5
  89. package/tests/examples/__snapshots__/example48.snap.svg +65 -5
  90. package/tests/examples/__snapshots__/example49.snap.svg +177 -5
  91. package/tests/examples/__snapshots__/example50.snap.svg +93 -5
  92. package/tests/examples/__snapshots__/example51.snap.svg +590 -5
  93. package/tests/fixtures/convertSolverOutputToCircuitJson.test.ts +219 -0
  94. package/tests/fixtures/convertSolverOutputToCircuitJson.ts +1058 -0
  95. package/tests/fixtures/matcher.ts +45 -8
  96. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +762 -2
  97. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +111 -2
  98. package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +167 -2
  99. package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +65 -5
  100. package/tests/repros/__snapshots__/manufacturePartNumber-text-box.snap.svg +30 -5
  101. package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +79 -5
  102. package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +65 -5
  103. package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +149 -5
  104. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +55 -2
  105. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +160 -2
  106. package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +135 -5
  107. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +69 -2
  108. package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +86 -5
  109. package/tests/repros/__snapshots__/repro-core-555-power-cross-net-overlap.snap.svg +44 -5
  110. package/tests/repros/__snapshots__/repro-core-subcircuit-missing-ground.snap.svg +44 -5
  111. package/tests/repros/__snapshots__/repro-core-tb67s579ftg-inline-label-routing.snap.svg +30 -5
  112. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +335 -2
  113. package/tests/repros/__snapshots__/repro-ground-label-on-lower-rail-end.snap.svg +48 -2
  114. package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +149 -5
  115. package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +72 -5
  116. package/tests/repros/__snapshots__/repro-netlabel-collision-687.snap.svg +100 -5
  117. package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +51 -5
  118. package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +90 -2
  119. package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +37 -5
  120. package/tests/repros/__snapshots__/repro-rectifier-trace-overlap.snap.svg +44 -5
  121. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +237 -2
  122. package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +72 -5
  123. package/tests/repros/__snapshots__/repro-rp2040-v3v3-label-placement.snap.svg +51 -5
  124. package/tests/repros/__snapshots__/repro-rp2040-zero-crystal-fallback-netlabels.snap.svg +30 -5
  125. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +79 -5
  126. package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +27 -2
  127. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +1150 -5
  128. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +51 -5
  129. package/tests/repros/__snapshots__/repro-type-c-16pin-self-connection.snap.svg +30 -5
  130. package/tests/repros/__snapshots__/repro-type-c-16pin-to-resistor.snap.svg +30 -5
  131. package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +44 -5
  132. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +685 -2
  133. package/tests/repros/__snapshots__/repro-vcc-pin1-detour.snap.svg +27 -2
  134. package/tests/repros/__snapshots__/repro129-host-custom-symbol-passives.snap.svg +51 -5
  135. package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +100 -5
  136. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +107 -5
  137. package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +51 -5
  138. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +104 -2
  139. package/tests/repros/__snapshots__/repro5-escape-padded-text-obstacles.snap.svg +30 -5
  140. package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +65 -5
  141. package/tests/repros/__snapshots__/repro84-stacked-gnd-label-orientation.snap.svg +27 -2
  142. package/tests/repros/__snapshots__/small-variant-resistor-facing-direction.snap.svg +58 -5
  143. package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +37 -5
  144. package/tests/solvers/InlineNetLabelSolver/__snapshots__/fallback-net-label-width.snap.svg +25 -7
  145. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-anchored-label-clearance.snap.svg +34 -2
  146. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-port-stub.snap.svg +23 -5
  147. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-two-pin-net.snap.svg +23 -5
  148. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +23 -5
  149. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +23 -5
  150. package/tests/solvers/InlineNetLabelSolver/__snapshots__/terminal-stub-trace-clearance.snap.svg +23 -5
  151. package/tests/solvers/InlineNetLabelSolver/fallback-net-label-width.test.ts +10 -1
  152. package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.snap.svg +23 -5
  153. package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_repro47-short-opposing-pins.snap.svg +20 -2
  154. package/tests/solvers/TraceCleanupSolver/__snapshots__/TraceCleanupSolver.snap.svg +44 -5
  155. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +79 -5
  156. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +44 -5
  157. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +23 -5
  158. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +30 -5
@@ -48,7 +48,8 @@ const png = await getPngBufferFromGraphicsObject(graphics, {
48
48
 
49
49
  ## Existing Repo Helpers
50
50
 
51
- - `tests/fixtures/matcher.ts`: `expect(solver).toMatchSolverSnapshot(import.meta.path)` converts `solver.visualize()` to SVG and snapshots it.
51
+ - `tests/fixtures/matcher.ts`: `expect(solver).toMatchSolverSnapshot(import.meta.path)` converts the solver's semantic output to Circuit JSON and renders it with `circuit-to-svg`. These snapshots hide the input rats nest by default.
52
+ - `tests/fixtures/convertSolverOutputToCircuitJson.ts`: builds snapshot Circuit JSON with schematic components/symbols, ports, routed traces, net labels, text, and boxes.
52
53
  - `tests/fixtures/getLastStepGraphicsObject.ts`: filters a `GraphicsObject` to its highest numbered `step`.
53
54
  - `tests/fixtures/getLastStepSvg.ts`: converts only the last step to SVG.
54
55
  - `lib/testing/PipelineStageDebugRunner.ts`: runs `SchematicTracePipelineSolver` and writes per-stage PNG/SVG/graphics JSON artifacts.
@@ -56,7 +57,8 @@ const png = await getPngBufferFromGraphicsObject(graphics, {
56
57
 
57
58
  ## Minimal SVG Tests
58
59
 
59
- Prefer focused solver SVG snapshots over broad dumps:
60
+ Prefer focused solver SVG snapshots over broad dumps. Solver snapshots use the
61
+ semantic Circuit JSON renderer by default:
60
62
 
61
63
  ```ts
62
64
  const solver = new SchematicTracePipelineSolver(inputProblem as any)
@@ -64,6 +66,9 @@ solver.solve()
64
66
  expect(solver).toMatchSolverSnapshot(import.meta.path)
65
67
  ```
66
68
 
69
+ Use `solver.visualize()` with `getSvgFromGraphicsObject` directly when a test is
70
+ specifically about internal candidates, failed geometry, or staged debug state.
71
+
67
72
  For multi-step graphics where only the final stage matters:
68
73
 
69
74
  ```ts
package/dist/index.d.ts CHANGED
@@ -75,6 +75,13 @@ interface TextBoxes {
75
75
  }
76
76
  interface InputChip {
77
77
  chipId: ChipId;
78
+ /**
79
+ * Exact directional/oriented schematic-symbol name when the caller has it.
80
+ * `center`, `width`, and `height` describe the solver obstacle and may be
81
+ * expanded to include reference/value or manufacturer-part-number text, so
82
+ * renderers should use this symbol's geometry for the component body.
83
+ */
84
+ symbolName?: string;
78
85
  center: {
79
86
  x: number;
80
87
  y: number;
@@ -87,6 +94,13 @@ interface InputChip {
87
94
  interface InputDirectConnection {
88
95
  pinIds: [PinId, PinId];
89
96
  netId?: string;
97
+ /**
98
+ * User-facing text to render for this net label. `netId` remains the stable
99
+ * connectivity identifier and may be an internal id. When this is omitted
100
+ * but a label width is provided, renderers should use a width-preserving
101
+ * placeholder instead of exposing `netId` as label text.
102
+ */
103
+ netLabelText?: string;
90
104
  netLabelWidth?: number;
91
105
  /**
92
106
  * Width of the conventional anchored label to use only when an inline label
@@ -109,7 +123,8 @@ interface InputDirectConnection {
109
123
  allowInlineNetLabel?: boolean;
110
124
  /**
111
125
  * Extent of the inline net label along the trace. Falls back to
112
- * `netLabelWidth`, then to an estimate from the netId text.
126
+ * `netLabelWidth`, then to an estimate from `netLabelText` (or `netId` for
127
+ * backwards compatibility).
113
128
  */
114
129
  inlineNetLabelWidth?: number;
115
130
  /**
@@ -121,6 +136,13 @@ interface InputDirectConnection {
121
136
  interface InputNetConnection {
122
137
  netId: string;
123
138
  pinIds: Array<PinId>;
139
+ /**
140
+ * User-facing text to render for this net label. `netId` remains the stable
141
+ * connectivity identifier and may be an internal id. When this is omitted
142
+ * but a label width is provided, renderers should use a width-preserving
143
+ * placeholder instead of exposing `netId` as label text.
144
+ */
145
+ netLabelText?: string;
124
146
  netLabelWidth?: number;
125
147
  fallbackNetLabelWidth?: number;
126
148
  netLabelHeight?: number;
@@ -425,6 +447,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
425
447
  type OverlappingSameNetTraceGroup = {
426
448
  globalConnNetId: string;
427
449
  netId?: string;
450
+ netLabelText?: string;
428
451
  overlappingTraces?: SolvedTracePath;
429
452
  portOnlyPinId?: string;
430
453
  mspConnectionPairIds?: MspConnectionPairId[];
@@ -436,6 +459,8 @@ interface NetLabelPlacement {
436
459
  * Optional user-provided net identifier (if present in the input problem).
437
460
  */
438
461
  netId?: string;
462
+ /** User-facing label content, separate from the connectivity identifier. */
463
+ netLabelText?: string;
439
464
  /**
440
465
  * MSP pair ids that the label is associated with. Port-only labels use [].
441
466
  */
@@ -1209,6 +1234,8 @@ declare const INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4;
1209
1234
  interface InlineNetLabelPlacement {
1210
1235
  globalConnNetId: string;
1211
1236
  netId?: string;
1237
+ /** User-facing label content, separate from the connectivity identifier. */
1238
+ netLabelText?: string;
1212
1239
  mspPairId?: string;
1213
1240
  pinIds: PinId[];
1214
1241
  /**
package/dist/index.js CHANGED
@@ -2742,6 +2742,7 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
2742
2742
  globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
2743
2743
  dcConnNetId: void 0,
2744
2744
  netId: overlappingSameNetTraceGroup.netId,
2745
+ netLabelText: overlappingSameNetTraceGroup.netLabelText,
2745
2746
  mspConnectionPairIds: [],
2746
2747
  pinIds: [pinId],
2747
2748
  orientation,
@@ -2773,6 +2774,7 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
2773
2774
  globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
2774
2775
  dcConnNetId: void 0,
2775
2776
  netId: overlappingSameNetTraceGroup.netId,
2777
+ netLabelText: overlappingSameNetTraceGroup.netLabelText,
2776
2778
  mspConnectionPairIds: [],
2777
2779
  pinIds: [pinId],
2778
2780
  orientation: fallbackOrientation,
@@ -3059,6 +3061,7 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
3059
3061
  globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
3060
3062
  dcConnNetId: curr.dcConnNetId,
3061
3063
  netId: this.overlappingSameNetTraceGroup.netId,
3064
+ netLabelText: this.overlappingSameNetTraceGroup.netLabelText,
3062
3065
  mspConnectionPairIds: [curr.mspPairId],
3063
3066
  pinIds: [curr.pins[0].pinId, curr.pins[1].pinId],
3064
3067
  orientation,
@@ -3078,6 +3081,7 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
3078
3081
  globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
3079
3082
  dcConnNetId: bestCandidate.dcConnNetId,
3080
3083
  netId: this.overlappingSameNetTraceGroup.netId,
3084
+ netLabelText: this.overlappingSameNetTraceGroup.netLabelText,
3081
3085
  mspConnectionPairIds: [bestCandidate.mspPairId],
3082
3086
  pinIds: bestCandidate.pinIds,
3083
3087
  orientation: bestCandidate.orientation,
@@ -3133,16 +3137,25 @@ var NetLabelPlacementSolver = class extends BaseSolver {
3133
3137
  }
3134
3138
  }
3135
3139
  const userNetIdByPinId = {};
3140
+ const netLabelTextByPinId = {};
3136
3141
  for (const dc of this.inputProblem.directConnections) {
3137
3142
  if (dc.netId) {
3138
3143
  const [a, b] = dc.pinIds;
3139
3144
  userNetIdByPinId[a] = dc.netId;
3140
3145
  userNetIdByPinId[b] = dc.netId;
3141
3146
  }
3147
+ const netLabelText = dc.netLabelText?.trim();
3148
+ if (netLabelText) {
3149
+ const [a, b] = dc.pinIds;
3150
+ netLabelTextByPinId[a] = netLabelText;
3151
+ netLabelTextByPinId[b] = netLabelText;
3152
+ }
3142
3153
  }
3143
3154
  for (const nc of this.inputProblem.netConnections) {
3144
3155
  for (const pid of nc.pinIds) {
3145
3156
  userNetIdByPinId[pid] = nc.netId;
3157
+ const netLabelText = nc.netLabelText?.trim();
3158
+ if (netLabelText) netLabelTextByPinId[pid] = netLabelText;
3146
3159
  }
3147
3160
  }
3148
3161
  const groups = [];
@@ -3228,6 +3241,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
3228
3241
  const group = {
3229
3242
  globalConnNetId,
3230
3243
  netId: userNetId,
3244
+ netLabelText: [...component].map((pinId) => netLabelTextByPinId[pinId]).find((text) => Boolean(text)),
3231
3245
  overlappingTraces: rep,
3232
3246
  mspConnectionPairIds
3233
3247
  };
@@ -3239,6 +3253,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
3239
3253
  groups.push({
3240
3254
  globalConnNetId,
3241
3255
  netId: userNetId,
3256
+ netLabelText: netLabelTextByPinId[p],
3242
3257
  portOnlyPinId: p
3243
3258
  });
3244
3259
  }
@@ -13326,7 +13341,8 @@ var InlineNetLabelSolver = class extends BaseSolver {
13326
13341
  );
13327
13342
  const inputPin = inputChip?.pins.find((pin) => pin.pinId === pinId);
13328
13343
  const height = connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
13329
- const width = connection.inlineNetLabelWidth ?? connection.netLabelWidth ?? estimateInlineNetLabelWidth(connection.netId, height);
13344
+ const netLabelText = connection.netLabelText?.trim() || void 0;
13345
+ const width = connection.inlineNetLabelWidth ?? connection.netLabelWidth ?? estimateInlineNetLabelWidth(netLabelText ?? connection.netId, height);
13330
13346
  const stubLength = Math.max(width + 0.2, 0.6);
13331
13347
  const start = inputPin ? { x: inputPin.x, y: inputPin.y } : anchoredPlacement.anchorPoint;
13332
13348
  const direction = inputPin && inputChip ? inputPin._facingDirection ?? getPinDirection(inputPin, inputChip) : anchoredPlacement.orientation;
@@ -13368,6 +13384,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
13368
13384
  return {
13369
13385
  globalConnNetId: anchoredPlacement.globalConnNetId,
13370
13386
  netId: connection.netId,
13387
+ netLabelText,
13371
13388
  pinIds: [pinId],
13372
13389
  stubTracePath: [start, end],
13373
13390
  axis,
@@ -13385,7 +13402,8 @@ var InlineNetLabelSolver = class extends BaseSolver {
13385
13402
  const segments = getAxisAlignedSegments(trace.tracePath);
13386
13403
  if (segments.length === 0) return null;
13387
13404
  const height = connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
13388
- const width = connection.inlineNetLabelWidth ?? connection.netLabelWidth ?? estimateInlineNetLabelWidth(connection.netId, height);
13405
+ const netLabelText = connection.netLabelText?.trim() || void 0;
13406
+ const width = connection.inlineNetLabelWidth ?? connection.netLabelWidth ?? estimateInlineNetLabelWidth(netLabelText ?? connection.netId, height);
13389
13407
  const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN;
13390
13408
  const usableSegments = segments.filter((segment) => segment.length >= width).sort((a, b) => b.length - a.length);
13391
13409
  for (const segment of usableSegments) {
@@ -13414,6 +13432,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
13414
13432
  return {
13415
13433
  globalConnNetId: trace.globalConnNetId,
13416
13434
  netId: connection.netId,
13435
+ netLabelText,
13417
13436
  mspPairId: trace.mspPairId,
13418
13437
  pinIds: [...connection.pinIds],
13419
13438
  axis: segment.axis,
@@ -13429,6 +13448,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
13429
13448
  const spanPlacement = this.computeSpanPlacement({
13430
13449
  trace,
13431
13450
  connection,
13451
+ netLabelText,
13432
13452
  width,
13433
13453
  height,
13434
13454
  offset
@@ -13445,6 +13465,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
13445
13465
  computeSpanPlacement({
13446
13466
  trace,
13447
13467
  connection,
13468
+ netLabelText,
13448
13469
  width,
13449
13470
  height,
13450
13471
  offset
@@ -13504,6 +13525,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
13504
13525
  return {
13505
13526
  globalConnNetId: trace.globalConnNetId,
13506
13527
  netId: connection.netId,
13528
+ netLabelText,
13507
13529
  mspPairId: trace.mspPairId,
13508
13530
  pinIds: [...connection.pinIds],
13509
13531
  axis,
@@ -13638,7 +13660,7 @@ orientation: ${label.orientation}`
13638
13660
  graphics.texts.push({
13639
13661
  x: inlineLabel.stubTracePath && inlineLabel.axis === "x" ? inlineLabel.center.x + (inlineLabel.stubTracePath[1].x > inlineLabel.stubTracePath[0].x ? -inlineLabel.width / 2 : inlineLabel.width / 2) : inlineLabel.center.x,
13640
13662
  y: inlineLabel.stubTracePath && inlineLabel.axis === "y" ? inlineLabel.center.y + (inlineLabel.stubTracePath[1].y > inlineLabel.stubTracePath[0].y ? -inlineLabel.width / 2 : inlineLabel.width / 2) : inlineLabel.center.y,
13641
- text: inlineLabel.netId ?? "",
13663
+ text: inlineLabel.netLabelText ?? inlineLabel.netId ?? "",
13642
13664
  color: "green",
13643
13665
  fontSize: inlineLabel.height,
13644
13666
  anchorSide: inlineLabel.stubTracePath ? inlineLabel.stubTracePath[1][inlineLabel.axis] > inlineLabel.stubTracePath[0][inlineLabel.axis] ? "center_left" : "center_right" : "center",
@@ -13650,7 +13672,7 @@ orientation: ${label.orientation}`
13650
13672
  y: inlineLabel.anchorPoint.y,
13651
13673
  color: "green",
13652
13674
  label: `inline anchor
13653
- ${inlineLabel.netId}`
13675
+ ${inlineLabel.netLabelText ?? inlineLabel.netId}`
13654
13676
  });
13655
13677
  }
13656
13678
  return graphics;
@@ -52,6 +52,8 @@ export const INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4
52
52
  export interface InlineNetLabelPlacement {
53
53
  globalConnNetId: string
54
54
  netId?: string
55
+ /** User-facing label content, separate from the connectivity identifier. */
56
+ netLabelText?: string
55
57
  mspPairId?: string
56
58
  pinIds: PinId[]
57
59
 
@@ -236,10 +238,11 @@ export class InlineNetLabelSolver extends BaseSolver {
236
238
 
237
239
  const height =
238
240
  connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT
241
+ const netLabelText = connection.netLabelText?.trim() || undefined
239
242
  const width =
240
243
  connection.inlineNetLabelWidth ??
241
244
  connection.netLabelWidth ??
242
- estimateInlineNetLabelWidth(connection.netId, height)
245
+ estimateInlineNetLabelWidth(netLabelText ?? connection.netId, height)
243
246
 
244
247
  // Leave a small wire tail at both ends of the text so it unmistakably
245
248
  // reads as a label on a trace rather than free-standing text.
@@ -310,6 +313,7 @@ export class InlineNetLabelSolver extends BaseSolver {
310
313
  return {
311
314
  globalConnNetId: anchoredPlacement.globalConnNetId,
312
315
  netId: connection.netId,
316
+ netLabelText,
313
317
  pinIds: [pinId],
314
318
  stubTracePath: [start, end],
315
319
  axis,
@@ -336,10 +340,11 @@ export class InlineNetLabelSolver extends BaseSolver {
336
340
 
337
341
  const height =
338
342
  connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT
343
+ const netLabelText = connection.netLabelText?.trim() || undefined
339
344
  const width =
340
345
  connection.inlineNetLabelWidth ??
341
346
  connection.netLabelWidth ??
342
- estimateInlineNetLabelWidth(connection.netId!, height)
347
+ estimateInlineNetLabelWidth(netLabelText ?? connection.netId!, height)
343
348
 
344
349
  const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN
345
350
 
@@ -397,6 +402,7 @@ export class InlineNetLabelSolver extends BaseSolver {
397
402
  return {
398
403
  globalConnNetId: trace.globalConnNetId,
399
404
  netId: connection.netId,
405
+ netLabelText,
400
406
  mspPairId: trace.mspPairId,
401
407
  pinIds: [...connection.pinIds],
402
408
  axis: segment.axis,
@@ -417,6 +423,7 @@ export class InlineNetLabelSolver extends BaseSolver {
417
423
  const spanPlacement = this.computeSpanPlacement({
418
424
  trace,
419
425
  connection,
426
+ netLabelText,
420
427
  width,
421
428
  height,
422
429
  offset,
@@ -437,12 +444,14 @@ export class InlineNetLabelSolver extends BaseSolver {
437
444
  private computeSpanPlacement({
438
445
  trace,
439
446
  connection,
447
+ netLabelText,
440
448
  width,
441
449
  height,
442
450
  offset,
443
451
  }: {
444
452
  trace: SolvedTracePath
445
453
  connection: InlineEligibleConnection
454
+ netLabelText?: string
446
455
  width: number
447
456
  height: number
448
457
  offset: number
@@ -533,6 +542,7 @@ export class InlineNetLabelSolver extends BaseSolver {
533
542
  return {
534
543
  globalConnNetId: trace.globalConnNetId,
535
544
  netId: connection.netId,
545
+ netLabelText,
536
546
  mspPairId: trace.mspPairId,
537
547
  pinIds: [...connection.pinIds],
538
548
  axis,
@@ -703,7 +713,7 @@ export class InlineNetLabelSolver extends BaseSolver {
703
713
  ? -inlineLabel.width / 2
704
714
  : inlineLabel.width / 2)
705
715
  : inlineLabel.center.y,
706
- text: inlineLabel.netId ?? "",
716
+ text: inlineLabel.netLabelText ?? inlineLabel.netId ?? "",
707
717
  color: "green",
708
718
  fontSize: inlineLabel.height,
709
719
  anchorSide: inlineLabel.stubTracePath
@@ -719,7 +729,7 @@ export class InlineNetLabelSolver extends BaseSolver {
719
729
  x: inlineLabel.anchorPoint.x,
720
730
  y: inlineLabel.anchorPoint.y,
721
731
  color: "green",
722
- label: `inline anchor\n${inlineLabel.netId}`,
732
+ label: `inline anchor\n${inlineLabel.netLabelText ?? inlineLabel.netId}`,
723
733
  })
724
734
  }
725
735
 
@@ -18,6 +18,7 @@ import { getNetLabelWidthForConnection } from "lib/utils/getNetLabelWidthForConn
18
18
  export type OverlappingSameNetTraceGroup = {
19
19
  globalConnNetId: string
20
20
  netId?: string
21
+ netLabelText?: string
21
22
  overlappingTraces?: SolvedTracePath
22
23
  portOnlyPinId?: string
23
24
  mspConnectionPairIds?: MspConnectionPairId[]
@@ -30,6 +31,8 @@ export interface NetLabelPlacement {
30
31
  * Optional user-provided net identifier (if present in the input problem).
31
32
  */
32
33
  netId?: string
34
+ /** User-facing label content, separate from the connectivity identifier. */
35
+ netLabelText?: string
33
36
  /**
34
37
  * MSP pair ids that the label is associated with. Port-only labels use [].
35
38
  */
@@ -116,18 +119,27 @@ export class NetLabelPlacementSolver extends BaseSolver {
116
119
  }
117
120
  }
118
121
 
119
- // Map pins to user-provided netIds (if any)
122
+ // Map pins to user-provided connectivity ids and display text (if any).
120
123
  const userNetIdByPinId: Record<string, string | undefined> = {}
124
+ const netLabelTextByPinId: Record<string, string | undefined> = {}
121
125
  for (const dc of this.inputProblem.directConnections) {
122
126
  if (dc.netId) {
123
127
  const [a, b] = dc.pinIds
124
128
  userNetIdByPinId[a] = dc.netId
125
129
  userNetIdByPinId[b] = dc.netId
126
130
  }
131
+ const netLabelText = dc.netLabelText?.trim()
132
+ if (netLabelText) {
133
+ const [a, b] = dc.pinIds
134
+ netLabelTextByPinId[a] = netLabelText
135
+ netLabelTextByPinId[b] = netLabelText
136
+ }
127
137
  }
128
138
  for (const nc of this.inputProblem.netConnections) {
129
139
  for (const pid of nc.pinIds) {
130
140
  userNetIdByPinId[pid] = nc.netId
141
+ const netLabelText = nc.netLabelText?.trim()
142
+ if (netLabelText) netLabelTextByPinId[pid] = netLabelText
131
143
  }
132
144
  }
133
145
 
@@ -237,6 +249,9 @@ export class NetLabelPlacementSolver extends BaseSolver {
237
249
  const group = {
238
250
  globalConnNetId,
239
251
  netId: userNetId,
252
+ netLabelText: [...component]
253
+ .map((pinId) => netLabelTextByPinId[pinId])
254
+ .find((text): text is string => Boolean(text)),
240
255
  overlappingTraces: rep,
241
256
  mspConnectionPairIds,
242
257
  }
@@ -249,6 +264,7 @@ export class NetLabelPlacementSolver extends BaseSolver {
249
264
  groups.push({
250
265
  globalConnNetId,
251
266
  netId: userNetId,
267
+ netLabelText: netLabelTextByPinId[p],
252
268
  portOnlyPinId: p,
253
269
  })
254
270
  }
@@ -354,6 +354,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
354
354
  this.overlappingSameNetTraceGroup.globalConnNetId,
355
355
  dcConnNetId: curr.dcConnNetId,
356
356
  netId: this.overlappingSameNetTraceGroup.netId,
357
+ netLabelText: this.overlappingSameNetTraceGroup.netLabelText,
357
358
  mspConnectionPairIds: [curr.mspPairId],
358
359
  pinIds: [curr.pins[0].pinId, curr.pins[1].pinId],
359
360
  orientation,
@@ -374,6 +375,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
374
375
  globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
375
376
  dcConnNetId: bestCandidate.dcConnNetId,
376
377
  netId: this.overlappingSameNetTraceGroup.netId,
378
+ netLabelText: this.overlappingSameNetTraceGroup.netLabelText,
377
379
  mspConnectionPairIds: [bestCandidate.mspPairId],
378
380
  pinIds: bestCandidate.pinIds,
379
381
  orientation: bestCandidate.orientation,
@@ -195,6 +195,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
195
195
  globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
196
196
  dcConnNetId: undefined,
197
197
  netId: overlappingSameNetTraceGroup.netId,
198
+ netLabelText: overlappingSameNetTraceGroup.netLabelText,
198
199
  mspConnectionPairIds: [],
199
200
  pinIds: [pinId],
200
201
  orientation,
@@ -230,6 +231,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
230
231
  globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
231
232
  dcConnNetId: undefined,
232
233
  netId: overlappingSameNetTraceGroup.netId,
234
+ netLabelText: overlappingSameNetTraceGroup.netLabelText,
233
235
  mspConnectionPairIds: [],
234
236
  pinIds: [pinId],
235
237
  orientation: fallbackOrientation,
@@ -24,6 +24,15 @@ export interface TextBoxes {
24
24
 
25
25
  export interface InputChip {
26
26
  chipId: ChipId
27
+
28
+ /**
29
+ * Exact directional/oriented schematic-symbol name when the caller has it.
30
+ * `center`, `width`, and `height` describe the solver obstacle and may be
31
+ * expanded to include reference/value or manufacturer-part-number text, so
32
+ * renderers should use this symbol's geometry for the component body.
33
+ */
34
+ symbolName?: string
35
+
27
36
  center: { x: number; y: number }
28
37
  width: number
29
38
  height: number
@@ -33,6 +42,14 @@ export interface InputChip {
33
42
  export interface InputDirectConnection {
34
43
  pinIds: [PinId, PinId]
35
44
  netId?: string
45
+
46
+ /**
47
+ * User-facing text to render for this net label. `netId` remains the stable
48
+ * connectivity identifier and may be an internal id. When this is omitted
49
+ * but a label width is provided, renderers should use a width-preserving
50
+ * placeholder instead of exposing `netId` as label text.
51
+ */
52
+ netLabelText?: string
36
53
  netLabelWidth?: number
37
54
 
38
55
  /**
@@ -58,7 +75,8 @@ export interface InputDirectConnection {
58
75
 
59
76
  /**
60
77
  * Extent of the inline net label along the trace. Falls back to
61
- * `netLabelWidth`, then to an estimate from the netId text.
78
+ * `netLabelWidth`, then to an estimate from `netLabelText` (or `netId` for
79
+ * backwards compatibility).
62
80
  */
63
81
  inlineNetLabelWidth?: number
64
82
 
@@ -72,6 +90,14 @@ export interface InputDirectConnection {
72
90
  export interface InputNetConnection {
73
91
  netId: string
74
92
  pinIds: Array<PinId>
93
+
94
+ /**
95
+ * User-facing text to render for this net label. `netId` remains the stable
96
+ * connectivity identifier and may be an internal id. When this is omitted
97
+ * but a label width is provided, renderers should use a width-preserving
98
+ * placeholder instead of exposing `netId` as label text.
99
+ */
100
+ netLabelText?: string
75
101
  netLabelWidth?: number
76
102
  fallbackNetLabelWidth?: number
77
103
  netLabelHeight?: number
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
6
  },
7
7
  "main": "dist/index.js",
8
- "version": "0.0.149",
8
+ "version": "0.0.150",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",
@@ -17,10 +17,15 @@
17
17
  "devDependencies": {
18
18
  "@biomejs/biome": "^2.2.2",
19
19
  "@react-hook/resize-observer": "^2.0.2",
20
+ "@tscircuit/alphabet": "^0.0.26",
21
+ "@tscircuit/circuit-json-util": "^0.0.111",
20
22
  "@tscircuit/math-utils": "^0.0.36",
21
23
  "@types/bun": "^1.2.21",
22
24
  "bun-match-svg": "^0.0.13",
23
25
  "calculate-elbow": "^0.0.12",
26
+ "circuit-json": "^0.0.475",
27
+ "circuit-json-to-connectivity-map": "^0.0.29",
28
+ "circuit-to-svg": "^0.0.407",
24
29
  "connectivity-map": "^1.0.0",
25
30
  "flatbush": "^4.5.0",
26
31
  "graphics-debug": "^0.0.98",
@@ -28,6 +33,8 @@
28
33
  "react-cosmos": "^7.0.0",
29
34
  "react-cosmos-plugin-vite": "^7.0.0",
30
35
  "react-dom": "^19.1.1",
36
+ "schematic-symbols": "^0.0.241",
37
+ "stack-svgs": "^0.0.1",
31
38
  "tsup": "^8.5.0",
32
39
  "vite": "^7.1.3"
33
40
  },