@crazyhappyone/auto-graph 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1289,6 +1289,9 @@ function applyLayoutConstraints(input) {
1289
1289
  const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
1290
1290
  applyFixedPositionLocks(input.nodes, boxes, locks, diagnostics);
1291
1291
  applyExactPositions(input.constraints, boxes, locks, diagnostics, nodeById);
1292
+ if (input.distributeContainedChildren) {
1293
+ yieldFixedPositionLocks(input, boxes, locks);
1294
+ }
1292
1295
  applyContainment(input.constraints, boxes, locks, diagnostics, false);
1293
1296
  applyRelative(input.constraints, boxes, locks, diagnostics);
1294
1297
  applyAlign(input.constraints, boxes, locks, diagnostics);
@@ -1302,6 +1305,13 @@ function applyLayoutConstraints(input) {
1302
1305
  );
1303
1306
  applyContainment(input.constraints, boxes, locks, diagnostics, true);
1304
1307
  applyDistributeContained(input, boxes, locks, diagnostics);
1308
+ if (input.distributeContainedChildren) {
1309
+ const diagBefore = diagnostics.length;
1310
+ applyContainment(input.constraints, boxes, locks, diagnostics, true);
1311
+ applyDistributeContained(input, boxes, locks, diagnostics);
1312
+ dedupReplayDiagnostics(diagnostics, diagBefore);
1313
+ }
1314
+ removeResolvedConstraintDiagnostics(input.constraints, boxes, diagnostics);
1305
1315
  reportOverlaps(boxes, diagnostics, containmentOverlapKeys(input.constraints));
1306
1316
  reportIntraContainerOverflow(input, boxes, diagnostics);
1307
1317
  return { boxes, locks, diagnostics };
@@ -1347,6 +1357,62 @@ function applyFixedPositionLocks(nodes, boxes, locks, diagnostics) {
1347
1357
  locks.set(node.id, { nodeId: node.id, source: "fixed-position" });
1348
1358
  }
1349
1359
  }
1360
+ function dedupReplayDiagnostics(diagnostics, keepUpTo) {
1361
+ const seen = /* @__PURE__ */ new Set();
1362
+ for (let i = 0; i < keepUpTo && i < diagnostics.length; i += 1) {
1363
+ const d = diagnostics[i];
1364
+ if (d === void 0) continue;
1365
+ seen.add(diagnosticFingerprint(d));
1366
+ }
1367
+ for (let i = diagnostics.length - 1; i >= keepUpTo; i -= 1) {
1368
+ const d = diagnostics[i];
1369
+ if (d === void 0) continue;
1370
+ const fp = diagnosticFingerprint(d);
1371
+ if (seen.has(fp)) {
1372
+ diagnostics.splice(i, 1);
1373
+ } else {
1374
+ seen.add(fp);
1375
+ }
1376
+ }
1377
+ }
1378
+ function diagnosticFingerprint(d) {
1379
+ const nodeId = typeof d.detail?.nodeId === "string" ? d.detail.nodeId : "";
1380
+ const containerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : "";
1381
+ return `${d.code}|${nodeId}|${containerId}`;
1382
+ }
1383
+ function yieldFixedPositionLocks(input, boxes, locks) {
1384
+ for (const c of input.constraints) {
1385
+ if (c.kind !== "containment") continue;
1386
+ const container = boxes.get(c.containerId);
1387
+ if (container === void 0) continue;
1388
+ const content = contentBox(container, c.padding);
1389
+ const mainAxis = input.direction === "LR" || input.direction === "RL" ? "width" : "height";
1390
+ const crossAxis = mainAxis === "width" ? "height" : "width";
1391
+ let eligible = 0;
1392
+ for (const childId of c.childIds) {
1393
+ const box = boxes.get(childId);
1394
+ if (box === void 0) continue;
1395
+ const lock = locks.get(childId);
1396
+ if (lock?.source === "exact-position") continue;
1397
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
1398
+ if (fits) {
1399
+ eligible += 1;
1400
+ }
1401
+ }
1402
+ if (eligible < 2) continue;
1403
+ for (const childId of c.childIds) {
1404
+ const lock = locks.get(childId);
1405
+ if (lock?.source === "fixed-position") {
1406
+ const box = boxes.get(childId);
1407
+ if (box === void 0) continue;
1408
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
1409
+ if (fits) {
1410
+ locks.delete(childId);
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ }
1350
1416
  function applyExactPositions(constraints, boxes, locks, diagnostics, nodeById) {
1351
1417
  for (const constraint of constraints) {
1352
1418
  if (constraint.kind !== "exact-position") {
@@ -1419,7 +1485,7 @@ function applyContainment(constraints, boxes, locks, diagnostics, reportOverflow
1419
1485
  code: "constraints.locked-target-not-moved",
1420
1486
  message: `Locked child ${childId} was not moved into containment.`,
1421
1487
  path: ["constraints", constraint.id ?? constraint.containerId],
1422
- detail: { nodeId: childId }
1488
+ detail: { nodeId: childId, containerId: constraint.containerId }
1423
1489
  });
1424
1490
  if (!isInside(child, content)) {
1425
1491
  diagnostics.push({
@@ -1571,6 +1637,60 @@ function repairOverlaps(input, boxes, locks, diagnostics, siblingPairs) {
1571
1637
  }
1572
1638
  reportOverlaps(boxes, diagnostics, ignoredPairs);
1573
1639
  }
1640
+ function removeResolvedConstraintDiagnostics(constraints, boxes, diagnostics) {
1641
+ for (let i = diagnostics.length - 1; i >= 0; i -= 1) {
1642
+ const d = diagnostics[i];
1643
+ if (d === void 0) continue;
1644
+ if (d.code === "constraints.overlap.unresolved") {
1645
+ const aId = d.detail?.firstId;
1646
+ const bId = d.detail?.secondId;
1647
+ if (typeof aId !== "string" || typeof bId !== "string") continue;
1648
+ const a = boxes.get(aId);
1649
+ const b = boxes.get(bId);
1650
+ if (a !== void 0 && b !== void 0 && !intersectsAabb(a, b)) {
1651
+ diagnostics.splice(i, 1);
1652
+ }
1653
+ continue;
1654
+ }
1655
+ if (d.code === "constraints.containment.impossible" || d.code === "constraints.locked-target-not-moved" && typeof d.message === "string" && d.message.includes("not moved into containment")) {
1656
+ const nodeId = d.detail?.nodeId;
1657
+ if (typeof nodeId !== "string") continue;
1658
+ const child = boxes.get(nodeId);
1659
+ if (child === void 0) continue;
1660
+ const diagContainerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : void 0;
1661
+ let resolved = false;
1662
+ for (const c of constraints) {
1663
+ if (c.kind !== "containment") continue;
1664
+ if (!c.childIds.includes(nodeId)) continue;
1665
+ if (diagContainerId !== void 0 && c.containerId !== diagContainerId) {
1666
+ continue;
1667
+ }
1668
+ const container = boxes.get(c.containerId);
1669
+ if (container === void 0) continue;
1670
+ const content = contentBox(container, c.padding);
1671
+ if (isInside(child, content)) {
1672
+ diagnostics.splice(i, 1);
1673
+ resolved = true;
1674
+ }
1675
+ break;
1676
+ }
1677
+ if (!resolved && diagContainerId !== void 0) {
1678
+ for (const c of constraints) {
1679
+ if (c.kind !== "containment") continue;
1680
+ if (c.containerId !== diagContainerId) continue;
1681
+ if (!c.childIds.includes(nodeId)) continue;
1682
+ const container = boxes.get(c.containerId);
1683
+ if (container === void 0) continue;
1684
+ const content = contentBox(container, c.padding);
1685
+ if (isInside(child, content)) {
1686
+ diagnostics.splice(i, 1);
1687
+ }
1688
+ break;
1689
+ }
1690
+ }
1691
+ }
1692
+ }
1693
+ }
1574
1694
  function reportOverlaps(boxes, diagnostics, ignoredPairs = /* @__PURE__ */ new Set()) {
1575
1695
  const ids = [...boxes.keys()].sort();
1576
1696
  const reported = new Set(
@@ -1878,6 +1998,12 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
1878
1998
  continue;
1879
1999
  }
1880
2000
  if (locks.has(childId)) {
2001
+ const lock = locks.get(childId);
2002
+ if (lock?.source === "fixed-position") {
2003
+ unlocked.push({ id: childId, box });
2004
+ locks.delete(childId);
2005
+ continue;
2006
+ }
1881
2007
  diagnostics.push({
1882
2008
  severity: "warning",
1883
2009
  code: "constraints.locked-target-not-moved",
@@ -1936,6 +2062,7 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
1936
2062
  });
1937
2063
  }
1938
2064
  boxes.set(child.id, clamped);
2065
+ locks.delete(child.id);
1939
2066
  pos = clamped[axis] + clamped[mainSize] + minGap;
1940
2067
  }
1941
2068
  diagnostics.push({
@@ -3385,7 +3512,7 @@ function routeEdge(input) {
3385
3512
  const rerouted = greedyRerouteAroundObstacles(
3386
3513
  bestPoints2,
3387
3514
  allObstacles,
3388
- Math.min(maxAttempts, 3)
3515
+ maxAttempts
3389
3516
  );
3390
3517
  const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
3391
3518
  rerouted,
@@ -4147,9 +4274,7 @@ function solveDiagram(diagram, options = {}) {
4147
4274
  options?.overlapSpacing ?? 40,
4148
4275
  Math.max(0, options?.minLaneGutter ?? 0)
4149
4276
  );
4150
- if (swimlaneContracts.layouts.size > 0) {
4151
- removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4152
- }
4277
+ removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4153
4278
  diagnostics.push(...swimlaneContracts.diagnostics);
4154
4279
  const coordinatedNodes = coordinateNodes(
4155
4280
  styledNodes,
@@ -6092,7 +6217,8 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
6092
6217
  ...softObstacles,
6093
6218
  ...routeTextObstacles
6094
6219
  ],
6095
- hardObstacles
6220
+ hardObstacles,
6221
+ ...options.maxRoutingAttempts === void 0 ? {} : { maxRoutingAttempts: options.maxRoutingAttempts }
6096
6222
  });
6097
6223
  diagnostics.push(
6098
6224
  ...route.diagnostics.map((diagnostic) => ({
@@ -6575,7 +6701,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
6575
6701
  for (const candidate of edgeLabelAnchorCandidates(
6576
6702
  edge.points,
6577
6703
  placement,
6578
- layout2
6704
+ layout2,
6705
+ baseOffset
6579
6706
  )) {
6580
6707
  const labelBox = {
6581
6708
  x: candidate.x - layout2.box.width / 2,
@@ -6607,8 +6734,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
6607
6734
  }
6608
6735
  return placement;
6609
6736
  }
6610
- function edgeLabelAnchorCandidates(points, placement, layout2) {
6611
- const segment = labelSegmentOnPolyline(points);
6737
+ function edgeLabelAnchorCandidates(points, placement, layout2, baseOffset = 10) {
6738
+ const segment = labelSegmentOnPolyline(points, baseOffset);
6612
6739
  if (segment === void 0) {
6613
6740
  return [placement];
6614
6741
  }
@@ -6658,7 +6785,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
6658
6785
  }, 0);
6659
6786
  if (totalLen > 200) {
6660
6787
  for (const ratio of [0.25, 0.75]) {
6661
- const qp = labelPlacementAtRatio(points, ratio, totalLen);
6788
+ const qp = labelPlacementAtRatio(points, ratio, totalLen, baseOffset);
6662
6789
  if (qp !== void 0) {
6663
6790
  candidates.push(qp);
6664
6791
  const qTargetDist = totalLen * ratio;
@@ -6744,7 +6871,7 @@ function labelSegmentOnPolyline(points, baseOffset = 10) {
6744
6871
  if (last === void 0) {
6745
6872
  return void 0;
6746
6873
  }
6747
- const offset = labelOffset2(last);
6874
+ const offset = labelOffset2(last, baseOffset);
6748
6875
  return {
6749
6876
  start: last.start,
6750
6877
  end: last.end,
@@ -6766,7 +6893,7 @@ function nonZeroSegments2(points) {
6766
6893
  }
6767
6894
  return segments;
6768
6895
  }
6769
- function labelPlacementAtRatio(points, ratio, totalLength) {
6896
+ function labelPlacementAtRatio(points, ratio, totalLength, baseOffset = 10) {
6770
6897
  if (points.length < 2 || ratio < 0 || ratio > 1) {
6771
6898
  return void 0;
6772
6899
  }
@@ -6784,7 +6911,10 @@ function labelPlacementAtRatio(points, ratio, totalLength) {
6784
6911
  }
6785
6912
  if (travelled + segLen >= targetDist) {
6786
6913
  const t = (targetDist - travelled) / segLen;
6787
- const offset = labelOffset2({ start: prev, end: curr, length: segLen });
6914
+ const offset = labelOffset2(
6915
+ { start: prev, end: curr, length: segLen },
6916
+ baseOffset
6917
+ );
6788
6918
  return {
6789
6919
  x: prev.x + (curr.x - prev.x) * t + offset.x,
6790
6920
  y: prev.y + (curr.y - prev.y) * t + offset.y