@tscircuit/schematic-trace-solver 0.0.104 → 0.0.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4774,6 +4774,95 @@ var minimizeTurns = ({
4774
4774
  return finalSimplifiedPath;
4775
4775
  };
4776
4776
 
4777
+ // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
4778
+ var RAIL_ALIGNMENT_EPSILON = 2e-3;
4779
+ var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
4780
+ var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
4781
+ var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
4782
+ var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
4783
+ var getRailOrientation = (a, b) => {
4784
+ if (!isPositiveLength(a, b)) return null;
4785
+ if (isVertical2(a, b)) return "vertical";
4786
+ if (isHorizontal2(a, b)) return "horizontal";
4787
+ return null;
4788
+ };
4789
+ var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
4790
+ var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
4791
+ var getDistinctCoordinates = (coordinates) => {
4792
+ const distinct = [];
4793
+ for (const coordinate of coordinates) {
4794
+ if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
4795
+ distinct.push(coordinate);
4796
+ }
4797
+ }
4798
+ return distinct;
4799
+ };
4800
+ var getMergedIntervalMetrics = (intervals) => {
4801
+ const groups = [];
4802
+ for (const interval of intervals) {
4803
+ const group = groups.find(
4804
+ (items) => nearlyEqual(items[0].coordinate, interval.coordinate)
4805
+ );
4806
+ if (group) group.push(interval);
4807
+ else groups.push([interval]);
4808
+ }
4809
+ return groups.reduce(
4810
+ (total, group) => {
4811
+ const sorted = [...group].sort((a, b) => a.min - b.min);
4812
+ let groupLength = 0;
4813
+ let segmentCount = 1;
4814
+ let currentMin = sorted[0].min;
4815
+ let currentMax = sorted[0].max;
4816
+ for (const interval of sorted.slice(1)) {
4817
+ if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
4818
+ currentMax = Math.max(currentMax, interval.max);
4819
+ } else {
4820
+ groupLength += currentMax - currentMin;
4821
+ segmentCount++;
4822
+ currentMin = interval.min;
4823
+ currentMax = interval.max;
4824
+ }
4825
+ }
4826
+ return {
4827
+ length: total.length + groupLength + currentMax - currentMin,
4828
+ segmentCount: total.segmentCount + segmentCount
4829
+ };
4830
+ },
4831
+ { length: 0, segmentCount: 0 }
4832
+ );
4833
+ };
4834
+ var getVisibleTraceMetrics = (traces) => {
4835
+ const horizontal = [];
4836
+ const vertical = [];
4837
+ for (const trace of traces) {
4838
+ for (let index = 0; index < trace.tracePath.length - 1; index++) {
4839
+ const start = trace.tracePath[index];
4840
+ const end = trace.tracePath[index + 1];
4841
+ if (isHorizontal2(start, end)) {
4842
+ horizontal.push({
4843
+ coordinate: start.y,
4844
+ min: Math.min(start.x, end.x),
4845
+ max: Math.max(start.x, end.x)
4846
+ });
4847
+ } else if (isVertical2(start, end)) {
4848
+ vertical.push({
4849
+ coordinate: start.x,
4850
+ min: Math.min(start.y, end.y),
4851
+ max: Math.max(start.y, end.y)
4852
+ });
4853
+ }
4854
+ }
4855
+ }
4856
+ const horizontalMetrics = getMergedIntervalMetrics(horizontal);
4857
+ const verticalMetrics = getMergedIntervalMetrics(vertical);
4858
+ return {
4859
+ length: horizontalMetrics.length + verticalMetrics.length,
4860
+ segmentCount: horizontalMetrics.segmentCount + verticalMetrics.segmentCount
4861
+ };
4862
+ };
4863
+ var getVisibleTraceLength = (traces) => getVisibleTraceMetrics(traces).length;
4864
+ var getVisibleTraceSegmentCount = (traces) => getVisibleTraceMetrics(traces).segmentCount;
4865
+
4777
4866
  // lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
4778
4867
  var minimizeTurnsWithFilteredLabels = ({
4779
4868
  targetMspConnectionPairId,
@@ -4789,11 +4878,16 @@ var minimizeTurnsWithFilteredLabels = ({
4789
4878
  if (!targetTrace) {
4790
4879
  throw new Error(`Target trace ${targetMspConnectionPairId} not found`);
4791
4880
  }
4792
- const obstacleTraces = traces.filter(
4793
- (t) => t.mspPairId !== targetMspConnectionPairId
4881
+ const targetPinIds = new Set(targetTrace.pinIds);
4882
+ const otherTraces = traces.filter(
4883
+ (trace) => trace.mspPairId !== targetMspConnectionPairId
4794
4884
  );
4885
+ const relaxedObstacleTraces = otherTraces.filter((trace) => {
4886
+ const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId));
4887
+ return trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint;
4888
+ });
4795
4889
  const TRACE_WIDTH = 0.01;
4796
- const traceObstacles = obstacleTraces.flatMap(
4890
+ const getTraceObstacles2 = (obstacleTraces) => obstacleTraces.flatMap(
4797
4891
  (trace, i) => trace.tracePath.slice(0, -1).map((p1, pi) => {
4798
4892
  const p2 = trace.tracePath[pi + 1];
4799
4893
  return {
@@ -4814,7 +4908,6 @@ var minimizeTurnsWithFilteredLabels = ({
4814
4908
  maxX: obs.maxX + PADDING,
4815
4909
  maxY: obs.maxY + PADDING
4816
4910
  }));
4817
- const combinedObstacles = [...staticObstacles, ...traceObstacles];
4818
4911
  const originalPath = targetTrace.tracePath;
4819
4912
  const filteredLabels = allLabelPlacements.filter((label) => {
4820
4913
  const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId];
@@ -4829,12 +4922,46 @@ var minimizeTurnsWithFilteredLabels = ({
4829
4922
  minY: nl.center.y - nl.height / 2 - paddingBuffer,
4830
4923
  maxY: nl.center.y + nl.height / 2 + paddingBuffer
4831
4924
  }));
4832
- const newPath = minimizeTurns({
4925
+ const strictPath = minimizeTurns({
4926
+ path: originalPath,
4927
+ obstacles: [...staticObstacles, ...getTraceObstacles2(otherTraces)],
4928
+ labelBounds,
4929
+ originalPath
4930
+ });
4931
+ const relaxedPath = minimizeTurns({
4833
4932
  path: originalPath,
4834
- obstacles: combinedObstacles,
4933
+ obstacles: [
4934
+ ...staticObstacles,
4935
+ ...getTraceObstacles2(relaxedObstacleTraces)
4936
+ ],
4835
4937
  labelBounds,
4836
4938
  originalPath
4837
4939
  });
4940
+ const sameNetTraces = otherTraces.filter(
4941
+ (trace) => trace.globalConnNetId === targetTrace.globalConnNetId
4942
+ );
4943
+ const getSameNetReadability = (tracePath) => {
4944
+ const tracesWithCandidate = [
4945
+ ...sameNetTraces,
4946
+ { ...targetTrace, tracePath }
4947
+ ];
4948
+ return {
4949
+ segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate),
4950
+ visibleLength: getVisibleTraceLength(tracesWithCandidate)
4951
+ };
4952
+ };
4953
+ const strictTurns = countTurns(strictPath);
4954
+ const relaxedTurns = countTurns(relaxedPath);
4955
+ let newPath = strictPath;
4956
+ if (relaxedTurns < strictTurns) {
4957
+ newPath = relaxedPath;
4958
+ } else if (relaxedTurns === strictTurns) {
4959
+ const strictReadability = getSameNetReadability(strictPath);
4960
+ const relaxedReadability = getSameNetReadability(relaxedPath);
4961
+ if (relaxedReadability.segmentCount < strictReadability.segmentCount || relaxedReadability.segmentCount === strictReadability.segmentCount && relaxedReadability.visibleLength < strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON) {
4962
+ newPath = relaxedPath;
4963
+ }
4964
+ }
4838
4965
  return {
4839
4966
  ...targetTrace,
4840
4967
  tracePath: newPath
@@ -4978,80 +5105,6 @@ var balanceZShapes = ({
4978
5105
  };
4979
5106
  };
4980
5107
 
4981
- // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
4982
- var RAIL_ALIGNMENT_EPSILON = 2e-3;
4983
- var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
4984
- var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
4985
- var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
4986
- var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
4987
- var getRailOrientation = (a, b) => {
4988
- if (!isPositiveLength(a, b)) return null;
4989
- if (isVertical2(a, b)) return "vertical";
4990
- if (isHorizontal2(a, b)) return "horizontal";
4991
- return null;
4992
- };
4993
- var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
4994
- var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
4995
- var getDistinctCoordinates = (coordinates) => {
4996
- const distinct = [];
4997
- for (const coordinate of coordinates) {
4998
- if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
4999
- distinct.push(coordinate);
5000
- }
5001
- }
5002
- return distinct;
5003
- };
5004
- var getMergedIntervalLength = (intervals) => {
5005
- const groups = [];
5006
- for (const interval of intervals) {
5007
- const group = groups.find(
5008
- (items) => nearlyEqual(items[0].coordinate, interval.coordinate)
5009
- );
5010
- if (group) group.push(interval);
5011
- else groups.push([interval]);
5012
- }
5013
- return groups.reduce((total, group) => {
5014
- const sorted = [...group].sort((a, b) => a.min - b.min);
5015
- let groupLength = 0;
5016
- let currentMin = sorted[0].min;
5017
- let currentMax = sorted[0].max;
5018
- for (const interval of sorted.slice(1)) {
5019
- if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
5020
- currentMax = Math.max(currentMax, interval.max);
5021
- } else {
5022
- groupLength += currentMax - currentMin;
5023
- currentMin = interval.min;
5024
- currentMax = interval.max;
5025
- }
5026
- }
5027
- return total + groupLength + currentMax - currentMin;
5028
- }, 0);
5029
- };
5030
- var getVisibleTraceLength = (traces) => {
5031
- const horizontal = [];
5032
- const vertical = [];
5033
- for (const trace of traces) {
5034
- for (let index = 0; index < trace.tracePath.length - 1; index++) {
5035
- const start = trace.tracePath[index];
5036
- const end = trace.tracePath[index + 1];
5037
- if (isHorizontal2(start, end)) {
5038
- horizontal.push({
5039
- coordinate: start.y,
5040
- min: Math.min(start.x, end.x),
5041
- max: Math.max(start.x, end.x)
5042
- });
5043
- } else if (isVertical2(start, end)) {
5044
- vertical.push({
5045
- coordinate: start.x,
5046
- min: Math.min(start.y, end.y),
5047
- max: Math.max(start.y, end.y)
5048
- });
5049
- }
5050
- }
5051
- }
5052
- return getMergedIntervalLength(horizontal) + getMergedIntervalLength(vertical);
5053
- };
5054
-
5055
5108
  // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
5056
5109
  var moveRailSegments = (trace, segments, coordinate) => {
5057
5110
  const pointsToMove = /* @__PURE__ */ new Set();
@@ -3,13 +3,18 @@ import { minimizeTurns } from "./turnMinimization"
3
3
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
4
  import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
5
5
  import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
6
+ import { countTurns } from "./countTurns"
7
+ import {
8
+ getVisibleTraceLength,
9
+ getVisibleTraceSegmentCount,
10
+ RAIL_ALIGNMENT_EPSILON,
11
+ } from "./sameNetRailAlignment/geometry"
6
12
 
7
13
  /**
8
- * Minimizes the turns of a target trace while considering other traces and labels as obstacles.
9
- * This function first identifies the target trace and separates it from other traces, which are then treated as obstacles.
10
- * It also filters out labels that belong to the target trace's net, so they don't act as obstacles.
11
- * The function then combines static obstacles (from the input problem) with the other traces and filtered labels to create a comprehensive set of obstacles.
12
- * Finally, it uses a turn minimization algorithm to find a new path for the target trace that avoids these combined obstacles.
14
+ * Minimizes turns with a strict pass that treats every other trace as an
15
+ * obstacle and a relaxed pass that permits joining endpoint-sharing same-net
16
+ * branches. Equivalent-turn routes are compared by their rendered same-net
17
+ * complexity so aligned rails win without arbitrarily shifting local routes.
13
18
  */
14
19
  export const minimizeTurnsWithFilteredLabels = ({
15
20
  targetMspConnectionPairId,
@@ -33,23 +38,31 @@ export const minimizeTurnsWithFilteredLabels = ({
33
38
  throw new Error(`Target trace ${targetMspConnectionPairId} not found`)
34
39
  }
35
40
 
36
- const obstacleTraces = traces.filter(
37
- (t) => t.mspPairId !== targetMspConnectionPairId,
41
+ const targetPinIds = new Set(targetTrace.pinIds)
42
+ const otherTraces = traces.filter(
43
+ (trace) => trace.mspPairId !== targetMspConnectionPairId,
38
44
  )
45
+ const relaxedObstacleTraces = otherTraces.filter((trace) => {
46
+ const sharesEndpoint = trace.pinIds.some((pinId) => targetPinIds.has(pinId))
47
+ return (
48
+ trace.globalConnNetId !== targetTrace.globalConnNetId || !sharesEndpoint
49
+ )
50
+ })
39
51
 
40
52
  const TRACE_WIDTH = 0.01
41
- const traceObstacles = obstacleTraces.flatMap((trace, i) =>
42
- trace.tracePath.slice(0, -1).map((p1, pi) => {
43
- const p2 = trace.tracePath[pi + 1]!
44
- return {
45
- chipId: `trace-obstacle-${i}-${pi}`,
46
- minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
47
- minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
48
- maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
49
- maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2,
50
- }
51
- }),
52
- )
53
+ const getTraceObstacles = (obstacleTraces: SolvedTracePath[]) =>
54
+ obstacleTraces.flatMap((trace, i) =>
55
+ trace.tracePath.slice(0, -1).map((p1, pi) => {
56
+ const p2 = trace.tracePath[pi + 1]!
57
+ return {
58
+ chipId: `trace-obstacle-${i}-${pi}`,
59
+ minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
60
+ minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
61
+ maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
62
+ maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2,
63
+ }
64
+ }),
65
+ )
53
66
 
54
67
  const staticObstaclesRaw = getObstacleRects(inputProblem)
55
68
  const PADDING = 0.01
@@ -61,8 +74,6 @@ export const minimizeTurnsWithFilteredLabels = ({
61
74
  maxY: obs.maxY + PADDING,
62
75
  }))
63
76
 
64
- const combinedObstacles = [...staticObstacles, ...traceObstacles]
65
-
66
77
  const originalPath = targetTrace.tracePath
67
78
  const filteredLabels = allLabelPlacements.filter((label) => {
68
79
  const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]
@@ -79,13 +90,57 @@ export const minimizeTurnsWithFilteredLabels = ({
79
90
  maxY: nl.center.y + nl.height / 2 + paddingBuffer,
80
91
  }))
81
92
 
82
- const newPath = minimizeTurns({
93
+ const strictPath = minimizeTurns({
83
94
  path: originalPath,
84
- obstacles: combinedObstacles,
95
+ obstacles: [...staticObstacles, ...getTraceObstacles(otherTraces)],
85
96
  labelBounds,
86
97
  originalPath: originalPath,
87
98
  })
88
99
 
100
+ const relaxedPath = minimizeTurns({
101
+ path: originalPath,
102
+ obstacles: [
103
+ ...staticObstacles,
104
+ ...getTraceObstacles(relaxedObstacleTraces),
105
+ ],
106
+ labelBounds,
107
+ originalPath: originalPath,
108
+ })
109
+
110
+ const sameNetTraces = otherTraces.filter(
111
+ (trace) => trace.globalConnNetId === targetTrace.globalConnNetId,
112
+ )
113
+ const getSameNetReadability = (tracePath: SolvedTracePath["tracePath"]) => {
114
+ const tracesWithCandidate = [
115
+ ...sameNetTraces,
116
+ { ...targetTrace, tracePath },
117
+ ]
118
+ return {
119
+ segmentCount: getVisibleTraceSegmentCount(tracesWithCandidate),
120
+ visibleLength: getVisibleTraceLength(tracesWithCandidate),
121
+ }
122
+ }
123
+
124
+ const strictTurns = countTurns(strictPath)
125
+ const relaxedTurns = countTurns(relaxedPath)
126
+ let newPath = strictPath
127
+
128
+ if (relaxedTurns < strictTurns) {
129
+ newPath = relaxedPath
130
+ } else if (relaxedTurns === strictTurns) {
131
+ const strictReadability = getSameNetReadability(strictPath)
132
+ const relaxedReadability = getSameNetReadability(relaxedPath)
133
+
134
+ if (
135
+ relaxedReadability.segmentCount < strictReadability.segmentCount ||
136
+ (relaxedReadability.segmentCount === strictReadability.segmentCount &&
137
+ relaxedReadability.visibleLength <
138
+ strictReadability.visibleLength - RAIL_ALIGNMENT_EPSILON)
139
+ ) {
140
+ newPath = relaxedPath
141
+ }
142
+ }
143
+
89
144
  return {
90
145
  ...targetTrace,
91
146
  tracePath: newPath,
@@ -48,7 +48,7 @@ interface Interval {
48
48
  max: number
49
49
  }
50
50
 
51
- const getMergedIntervalLength = (intervals: Interval[]) => {
51
+ const getMergedIntervalMetrics = (intervals: Interval[]) => {
52
52
  const groups: Interval[][] = []
53
53
  for (const interval of intervals) {
54
54
  const group = groups.find((items) =>
@@ -58,28 +58,35 @@ const getMergedIntervalLength = (intervals: Interval[]) => {
58
58
  else groups.push([interval])
59
59
  }
60
60
 
61
- return groups.reduce((total, group) => {
62
- const sorted = [...group].sort((a, b) => a.min - b.min)
63
- let groupLength = 0
64
- let currentMin = sorted[0]!.min
65
- let currentMax = sorted[0]!.max
66
-
67
- for (const interval of sorted.slice(1)) {
68
- if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
69
- currentMax = Math.max(currentMax, interval.max)
70
- } else {
71
- groupLength += currentMax - currentMin
72
- currentMin = interval.min
73
- currentMax = interval.max
61
+ return groups.reduce(
62
+ (total, group) => {
63
+ const sorted = [...group].sort((a, b) => a.min - b.min)
64
+ let groupLength = 0
65
+ let segmentCount = 1
66
+ let currentMin = sorted[0]!.min
67
+ let currentMax = sorted[0]!.max
68
+
69
+ for (const interval of sorted.slice(1)) {
70
+ if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
71
+ currentMax = Math.max(currentMax, interval.max)
72
+ } else {
73
+ groupLength += currentMax - currentMin
74
+ segmentCount++
75
+ currentMin = interval.min
76
+ currentMax = interval.max
77
+ }
74
78
  }
75
- }
76
79
 
77
- return total + groupLength + currentMax - currentMin
78
- }, 0)
80
+ return {
81
+ length: total.length + groupLength + currentMax - currentMin,
82
+ segmentCount: total.segmentCount + segmentCount,
83
+ }
84
+ },
85
+ { length: 0, segmentCount: 0 },
86
+ )
79
87
  }
80
88
 
81
- /** Returns rendered length after overlapping collinear runs are merged. */
82
- export const getVisibleTraceLength = (traces: SolvedTracePath[]) => {
89
+ const getVisibleTraceMetrics = (traces: SolvedTracePath[]) => {
83
90
  const horizontal: Interval[] = []
84
91
  const vertical: Interval[] = []
85
92
 
@@ -104,5 +111,18 @@ export const getVisibleTraceLength = (traces: SolvedTracePath[]) => {
104
111
  }
105
112
  }
106
113
 
107
- return getMergedIntervalLength(horizontal) + getMergedIntervalLength(vertical)
114
+ const horizontalMetrics = getMergedIntervalMetrics(horizontal)
115
+ const verticalMetrics = getMergedIntervalMetrics(vertical)
116
+ return {
117
+ length: horizontalMetrics.length + verticalMetrics.length,
118
+ segmentCount: horizontalMetrics.segmentCount + verticalMetrics.segmentCount,
119
+ }
108
120
  }
121
+
122
+ /** Returns rendered length after overlapping collinear runs are merged. */
123
+ export const getVisibleTraceLength = (traces: SolvedTracePath[]) =>
124
+ getVisibleTraceMetrics(traces).length
125
+
126
+ /** Returns the number of distinct collinear runs visible on the schematic. */
127
+ export const getVisibleTraceSegmentCount = (traces: SolvedTracePath[]) =>
128
+ getVisibleTraceMetrics(traces).segmentCount
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.104",
4
+ "version": "0.0.106",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/bug-reports/bug-report-20260721T221026Z/bug-report-20260721T221026Z.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />