@tscircuit/core 0.0.2299 → 0.0.2301

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
@@ -11356,6 +11356,42 @@ var underscorifyPortArrangement = (portArrangement, pinLabels) => {
11356
11356
  return void 0;
11357
11357
  };
11358
11358
 
11359
+ // lib/utils/join-imported-courtyard-outlines.ts
11360
+ function joinImportedCourtyardOutlines(courtyards, componentName) {
11361
+ const polygons = /* @__PURE__ */ new Map();
11362
+ const pending = courtyards.filter(
11363
+ (courtyard) => courtyard.outline.length === 2
11364
+ );
11365
+ const samePoint2 = (a2, b2) => Math.hypot(a2.x - b2.x, a2.y - b2.y) < 1e-6;
11366
+ while (pending.length) {
11367
+ const first = pending.shift();
11368
+ const outline = [...first.outline];
11369
+ while (!samePoint2(outline[0], outline.at(-1))) {
11370
+ const end = outline.at(-1);
11371
+ const nextIndex = pending.findIndex(
11372
+ (candidate) => candidate.pcb_component_id === first.pcb_component_id && candidate.layer === first.layer && candidate.outline.some((point8) => samePoint2(point8, end))
11373
+ );
11374
+ if (nextIndex === -1) {
11375
+ throw new Error(
11376
+ `Imported courtyard for "${componentName}" has disconnected line segments; provide a complete closed polygon.`
11377
+ );
11378
+ }
11379
+ const [next] = pending.splice(nextIndex, 1);
11380
+ outline.push(
11381
+ samePoint2(next.outline[0], end) ? next.outline[1] : next.outline[0]
11382
+ );
11383
+ }
11384
+ outline.pop();
11385
+ if (outline.length < 3) {
11386
+ throw new Error(
11387
+ `Imported courtyard for "${componentName}" needs at least three polygon vertices.`
11388
+ );
11389
+ }
11390
+ polygons.set(first, outline);
11391
+ }
11392
+ return polygons;
11393
+ }
11394
+
11359
11395
  // lib/utils/createComponentsFromCircuitJson.ts
11360
11396
  import { getUnitVectorFromDirection as getUnitVectorFromDirection2 } from "@tscircuit/math-utils";
11361
11397
  import {
@@ -15829,6 +15865,10 @@ var createComponentsFromCircuitJson = ({
15829
15865
  pcbPinLabels,
15830
15866
  preserveSolderPaste = false
15831
15867
  }, circuitJson) => {
15868
+ const joinedCourtyards = joinImportedCourtyardOutlines(
15869
+ circuitJson.filter((elm) => elm.type === "pcb_courtyard_outline"),
15870
+ componentName
15871
+ );
15832
15872
  const importedSourcePorts = circuitJson.filter(
15833
15873
  (elm) => elm.type === "source_port"
15834
15874
  );
@@ -16540,9 +16580,11 @@ var createComponentsFromCircuitJson = ({
16540
16580
  })
16541
16581
  );
16542
16582
  } else if (elm.type === "pcb_courtyard_outline") {
16583
+ const outline = elm.outline.length === 2 ? joinedCourtyards.get(elm) : elm.outline;
16584
+ if (!outline) continue;
16543
16585
  components.push(
16544
16586
  new CourtyardOutline({
16545
- outline: elm.outline,
16587
+ outline,
16546
16588
  layer: elm.layer
16547
16589
  })
16548
16590
  );
@@ -22331,7 +22373,7 @@ function intersectsObstacle(points, obstacle, depth = 0) {
22331
22373
  // lib/utils/system-blocks/round-system-block-route.ts
22332
22374
  import { applyToPoint as applyToPoint33, compose as compose9, rotate as rotate4, translate as translate7 } from "transformation-matrix";
22333
22375
  var EPSILON = 1e-8;
22334
- function roundSystemBlockRoute(route, obstacles) {
22376
+ function roundSystemBlockRoute(route, obstacles, wireObstacles = []) {
22335
22377
  const points = [];
22336
22378
  for (const point8 of route) {
22337
22379
  if (points.length && distance9(points.at(-1), point8) < EPSILON) continue;
@@ -22356,6 +22398,27 @@ function roundSystemBlockRoute(route, obstacles) {
22356
22398
  const previous = points[index2 - 1];
22357
22399
  const corner = points[index2];
22358
22400
  const next = points[index2 + 1];
22401
+ const cornerObstacles = [
22402
+ ...obstacles,
22403
+ ...wireObstacles.map((wire) => {
22404
+ const clearance = Math.min(
22405
+ 3,
22406
+ Math.max(
22407
+ 0,
22408
+ wire.minX - corner.x,
22409
+ corner.x - wire.maxX,
22410
+ wire.minY - corner.y,
22411
+ corner.y - wire.maxY
22412
+ )
22413
+ );
22414
+ return {
22415
+ minX: wire.minX - clearance,
22416
+ maxX: wire.maxX + clearance,
22417
+ minY: wire.minY - clearance,
22418
+ maxY: wire.maxY + clearance
22419
+ };
22420
+ })
22421
+ ];
22359
22422
  const incoming = unitDirection(previous, corner);
22360
22423
  const outgoing = unitDirection(corner, next);
22361
22424
  const turn = Math.atan2(
@@ -22384,7 +22447,7 @@ function roundSystemBlockRoute(route, obstacles) {
22384
22447
  const maxX = Math.max(start.x, corner.x, end.x);
22385
22448
  const minY = Math.min(start.y, corner.y, end.y);
22386
22449
  const maxY = Math.max(start.y, corner.y, end.y);
22387
- const blocked = obstacles.some(
22450
+ const blocked = cornerObstacles.some(
22388
22451
  (obstacle) => minX < obstacle.maxX && maxX > obstacle.minX && minY < obstacle.maxY && maxY > obstacle.minY
22389
22452
  );
22390
22453
  if (!blocked || trim < 1e-3) break;
@@ -22439,103 +22502,744 @@ function unitDirection(a2, b2) {
22439
22502
  return { x: (b2.x - a2.x) / length9, y: (b2.y - a2.y) / length9 };
22440
22503
  }
22441
22504
 
22442
- // lib/utils/system-blocks/layout-system-blocks.ts
22443
- function layoutSystemBlocks(tree, direction) {
22444
- const graph = new dagre.graphlib.Graph({
22445
- compound: true,
22446
- multigraph: true
22447
- });
22448
- graph.setGraph({
22449
- rankdir: { right: "LR", left: "RL", down: "TB", up: "BT" }[direction ?? "right"],
22450
- // Compound borders need room for the renderer's 12 mm heading area.
22451
- nodesep: 24,
22452
- ranksep: 28,
22453
- edgesep: 12,
22454
- marginx: 6,
22455
- marginy: 6
22456
- });
22457
- const blocks = [...tree.groups, ...tree.components];
22458
- for (const block of blocks) {
22459
- const { name, displayName, description } = block._parsedProps;
22460
- const width2 = Math.max(
22461
- 48,
22462
- Math.min(76, (displayName ?? name).length * 1.7 + 8)
22463
- );
22464
- const descriptionLines = description ? Math.ceil(description.length / ((width2 - 8) / 1.3)) : 0;
22465
- graph.setNode(name, {
22466
- width: width2,
22467
- height: Math.max(24, 12 + descriptionLines * 3.5)
22468
- });
22505
+ // lib/utils/system-blocks/measure-system-route-quality.ts
22506
+ function measureSystemRouteQuality(routes) {
22507
+ let crossings = 0;
22508
+ let sharedLength = 0;
22509
+ let closeParallelLength = 0;
22510
+ let length9 = 0;
22511
+ let bends = 0;
22512
+ const segments = routes.map(
22513
+ (route) => route.slice(1).map((end, index2) => {
22514
+ const start = route[index2];
22515
+ length9 += Math.abs(end.x - start.x) + Math.abs(end.y - start.y);
22516
+ return { start, end, horizontal: start.y === end.y };
22517
+ })
22518
+ );
22519
+ for (const route of routes) bends += Math.max(0, route.length - 2);
22520
+ for (let i2 = 0; i2 < segments.length; i2++) {
22521
+ for (let j2 = i2 + 1; j2 < segments.length; j2++) {
22522
+ const intersections2 = /* @__PURE__ */ new Set();
22523
+ for (const a2 of segments[i2])
22524
+ for (const b2 of segments[j2]) {
22525
+ if (a2.horizontal === b2.horizontal) {
22526
+ const along2 = a2.horizontal ? "x" : "y";
22527
+ const across = a2.horizontal ? "y" : "x";
22528
+ const overlap = Math.min(
22529
+ Math.max(a2.start[along2], a2.end[along2]),
22530
+ Math.max(b2.start[along2], b2.end[along2])
22531
+ ) - Math.max(
22532
+ Math.min(a2.start[along2], a2.end[along2]),
22533
+ Math.min(b2.start[along2], b2.end[along2])
22534
+ );
22535
+ if (overlap <= 1e-6) continue;
22536
+ const separation = Math.abs(a2.start[across] - b2.start[across]);
22537
+ if (separation < 1e-6) sharedLength += overlap;
22538
+ if (separation < 3 - 1e-6)
22539
+ closeParallelLength += overlap * (3 - separation) / 3;
22540
+ } else {
22541
+ const h2 = a2.horizontal ? a2 : b2, v2 = a2.horizontal ? b2 : a2;
22542
+ const x2 = v2.start.x, y2 = h2.start.y;
22543
+ if (x2 >= Math.min(h2.start.x, h2.end.x) - 1e-6 && x2 <= Math.max(h2.start.x, h2.end.x) + 1e-6 && y2 >= Math.min(v2.start.y, v2.end.y) - 1e-6 && y2 <= Math.max(v2.start.y, v2.end.y) + 1e-6) {
22544
+ intersections2.add(`${x2.toFixed(6)},${y2.toFixed(6)}`);
22545
+ }
22546
+ }
22547
+ }
22548
+ crossings += intersections2.size;
22549
+ }
22469
22550
  }
22470
- for (const [block, parent] of tree.parents)
22471
- graph.setParent(block.name, parent.name);
22472
- const edges = tree.connections.map((connection, index2) => {
22473
- const { from, to, label } = connection._parsedProps;
22474
- const edge = { v: from, w: to, name: `connection_${index2}` };
22475
- graph.setEdge(edge, {
22476
- width: label === void 0 ? 0 : Math.max(1, label.length * 1.5),
22477
- height: label === void 0 ? 0 : 4,
22478
- labelpos: "l",
22479
- labeloffset: 3
22480
- });
22481
- return edge;
22482
- });
22483
- if (blocks.length) dagre.layout(graph);
22484
- const width = Math.max(32, graph.graph().width ?? 0);
22485
- const height = Math.max(24, graph.graph().height ?? 0);
22486
- const toPage = compose10(translate8(-width / 2, height / 2), scale3(1, -1));
22487
- const bounds = new Map(
22488
- blocks.map((block) => {
22489
- const node = graph.node(block.name);
22490
- return [
22491
- block,
22492
- {
22493
- center: applyToPoint34(toPage, { x: node.x, y: node.y }),
22494
- width: node.width,
22495
- height: node.height
22551
+ return {
22552
+ crossings,
22553
+ sharedLength,
22554
+ closeParallelLength,
22555
+ bends,
22556
+ length: length9,
22557
+ score: crossings * 200 + sharedLength * 100 + closeParallelLength * 10 + bends * 3 + length9 * 0.05
22558
+ };
22559
+ }
22560
+
22561
+ // lib/utils/system-blocks/find-system-orthogonal-path.ts
22562
+ function findSystemOrthogonalPath(start, end, obstacles, routedSegments) {
22563
+ const xs = [
22564
+ .../* @__PURE__ */ new Set([
22565
+ start.x,
22566
+ end.x,
22567
+ ...obstacles.flatMap((r2) => [r2.left, r2.right]),
22568
+ ...routedSegments.filter(([a2, b2]) => a2.x === b2.x).flatMap(([a2]) => [a2.x - 3, a2.x + 3])
22569
+ ])
22570
+ ].sort((a2, b2) => a2 - b2);
22571
+ const ys = [
22572
+ .../* @__PURE__ */ new Set([
22573
+ start.y,
22574
+ end.y,
22575
+ ...obstacles.flatMap((r2) => [r2.bottom, r2.top]),
22576
+ ...routedSegments.filter(([a2, b2]) => a2.y === b2.y).flatMap(([a2]) => [a2.y - 3, a2.y + 3])
22577
+ ])
22578
+ ].sort((a2, b2) => a2 - b2);
22579
+ xs.unshift(xs[0] - 5);
22580
+ xs.push(xs.at(-1) + 5);
22581
+ ys.unshift(ys[0] - 5);
22582
+ ys.push(ys.at(-1) + 5);
22583
+ const columns = xs.length;
22584
+ const pointAt2 = (node) => ({
22585
+ x: xs[node % columns],
22586
+ y: ys[Math.floor(node / columns)]
22587
+ });
22588
+ const startNode = ys.indexOf(start.y) * columns + xs.indexOf(start.x);
22589
+ const endNode = ys.indexOf(end.y) * columns + xs.indexOf(end.x);
22590
+ const costs = new Float64Array(xs.length * ys.length * 3).fill(Infinity);
22591
+ const previous = new Int32Array(costs.length).fill(-1);
22592
+ const horizontalCosts = new Float64Array(xs.length * ys.length).fill(NaN);
22593
+ const verticalCosts = new Float64Array(horizontalCosts.length).fill(NaN);
22594
+ const queue = [];
22595
+ const enqueue = (state, cost2, priority) => {
22596
+ const entry = { state, cost: cost2, priority };
22597
+ queue.push(entry);
22598
+ let i2 = queue.length - 1;
22599
+ while (i2 > 0) {
22600
+ const parent = i2 - 1 >> 1;
22601
+ if (queue[parent].priority <= priority) break;
22602
+ queue[i2] = queue[parent];
22603
+ i2 = parent;
22604
+ }
22605
+ queue[i2] = entry;
22606
+ };
22607
+ costs[startNode * 3 + 2] = 0;
22608
+ enqueue(startNode * 3 + 2, 0, 0);
22609
+ while (queue.length) {
22610
+ const current2 = queue[0];
22611
+ const last = queue.pop();
22612
+ if (queue.length) {
22613
+ let i2 = 0;
22614
+ while (2 * i2 + 1 < queue.length) {
22615
+ let child = 2 * i2 + 1;
22616
+ if (child + 1 < queue.length && queue[child + 1].priority < queue[child].priority)
22617
+ child++;
22618
+ if (last.priority <= queue[child].priority) break;
22619
+ queue[i2] = queue[child];
22620
+ i2 = child;
22621
+ }
22622
+ queue[i2] = last;
22623
+ }
22624
+ if (current2.cost !== costs[current2.state]) continue;
22625
+ const node = Math.floor(current2.state / 3);
22626
+ if (node === endNode) {
22627
+ const path = [];
22628
+ for (let state = current2.state; state !== -1; state = previous[state])
22629
+ path.push(pointAt2(Math.floor(state / 3)));
22630
+ return path.reverse();
22631
+ }
22632
+ const from = pointAt2(node);
22633
+ const col = node % columns;
22634
+ const row = Math.floor(node / columns);
22635
+ const neighbors = [
22636
+ ...col > 0 ? [node - 1] : [],
22637
+ ...col + 1 < columns ? [node + 1] : [],
22638
+ ...row > 0 ? [node - columns] : [],
22639
+ ...row + 1 < ys.length ? [node + columns] : []
22640
+ ];
22641
+ for (const neighbor of neighbors) {
22642
+ const to = pointAt2(neighbor);
22643
+ const horizontal = from.y === to.y;
22644
+ const left = Math.min(from.x, to.x), right = Math.max(from.x, to.x);
22645
+ const bottom = Math.min(from.y, to.y), top = Math.max(from.y, to.y);
22646
+ const edgeCosts = horizontal ? horizontalCosts : verticalCosts;
22647
+ const edgeIndex = Math.min(node, neighbor);
22648
+ let travelCost = edgeCosts[edgeIndex];
22649
+ if (Number.isNaN(travelCost)) {
22650
+ if (obstacles.some(
22651
+ (r2) => horizontal ? from.y > r2.bottom + 1e-6 && from.y < r2.top - 1e-6 && right > r2.left + 1e-6 && left < r2.right - 1e-6 : from.x > r2.left + 1e-6 && from.x < r2.right - 1e-6 && top > r2.bottom + 1e-6 && bottom < r2.top - 1e-6
22652
+ )) {
22653
+ edgeCosts[edgeIndex] = Infinity;
22654
+ continue;
22496
22655
  }
22497
- ];
22656
+ let congestion = 0;
22657
+ for (const [a2, b2] of routedSegments) {
22658
+ const sameAxis = a2.y === b2.y === horizontal;
22659
+ if (sameAxis) {
22660
+ const separation = horizontal ? Math.abs(a2.y - from.y) : Math.abs(a2.x - from.x);
22661
+ if (horizontal && separation < 3)
22662
+ congestion += Math.max(
22663
+ 0,
22664
+ Math.min(right, Math.max(a2.x, b2.x)) - Math.max(left, Math.min(a2.x, b2.x))
22665
+ ) * (separation < 1e-6 ? 40 : (3 - separation) * 8);
22666
+ if (!horizontal && separation < 3)
22667
+ congestion += Math.max(
22668
+ 0,
22669
+ Math.min(top, Math.max(a2.y, b2.y)) - Math.max(bottom, Math.min(a2.y, b2.y))
22670
+ ) * (separation < 1e-6 ? 40 : (3 - separation) * 8);
22671
+ } else if (horizontal ? a2.x >= left && a2.x <= right && from.y >= Math.min(a2.y, b2.y) && from.y <= Math.max(a2.y, b2.y) : a2.y >= bottom && a2.y <= top && from.x >= Math.min(a2.x, b2.x) && from.x <= Math.max(a2.x, b2.x))
22672
+ congestion += 80;
22673
+ }
22674
+ travelCost = right - left + top - bottom + congestion;
22675
+ edgeCosts[edgeIndex] = travelCost;
22676
+ }
22677
+ if (travelCost === Infinity) continue;
22678
+ const axis = horizontal ? 0 : 1;
22679
+ const state = neighbor * 3 + axis;
22680
+ const bend = current2.state % 3 === 2 || current2.state % 3 === axis ? 0 : 6;
22681
+ const cost2 = current2.cost + travelCost + bend;
22682
+ if (cost2 >= costs[state]) continue;
22683
+ costs[state] = cost2;
22684
+ previous[state] = current2.state;
22685
+ enqueue(
22686
+ state,
22687
+ cost2,
22688
+ cost2 + Math.abs(to.x - end.x) + Math.abs(to.y - end.y)
22689
+ );
22690
+ }
22691
+ }
22692
+ }
22693
+
22694
+ // lib/utils/system-blocks/route-system-blocks.ts
22695
+ var SYSTEM_CONNECTION_LABEL_HORIZONTAL_PADDING = 3;
22696
+ function getSystemConnectionLabelWidth(label) {
22697
+ let width = SYSTEM_CONNECTION_LABEL_HORIZONTAL_PADDING;
22698
+ for (const character of label) {
22699
+ width += /[MW@%#&]|[^\x00-\x7f]/.test(character) ? 2.5 : /[A-Z0-9]/.test(character) ? 1.85 : /[il.,:;!| ']/.test(character) ? 0.85 : 1.5;
22700
+ }
22701
+ return width;
22702
+ }
22703
+ function routeSystemBlocks(tree, bounds, initialRoutes) {
22704
+ const obstacles = [...bounds].map(
22705
+ ([block, box3]) => ({
22706
+ left: box3.center.x - box3.width / 2 - 2,
22707
+ right: tree.groups.includes(block) ? box3.center.x - box3.width / 2 + Math.min(
22708
+ box3.width,
22709
+ block._parsedProps.description ? box3.width : (block._parsedProps.displayName ?? block.name).length * 1.8 + 6
22710
+ ) : box3.center.x + box3.width / 2 + 2,
22711
+ bottom: tree.groups.includes(block) ? box3.center.y + box3.height / 2 - (block._parsedProps.description ? 12 : 7) : box3.center.y - box3.height / 2 - 2,
22712
+ top: box3.center.y + box3.height / 2 + 2
22498
22713
  })
22499
22714
  );
22500
- const obstacles = blocks.map((block) => {
22501
- const { center, width: width2, height: height2 } = bounds.get(block);
22502
- const top = center.y + height2 / 2;
22715
+ const portsByBlock = /* @__PURE__ */ new Map();
22716
+ const portsByConnection = /* @__PURE__ */ new Map();
22717
+ for (const connection of tree.connections) {
22718
+ const endpoints = tree.endpoints.get(connection);
22719
+ const route = initialRoutes.get(connection).route;
22720
+ const ports = [endpoints.from, endpoints.to].map((block, end) => {
22721
+ const box3 = bounds.get(block);
22722
+ const target = bounds.get(
22723
+ end === 0 ? endpoints.to : endpoints.from
22724
+ ).center;
22725
+ const guide = initialRoutes.get(connection).label_anchor_center ?? target;
22726
+ const point8 = endpoints.from === endpoints.to ? end === 0 ? route[0] : route.at(-1) : guide;
22727
+ const dx2 = (point8.x - box3.center.x) / box3.width;
22728
+ const dy2 = (point8.y - box3.center.y) / box3.height;
22729
+ const side = Math.abs(dx2) >= Math.abs(dy2) ? dx2 >= 0 ? 0 : 2 : dy2 >= 0 ? 1 : 3;
22730
+ const port = {
22731
+ point: { ...point8 },
22732
+ escape: { ...point8 },
22733
+ side,
22734
+ target: guide,
22735
+ connection
22736
+ };
22737
+ const ports2 = portsByBlock.get(block) ?? [];
22738
+ ports2.push(port);
22739
+ portsByBlock.set(block, ports2);
22740
+ return port;
22741
+ });
22742
+ portsByConnection.set(connection, ports);
22743
+ }
22744
+ for (const [block, ports] of portsByBlock) {
22745
+ const box3 = bounds.get(block);
22746
+ for (const side of [0, 1, 2, 3]) {
22747
+ const horizontal = side % 2 === 0;
22748
+ const facePorts = ports.filter((p2) => p2.side === side).sort(
22749
+ (a2, b2) => horizontal ? (a2.target.y - box3.center.y) / Math.max(1, Math.abs(a2.target.x - box3.center.x)) - (b2.target.y - box3.center.y) / Math.max(1, Math.abs(b2.target.x - box3.center.x)) : (a2.target.x - box3.center.x) / Math.max(1, Math.abs(a2.target.y - box3.center.y)) - (b2.target.x - box3.center.x) / Math.max(1, Math.abs(b2.target.y - box3.center.y))
22750
+ );
22751
+ const span = horizontal ? box3.height : box3.width;
22752
+ const spacing = Math.min(
22753
+ 4,
22754
+ (span - 4) / Math.max(1, facePorts.length - 1)
22755
+ );
22756
+ facePorts.forEach((port, index2) => {
22757
+ const offset = (index2 - (facePorts.length - 1) / 2) * spacing;
22758
+ const sign = side < 2 ? 1 : -1;
22759
+ port.point = horizontal ? {
22760
+ x: box3.center.x + sign * box3.width / 2,
22761
+ y: box3.center.y + offset
22762
+ } : {
22763
+ x: box3.center.x + offset,
22764
+ y: box3.center.y + sign * box3.height / 2
22765
+ };
22766
+ port.escape = horizontal ? { x: port.point.x + sign * 4, y: port.point.y } : { x: port.point.x, y: port.point.y + sign * 4 };
22767
+ });
22768
+ }
22769
+ }
22770
+ const labels = /* @__PURE__ */ new Map();
22771
+ const labelCenters = /* @__PURE__ */ new Map();
22772
+ const labelAttachments = /* @__PURE__ */ new Map();
22773
+ const portObstacles = [...portsByConnection.values()].flat().map(({ escape }) => ({
22774
+ left: escape.x - 1,
22775
+ right: escape.x + 1,
22776
+ bottom: escape.y - 1,
22777
+ top: escape.y + 1
22778
+ }));
22779
+ for (const connection of tree.connections) {
22780
+ const label = connection._parsedProps.label;
22781
+ const center = initialRoutes.get(connection).label_anchor_center;
22782
+ if (label !== void 0 && center) {
22783
+ const halfWidth = getSystemConnectionLabelWidth(label) / 2;
22784
+ const occupied = [...obstacles, ...labels.values(), ...portObstacles];
22785
+ const fits = (rectangle) => !occupied.some(
22786
+ (r2) => rectangle.left < r2.right + 1 && rectangle.right > r2.left - 1 && rectangle.bottom < r2.top + 1 && rectangle.top > r2.bottom - 1
22787
+ );
22788
+ const [start, end] = portsByConnection.get(connection);
22789
+ const endpoints = tree.endpoints.get(connection);
22790
+ const selfLoop = endpoints.from === endpoints.to;
22791
+ const vertical = start.escape.x === end.escape.x;
22792
+ const horizontal = start.escape.y === end.escape.y;
22793
+ const oppositeFaces = (start.side + 2) % 4 === end.side;
22794
+ const straightAxis = vertical && start.side % 2 === 1 ? "y" : horizontal && start.side % 2 === 0 ? "x" : void 0;
22795
+ const straight = !selfLoop && oppositeFaces && straightAxis !== void 0 && (end.escape[straightAxis] - start.escape[straightAxis]) * (start.escape[straightAxis] - start.point[straightAxis]) >= 0;
22796
+ const sameFaceReturn = selfLoop && start.side === end.side;
22797
+ const textFitsHorizontalSpan = horizontal && !selfLoop && Math.abs(start.escape.x - end.escape.x) >= halfWidth * 2;
22798
+ if ((straight || sameFaceReturn) && !textFitsHorizontalSpan && !obstacles.some(
22799
+ (r2) => vertical ? start.escape.x > r2.left && start.escape.x < r2.right && Math.max(start.escape.y, end.escape.y) > r2.bottom && Math.min(start.escape.y, end.escape.y) < r2.top : start.escape.y > r2.bottom && start.escape.y < r2.top && Math.max(start.escape.x, end.escape.x) > r2.left && Math.min(start.escape.x, end.escape.x) < r2.right
22800
+ )) {
22801
+ const attachment = {
22802
+ x: (start.escape.x + end.escape.x) / 2,
22803
+ y: (start.escape.y + end.escape.y) / 2
22804
+ };
22805
+ const signs = selfLoop ? [start.side < 2 ? 1 : -1] : [1, -1];
22806
+ for (const sign of signs) {
22807
+ const candidate = vertical ? { x: attachment.x + sign * (halfWidth + 2), y: attachment.y } : { x: attachment.x, y: attachment.y + sign * 4.5 };
22808
+ const rectangle = {
22809
+ left: candidate.x - halfWidth,
22810
+ right: candidate.x + halfWidth,
22811
+ bottom: candidate.y - 2.5,
22812
+ top: candidate.y + 2.5
22813
+ };
22814
+ if (!fits(rectangle)) continue;
22815
+ labels.set(connection, rectangle);
22816
+ labelCenters.set(connection, candidate);
22817
+ labelAttachments.set(connection, attachment);
22818
+ break;
22819
+ }
22820
+ if (labelAttachments.has(connection)) continue;
22821
+ }
22822
+ let placed = false;
22823
+ for (let radius = 0; !placed; radius++) {
22824
+ const offsets = radius === 0 ? [[0, 0]] : [
22825
+ [0, radius * 6],
22826
+ [0, -radius * 6],
22827
+ [radius * 6, 0],
22828
+ [-radius * 6, 0]
22829
+ ];
22830
+ for (const [dx2, dy2] of offsets) {
22831
+ const candidate = { x: center.x + dx2, y: center.y + dy2 };
22832
+ const rectangle = {
22833
+ left: candidate.x - halfWidth,
22834
+ right: candidate.x + halfWidth,
22835
+ bottom: candidate.y - 2.5,
22836
+ top: candidate.y + 2.5
22837
+ };
22838
+ if (!fits(rectangle)) continue;
22839
+ labels.set(connection, rectangle);
22840
+ labelCenters.set(connection, candidate);
22841
+ placed = true;
22842
+ break;
22843
+ }
22844
+ }
22845
+ }
22846
+ }
22847
+ const routeConnection = (connection, routedSegments) => {
22848
+ const [start, end] = portsByConnection.get(connection);
22849
+ const label = labels.get(connection);
22850
+ const attachment = labelAttachments.get(connection);
22851
+ const blocked = [
22852
+ ...obstacles,
22853
+ ...[...labels].filter(([c2]) => c2 !== connection).map(([, r2]) => r2)
22854
+ ];
22855
+ const waypoints = [start.escape];
22856
+ if (attachment) {
22857
+ waypoints.push(attachment);
22858
+ } else if (label) {
22859
+ const y2 = labelCenters.get(connection).y;
22860
+ const labelPorts = [
22861
+ { x: label.left, y: y2 },
22862
+ { x: label.right, y: y2 }
22863
+ ];
22864
+ if (start.escape.x > end.escape.x) labelPorts.reverse();
22865
+ waypoints.push(...labelPorts);
22866
+ }
22867
+ waypoints.push(end.escape);
22868
+ const route = [start.point];
22869
+ for (let i2 = 1; i2 < waypoints.length; i2++) {
22870
+ const legObstacles = label && (attachment || i2 !== 2) ? [...blocked, label] : blocked;
22871
+ const leg = findSystemOrthogonalPath(
22872
+ waypoints[i2 - 1],
22873
+ waypoints[i2],
22874
+ legObstacles,
22875
+ routedSegments
22876
+ );
22877
+ if (!leg) {
22878
+ throw new Error(
22879
+ `Cannot route system connection from "${connection._parsedProps.from}" to "${connection._parsedProps.to}" around blocks and labels`
22880
+ );
22881
+ }
22882
+ route.push(...leg);
22883
+ }
22884
+ route.push(end.point);
22885
+ const simplified = [];
22886
+ for (const point8 of route) {
22887
+ const previous = simplified.at(-1);
22888
+ if (previous?.x === point8.x && previous.y === point8.y) continue;
22889
+ const before = simplified.at(-2);
22890
+ if (before && previous && (before.x === previous.x && previous.x === point8.x || before.y === previous.y && previous.y === point8.y))
22891
+ simplified.pop();
22892
+ simplified.push(point8);
22893
+ }
22503
22894
  return {
22504
- minX: center.x - width2 / 2,
22505
- maxX: center.x + width2 / 2,
22506
- minY: tree.groups.some((group) => group === block) ? top - 12 : center.y - height2 / 2,
22507
- maxY: top
22895
+ route: simplified,
22896
+ label_anchor_center: labelCenters.get(connection)
22897
+ };
22898
+ };
22899
+ const connections = [...tree.connections].sort((a2, b2) => {
22900
+ const distance29 = (connection) => {
22901
+ const [from, to] = portsByConnection.get(connection);
22902
+ return Math.abs(from.point.x - to.point.x) + Math.abs(from.point.y - to.point.y);
22508
22903
  };
22904
+ return distance29(a2) - distance29(b2) || a2._parsedProps.from.localeCompare(b2._parsedProps.from) || a2._parsedProps.to.localeCompare(b2._parsedProps.to) || (a2._parsedProps.label ?? "").localeCompare(b2._parsedProps.label ?? "");
22509
22905
  });
22510
- for (const [index2, connection] of tree.connections.entries()) {
22511
- if (connection._parsedProps.label === void 0) continue;
22512
- const edge = graph.edge(edges[index2]);
22513
- const label = applyToPoint34(toPage, { x: edge.x, y: edge.y });
22906
+ const segmentsOf = (resolvedRoutes) => [...resolvedRoutes].flatMap(
22907
+ ({ route }) => route.slice(1).map((point8, index2) => [route[index2], point8])
22908
+ );
22909
+ let routes = /* @__PURE__ */ new Map();
22910
+ for (const connection of connections)
22911
+ routes.set(
22912
+ connection,
22913
+ routeConnection(connection, segmentsOf(routes.values()))
22914
+ );
22915
+ let score = measureSystemRouteQuality(
22916
+ [...routes.values()].map((r2) => r2.route)
22917
+ ).score;
22918
+ const reroute = (affected) => {
22919
+ const candidate = new Map(
22920
+ [...routes].filter(([connection]) => !affected.has(connection))
22921
+ );
22922
+ for (const connection of connections.filter((c2) => affected.has(c2)))
22923
+ candidate.set(
22924
+ connection,
22925
+ routeConnection(connection, segmentsOf(candidate.values()))
22926
+ );
22927
+ const candidateScore = measureSystemRouteQuality(
22928
+ [...candidate.values()].map((r2) => r2.route)
22929
+ ).score;
22930
+ if (candidateScore >= score - 1e-6) return false;
22931
+ routes = candidate;
22932
+ score = candidateScore;
22933
+ return true;
22934
+ };
22935
+ let proposals = 0;
22936
+ const proposalLimit = Math.min(64, connections.length * 4);
22937
+ for (let pass = 0; pass < 4 && proposals < proposalLimit; pass++) {
22938
+ let improved = false;
22939
+ for (const [block, ports] of portsByBlock) {
22940
+ if (ports.length < 2) continue;
22941
+ const center = bounds.get(block).center;
22942
+ const perimeter = [...ports].sort(
22943
+ (a2, b2) => Math.atan2(a2.point.y - center.y, a2.point.x - center.x) - Math.atan2(b2.point.y - center.y, b2.point.x - center.x)
22944
+ );
22945
+ for (let i2 = 0; i2 < perimeter.length && proposals < proposalLimit; i2++) {
22946
+ proposals++;
22947
+ const a2 = perimeter[i2], b2 = perimeter[(i2 + 1) % perimeter.length];
22948
+ const swap2 = () => {
22949
+ ;
22950
+ [a2.point, b2.point] = [b2.point, a2.point];
22951
+ [a2.escape, b2.escape] = [b2.escape, a2.escape];
22952
+ [a2.side, b2.side] = [b2.side, a2.side];
22953
+ };
22954
+ swap2();
22955
+ if (reroute(/* @__PURE__ */ new Set([a2.connection, b2.connection]))) {
22956
+ improved = true;
22957
+ perimeter[i2] = b2;
22958
+ perimeter[(i2 + 1) % perimeter.length] = a2;
22959
+ } else swap2();
22960
+ }
22961
+ }
22962
+ if (!improved) break;
22963
+ const quality = measureSystemRouteQuality(
22964
+ [...routes.values()].map((r2) => r2.route)
22965
+ );
22966
+ if (quality.crossings === 0 && quality.sharedLength === 0 && quality.closeParallelLength === 0)
22967
+ break;
22968
+ }
22969
+ for (const connection of connections) reroute(/* @__PURE__ */ new Set([connection]));
22970
+ return routes;
22971
+ }
22972
+
22973
+ // lib/utils/system-blocks/round-system-block-connections.ts
22974
+ function roundSystemBlockConnections(tree, bounds, routes) {
22975
+ const obstacles = [...bounds].map(
22976
+ ([block, box3]) => ({
22977
+ minX: box3.center.x - box3.width / 2,
22978
+ maxX: box3.center.x + box3.width / 2,
22979
+ minY: tree.groups.includes(block) ? box3.center.y + box3.height / 2 - (block._parsedProps.description ? 12 : 7) : box3.center.y - box3.height / 2,
22980
+ maxY: box3.center.y + box3.height / 2
22981
+ })
22982
+ );
22983
+ for (const [connection, resolved] of routes) {
22984
+ const label = resolved.label_anchor_center;
22985
+ if (!label) continue;
22986
+ const halfWidth = (getSystemConnectionLabelWidth(connection._parsedProps.label) - SYSTEM_CONNECTION_LABEL_HORIZONTAL_PADDING) / 2;
22514
22987
  obstacles.push({
22515
- minX: label.x - edge.width / 2 - 0.5,
22516
- maxX: label.x + edge.width / 2 + 0.5,
22517
- minY: label.y - edge.height / 2 - 0.5,
22518
- maxY: label.y + edge.height / 2 + 0.5
22988
+ minX: label.x - halfWidth,
22989
+ maxX: label.x + halfWidth,
22990
+ minY: label.y - 2.5,
22991
+ maxY: label.y + 2.5
22992
+ });
22993
+ }
22994
+ const rounded = new Map(routes);
22995
+ for (const [connection, resolved] of routes) {
22996
+ const wireObstacles = [];
22997
+ for (const [other, { route }] of rounded) {
22998
+ if (other === connection) continue;
22999
+ for (let index2 = 1; index2 < route.length; index2++) {
23000
+ const end = route[index2];
23001
+ const points = [
23002
+ route[index2 - 1],
23003
+ ...end.cubic_bezier_control_points ?? [],
23004
+ end
23005
+ ];
23006
+ wireObstacles.push({
23007
+ minX: Math.min(...points.map((p2) => p2.x)),
23008
+ maxX: Math.max(...points.map((p2) => p2.x)),
23009
+ minY: Math.min(...points.map((p2) => p2.y)),
23010
+ maxY: Math.max(...points.map((p2) => p2.y))
23011
+ });
23012
+ }
23013
+ }
23014
+ const blocked = [
23015
+ ...obstacles,
23016
+ ...wireObstacles.map((wire) => ({
23017
+ minX: wire.minX - 3,
23018
+ maxX: wire.maxX + 3,
23019
+ minY: wire.minY - 3,
23020
+ maxY: wire.maxY + 3
23021
+ }))
23022
+ ];
23023
+ const { from, to } = tree.endpoints.get(connection);
23024
+ const selfLoop = from === to ? createSystemBlockSelfLoop(resolved.route, bounds.get(from), blocked) : void 0;
23025
+ rounded.set(connection, {
23026
+ ...resolved,
23027
+ route: selfLoop ?? roundSystemBlockRoute(resolved.route, obstacles, wireObstacles)
22519
23028
  });
22520
23029
  }
22521
- const routes = new Map(
22522
- tree.connections.map((connection, index2) => {
22523
- const edge = graph.edge(edges[index2]);
22524
- const route = edge.points.map((point8) => applyToPoint34(toPage, point8)).filter(
22525
- (point8, index3, points) => index3 === 0 || point8.x !== points[index3 - 1].x || point8.y !== points[index3 - 1].y
23030
+ return rounded;
23031
+ }
23032
+
23033
+ // lib/utils/system-blocks/layout-system-blocks.ts
23034
+ function layoutSystemBlocks(tree, direction) {
23035
+ const degrees2 = /* @__PURE__ */ new Map();
23036
+ for (const {
23037
+ _parsedProps: { from, to }
23038
+ } of tree.connections) {
23039
+ degrees2.set(from, (degrees2.get(from) ?? 0) + 1);
23040
+ degrees2.set(to, (degrees2.get(to) ?? 0) + 1);
23041
+ }
23042
+ const layout = layoutContainer(tree, void 0, direction ?? "right", degrees2);
23043
+ const toPage = compose10(
23044
+ translate8(-layout.width / 2, layout.height / 2),
23045
+ scale3(1, -1)
23046
+ );
23047
+ for (const box3 of layout.bounds.values())
23048
+ box3.center = applyToPoint34(toPage, box3.center);
23049
+ for (const [connection, resolved] of layout.routes) {
23050
+ resolved.route = resolved.route.map((point8) => applyToPoint34(toPage, point8));
23051
+ if (resolved.label_anchor_center)
23052
+ resolved.label_anchor_center = applyToPoint34(
23053
+ toPage,
23054
+ resolved.label_anchor_center
23055
+ );
23056
+ const endpoints = tree.endpoints.get(connection);
23057
+ for (const [block, index2] of [
23058
+ [endpoints.from, 0],
23059
+ [endpoints.to, resolved.route.length - 1]
23060
+ ]) {
23061
+ const box3 = layout.bounds.get(block);
23062
+ const point8 = resolved.route[index2];
23063
+ let dx2 = point8.x - box3.center.x, dy2 = point8.y - box3.center.y;
23064
+ if (dx2 === 0 && dy2 === 0) {
23065
+ const neighbor = resolved.route[index2 === 0 ? 1 : index2 - 1];
23066
+ dx2 = neighbor.x - point8.x;
23067
+ dy2 = neighbor.y - point8.y;
23068
+ }
23069
+ const factor = Math.max(
23070
+ Math.abs(dx2) / (box3.width / 2),
23071
+ Math.abs(dy2) / (box3.height / 2)
22526
23072
  );
22527
- const label_anchor_center = connection._parsedProps.label === void 0 ? void 0 : applyToPoint34(toPage, { x: edge.x, y: edge.y });
22528
- const { from, to } = tree.endpoints.get(connection);
22529
- const selfLoop = from === to ? createSystemBlockSelfLoop(route, bounds.get(from), obstacles) : void 0;
22530
- return [
22531
- connection,
22532
- {
22533
- route: selfLoop ?? roundSystemBlockRoute(route, obstacles),
22534
- label_anchor_center
22535
- }
22536
- ];
22537
- })
23073
+ resolved.route[index2] = {
23074
+ x: box3.center.x + dx2 / factor,
23075
+ y: box3.center.y + dy2 / factor
23076
+ };
23077
+ }
23078
+ }
23079
+ const routes = roundSystemBlockConnections(
23080
+ tree,
23081
+ layout.bounds,
23082
+ routeSystemBlocks(tree, layout.bounds, layout.routes)
22538
23083
  );
23084
+ let width = layout.width, height = layout.height;
23085
+ for (const [connection, resolved] of routes) {
23086
+ for (const point8 of resolved.route) {
23087
+ width = Math.max(width, 2 * (Math.abs(point8.x) + 6));
23088
+ height = Math.max(height, 2 * (Math.abs(point8.y) + 6));
23089
+ }
23090
+ if (resolved.label_anchor_center) {
23091
+ width = Math.max(
23092
+ width,
23093
+ 2 * (Math.abs(resolved.label_anchor_center.x) + getSystemConnectionLabelWidth(connection._parsedProps.label) / 2 + 6)
23094
+ );
23095
+ height = Math.max(
23096
+ height,
23097
+ 2 * (Math.abs(resolved.label_anchor_center.y) + 8)
23098
+ );
23099
+ }
23100
+ }
23101
+ return { width, height, bounds: layout.bounds, routes };
23102
+ }
23103
+ function layoutContainer(tree, parent, direction, degrees2) {
23104
+ const children = [
23105
+ ...tree.groups,
23106
+ ...tree.components
23107
+ ].filter((block) => tree.parents.get(block) === parent);
23108
+ const groupLayouts = /* @__PURE__ */ new Map();
23109
+ const graph = new dagre.graphlib.Graph({ multigraph: true });
23110
+ graph.setGraph({
23111
+ rankdir: { right: "LR", left: "RL", down: "TB", up: "BT" }[direction],
23112
+ nodesep: parent ? 12 : 18,
23113
+ ranksep: 18,
23114
+ edgesep: 8,
23115
+ marginx: 8,
23116
+ marginy: 8
23117
+ });
23118
+ for (const block of children) {
23119
+ if (tree.groups.includes(block)) {
23120
+ const child = layoutContainer(
23121
+ tree,
23122
+ block,
23123
+ direction,
23124
+ degrees2
23125
+ );
23126
+ groupLayouts.set(block, child);
23127
+ graph.setNode(block.name, { width: child.width, height: child.height });
23128
+ } else {
23129
+ const { name, displayName, description } = block._parsedProps;
23130
+ const width2 = Math.max(
23131
+ 32,
23132
+ Math.min(62, (displayName ?? name).length * 1.5 + 8)
23133
+ );
23134
+ const descriptionLines = description ? Math.ceil(description.length / ((width2 - 8) / 1.3)) : 0;
23135
+ graph.setNode(name, {
23136
+ width: width2,
23137
+ height: Math.max(18, 12 + descriptionLines * 3.5)
23138
+ });
23139
+ }
23140
+ }
23141
+ const childFor = (block) => {
23142
+ let current2 = block;
23143
+ while (current2 && tree.parents.get(current2) !== parent)
23144
+ current2 = tree.parents.get(current2);
23145
+ return current2;
23146
+ };
23147
+ const edges = /* @__PURE__ */ new Map();
23148
+ for (const [index2, connection] of tree.connections.entries()) {
23149
+ const endpoints = tree.endpoints.get(connection);
23150
+ const from = childFor(endpoints.from), to = childFor(endpoints.to);
23151
+ if (!from || !to || from === to && (from !== endpoints.from || to !== endpoints.to))
23152
+ continue;
23153
+ const props = connection._parsedProps;
23154
+ const reverse4 = props.direction === "both" && degrees2.get(props.to) > degrees2.get(props.from);
23155
+ const edge = {
23156
+ v: (reverse4 ? to : from).name,
23157
+ w: (reverse4 ? from : to).name,
23158
+ name: `connection_${index2}`
23159
+ };
23160
+ graph.setEdge(edge, {
23161
+ width: props.label === void 0 ? 0 : getSystemConnectionLabelWidth(props.label),
23162
+ height: props.label === void 0 ? 0 : 5,
23163
+ labelpos: "c",
23164
+ labeloffset: 0
23165
+ });
23166
+ edges.set(connection, edge);
23167
+ }
23168
+ if (children.length) dagre.layout(graph);
23169
+ const heading = parent ? parent._parsedProps.description ? 14 : 8 : 0;
23170
+ const minimumWidth = parent ? Math.min(
23171
+ 90,
23172
+ (parent._parsedProps.displayName ?? parent.name).length * 1.8 + 8
23173
+ ) : 32;
23174
+ const width = Math.max(minimumWidth, graph.graph().width ?? 0);
23175
+ const height = Math.max(24, (graph.graph().height ?? 0) + heading);
23176
+ const bounds = /* @__PURE__ */ new Map();
23177
+ const routes = /* @__PURE__ */ new Map();
23178
+ for (const block of children) {
23179
+ const node = graph.node(block.name);
23180
+ bounds.set(block, {
23181
+ center: { x: node.x, y: node.y + heading },
23182
+ width: node.width,
23183
+ height: node.height
23184
+ });
23185
+ const child = groupLayouts.get(block);
23186
+ if (!child) continue;
23187
+ const offset = {
23188
+ x: node.x - child.width / 2,
23189
+ y: node.y + heading - child.height / 2
23190
+ };
23191
+ const shift = (point8) => ({
23192
+ x: point8.x + offset.x,
23193
+ y: point8.y + offset.y
23194
+ });
23195
+ for (const [descendant, box3] of child.bounds)
23196
+ bounds.set(descendant, { ...box3, center: shift(box3.center) });
23197
+ for (const [connection, resolved] of child.routes)
23198
+ routes.set(connection, {
23199
+ route: resolved.route.map(shift),
23200
+ label_anchor_center: resolved.label_anchor_center ? shift(resolved.label_anchor_center) : void 0
23201
+ });
23202
+ }
23203
+ for (const [connection, edgeKey] of edges) {
23204
+ const edge = graph.edge(edgeKey);
23205
+ const route = edge.points.map((point8) => ({
23206
+ x: point8.x,
23207
+ y: point8.y + heading
23208
+ }));
23209
+ const from = childFor(tree.endpoints.get(connection).from);
23210
+ if (edgeKey.v !== from.name) route.reverse();
23211
+ routes.set(connection, {
23212
+ route,
23213
+ label_anchor_center: connection._parsedProps.label === void 0 ? void 0 : { x: edge.x, y: edge.y + heading }
23214
+ });
23215
+ }
23216
+ const bundles = /* @__PURE__ */ new Map();
23217
+ for (const [connection, edge] of edges) {
23218
+ if (connection._parsedProps.label === void 0) continue;
23219
+ const targets = bundles.get(edge.v) ?? /* @__PURE__ */ new Map();
23220
+ const connections = targets.get(edge.w) ?? [];
23221
+ connections.push(connection);
23222
+ targets.set(edge.w, connections);
23223
+ bundles.set(edge.v, targets);
23224
+ }
23225
+ const crossAxis = direction === "right" || direction === "left" ? "y" : "x";
23226
+ for (const targets of bundles.values()) {
23227
+ for (const connections of targets.values()) {
23228
+ const slots = connections.map((c2) => routes.get(c2).label_anchor_center).sort((a2, b2) => a2[crossAxis] - b2[crossAxis]);
23229
+ const destinationPosition = (connection) => {
23230
+ const endpoints = tree.endpoints.get(connection);
23231
+ const targetChild = edges.get(connection).w;
23232
+ const endpoint = childFor(endpoints.to).name === targetChild ? endpoints.to : endpoints.from;
23233
+ return bounds.get(endpoint).center[crossAxis];
23234
+ };
23235
+ connections.sort(
23236
+ (a2, b2) => destinationPosition(a2) - destinationPosition(b2)
23237
+ );
23238
+ connections.forEach((connection, index2) => {
23239
+ routes.get(connection).label_anchor_center = slots[index2];
23240
+ });
23241
+ }
23242
+ }
22539
23243
  return { width, height, bounds, routes };
22540
23244
  }
22541
23245
 
@@ -22634,7 +23338,7 @@ function SystemBlockPage_doInitialSystemBlockRender(page) {
22634
23338
  }
22635
23339
  for (const connection of tree.connections) {
22636
23340
  const { from, to } = tree.endpoints.get(connection);
22637
- const { name: name2, kind, direction: direction2, label } = connection._parsedProps;
23341
+ const { name: name2, kind, direction: direction2, label, color: color3, lineStyle } = connection._parsedProps;
22638
23342
  db2.system_block_connection.insert({
22639
23343
  system_block_page_id: page.system_block_page_id,
22640
23344
  from_system_block_component_id: componentIds.get(from),
@@ -22642,6 +23346,8 @@ function SystemBlockPage_doInitialSystemBlockRender(page) {
22642
23346
  name: name2,
22643
23347
  label,
22644
23348
  direction: direction2,
23349
+ color: color3,
23350
+ line_style: lineStyle,
22645
23351
  is_signal: kind === "signal",
22646
23352
  is_power: kind === "power",
22647
23353
  ...layout.routes.get(connection)
@@ -158599,7 +159305,7 @@ var RoutingPhasePreparationError = class extends Error {
158599
159305
  var package_default = {
158600
159306
  name: "@tscircuit/core",
158601
159307
  type: "module",
158602
- version: "0.0.2298",
159308
+ version: "0.0.2300",
158603
159309
  types: "dist/index.d.ts",
158604
159310
  main: "dist/index.js",
158605
159311
  module: "dist/index.js",
@@ -158661,7 +159367,7 @@ var package_default = {
158661
159367
  "@tscircuit/miniflex": "^0.0.4",
158662
159368
  "@tscircuit/modelprinter": "^0.0.37",
158663
159369
  "@tscircuit/ngspice-spice-engine": "^0.0.20",
158664
- "@tscircuit/props": "^0.0.719",
159370
+ "@tscircuit/props": "^0.0.721",
158665
159371
  "@tscircuit/schematic-match-adapt": "^0.0.18",
158666
159372
  "@tscircuit/schematic-trace-solver": "https://jscdn.tscircuit.com/@tscircuit/schematic-trace-solver/0.0.238.tgz",
158667
159373
  "@tscircuit/solver-utils": "^0.0.16",
@@ -158677,19 +159383,19 @@ var package_default = {
158677
159383
  "bun-match-svg": "0.0.12",
158678
159384
  "calculate-elbow": "^0.0.12",
158679
159385
  "chokidar-cli": "^3.0.0",
158680
- "circuit-json": "^0.0.547",
159386
+ "circuit-json": "^0.0.548",
158681
159387
  "circuit-json-to-bom-csv": "^0.0.28",
158682
159388
  "circuit-json-to-bpc": "^0.0.13",
158683
159389
  "circuit-json-to-connectivity-map": "^0.0.33",
158684
159390
  "circuit-json-to-spice": "^0.0.45",
158685
- "circuit-to-svg": "^0.0.456",
159391
+ "circuit-to-svg": "^0.0.457",
158686
159392
  concurrently: "^9.1.2",
158687
159393
  "connectivity-map": "^1.0.1",
158688
159394
  debug: "^4.3.6",
158689
159395
  flatbush: "^4.5.0",
158690
159396
  "graphics-debug": "^0.0.95",
158691
159397
  howfat: "^0.3.8",
158692
- "kicad-to-circuit-json": "^0.0.142",
159398
+ "kicad-to-circuit-json": "^0.0.149",
158693
159399
  kicadts: "^0.0.53",
158694
159400
  "live-server": "^1.2.2",
158695
159401
  minicssgrid: "^0.0.9",
@@ -158768,8 +159474,8 @@ var package_default = {
158768
159474
  },
158769
159475
  overrides: {
158770
159476
  "@tscircuit/graphics-debug-3d": "https://jscdn.tscircuit.com/@tscircuit/graphics-debug-3d/0.0.1.tgz",
158771
- "@tscircuit/props": "^0.0.719",
158772
- "circuit-json": "^0.0.547",
159477
+ "@tscircuit/props": "^0.0.721",
159478
+ "circuit-json": "^0.0.548",
158773
159479
  "@tscircuit/cableprinter": "^0.0.6"
158774
159480
  },
158775
159481
  browser: {