@spectratools/graphic-designer-cli 0.10.0 → 0.11.0

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.js CHANGED
@@ -1050,7 +1050,8 @@ var connectionElementSchema = z2.object({
1050
1050
  from: z2.string().min(1).max(120),
1051
1051
  to: z2.string().min(1).max(120),
1052
1052
  style: z2.enum(["solid", "dashed", "dotted"]).default("solid"),
1053
- strokeStyle: z2.enum(["solid", "dashed", "dotted"]).default("solid"),
1053
+ /** @deprecated Use `style` instead. */
1054
+ strokeStyle: z2.enum(["solid", "dashed", "dotted"]).optional(),
1054
1055
  arrow: z2.enum(["end", "start", "both", "none"]).default("end"),
1055
1056
  label: z2.string().min(1).max(200).optional(),
1056
1057
  labelPosition: z2.enum(["start", "middle", "end"]).default("middle"),
@@ -1062,7 +1063,8 @@ var connectionElementSchema = z2.object({
1062
1063
  arrowSize: z2.number().min(4).max(32).optional(),
1063
1064
  arrowPlacement: z2.enum(["endpoint", "boundary"]).default("endpoint"),
1064
1065
  opacity: z2.number().min(0).max(1).default(1),
1065
- routing: z2.enum(["auto", "orthogonal", "curve", "arc"]).default("auto"),
1066
+ routing: z2.enum(["auto", "orthogonal", "curve", "arc", "straight"]).default("auto"),
1067
+ curveMode: z2.enum(["normal", "ellipse"]).default("normal"),
1066
1068
  tension: z2.number().min(0.1).max(0.8).default(0.35),
1067
1069
  fromAnchor: anchorHintSchema.optional(),
1068
1070
  toAnchor: anchorHintSchema.optional()
@@ -1155,7 +1157,11 @@ var autoLayoutConfigSchema = z2.object({
1155
1157
  /** Sort strategy for radial layout node ordering. Only relevant when algorithm is 'radial'. */
1156
1158
  radialSortBy: z2.enum(["id", "connections"]).optional(),
1157
1159
  /** Explicit center used by curve/arc connection routing. */
1158
- diagramCenter: diagramCenterSchema.optional()
1160
+ diagramCenter: diagramCenterSchema.optional(),
1161
+ /** Horizontal radius for shared ellipse used by curveMode: 'ellipse'. */
1162
+ ellipseRx: z2.number().positive().optional(),
1163
+ /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
1164
+ ellipseRy: z2.number().positive().optional()
1159
1165
  }).strict();
1160
1166
  var gridLayoutConfigSchema = z2.object({
1161
1167
  mode: z2.literal("grid"),
@@ -1165,7 +1171,11 @@ var gridLayoutConfigSchema = z2.object({
1165
1171
  cardMaxHeight: z2.number().int().min(32).max(4096).optional(),
1166
1172
  equalHeight: z2.boolean().default(false),
1167
1173
  /** Explicit center used by curve/arc connection routing. */
1168
- diagramCenter: diagramCenterSchema.optional()
1174
+ diagramCenter: diagramCenterSchema.optional(),
1175
+ /** Horizontal radius for shared ellipse used by curveMode: 'ellipse'. */
1176
+ ellipseRx: z2.number().positive().optional(),
1177
+ /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
1178
+ ellipseRy: z2.number().positive().optional()
1169
1179
  }).strict();
1170
1180
  var stackLayoutConfigSchema = z2.object({
1171
1181
  mode: z2.literal("stack"),
@@ -1173,7 +1183,11 @@ var stackLayoutConfigSchema = z2.object({
1173
1183
  gap: z2.number().int().min(0).max(256).default(24),
1174
1184
  alignment: z2.enum(["start", "center", "end", "stretch"]).default("stretch"),
1175
1185
  /** Explicit center used by curve/arc connection routing. */
1176
- diagramCenter: diagramCenterSchema.optional()
1186
+ diagramCenter: diagramCenterSchema.optional(),
1187
+ /** Horizontal radius for shared ellipse used by curveMode: 'ellipse'. */
1188
+ ellipseRx: z2.number().positive().optional(),
1189
+ /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
1190
+ ellipseRy: z2.number().positive().optional()
1177
1191
  }).strict();
1178
1192
  var manualPositionSchema = z2.object({
1179
1193
  x: z2.number().int(),
@@ -1185,7 +1199,11 @@ var manualLayoutConfigSchema = z2.object({
1185
1199
  mode: z2.literal("manual"),
1186
1200
  positions: z2.record(z2.string().min(1), manualPositionSchema).default({}),
1187
1201
  /** Explicit center used by curve/arc connection routing. */
1188
- diagramCenter: diagramCenterSchema.optional()
1202
+ diagramCenter: diagramCenterSchema.optional(),
1203
+ /** Horizontal radius for shared ellipse used by curveMode: 'ellipse'. */
1204
+ ellipseRx: z2.number().positive().optional(),
1205
+ /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
1206
+ ellipseRy: z2.number().positive().optional()
1189
1207
  }).strict();
1190
1208
  var layoutConfigSchema = z2.discriminatedUnion("mode", [
1191
1209
  autoLayoutConfigSchema,
@@ -1250,7 +1268,11 @@ var diagramElementSchema = z2.discriminatedUnion("type", [
1250
1268
  var diagramLayoutSchema = z2.object({
1251
1269
  mode: z2.enum(["manual", "auto"]).default("manual"),
1252
1270
  positions: z2.record(z2.string(), diagramPositionSchema).optional(),
1253
- diagramCenter: diagramCenterSchema.optional()
1271
+ diagramCenter: diagramCenterSchema.optional(),
1272
+ /** Horizontal radius for shared ellipse used by curveMode: 'ellipse'. */
1273
+ ellipseRx: z2.number().positive().optional(),
1274
+ /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
1275
+ ellipseRy: z2.number().positive().optional()
1254
1276
  }).strict();
1255
1277
  var diagramSpecSchema = z2.object({
1256
1278
  version: z2.literal(1),
@@ -2778,12 +2800,12 @@ var MACOS_DOTS = [
2778
2800
  { fill: "#27C93F", stroke: "#1AAB29" }
2779
2801
  ];
2780
2802
  function drawMacosDots(ctx, x, y) {
2781
- for (const [index, dot2] of MACOS_DOTS.entries()) {
2803
+ for (const [index, dot] of MACOS_DOTS.entries()) {
2782
2804
  ctx.beginPath();
2783
2805
  ctx.arc(x + index * DOT_SPACING, y, DOT_RADIUS, 0, Math.PI * 2);
2784
2806
  ctx.closePath();
2785
- ctx.fillStyle = dot2.fill;
2786
- ctx.strokeStyle = dot2.stroke;
2807
+ ctx.fillStyle = dot.fill;
2808
+ ctx.strokeStyle = dot.stroke;
2787
2809
  ctx.lineWidth = DOT_STROKE_WIDTH;
2788
2810
  ctx.fill();
2789
2811
  ctx.stroke();
@@ -3270,56 +3292,71 @@ function curveRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, to
3270
3292
  const cp2 = { x: p3.x + n3.x * offset, y: p3.y + n3.y * offset };
3271
3293
  return [p0, cp1, cp2, p3];
3272
3294
  }
3273
- function dot(a, b) {
3274
- return a.x * b.x + a.y * b.y;
3275
- }
3276
- function localToWorld(origin, axisX, axisY, local) {
3277
- return {
3278
- x: origin.x + axisX.x * local.x + axisY.x * local.y,
3279
- y: origin.y + axisX.y * local.x + axisY.y * local.y
3280
- };
3295
+ function inferEllipseParams(nodeBounds, explicitCenter, explicitRx, explicitRy) {
3296
+ if (nodeBounds.length === 0) {
3297
+ return {
3298
+ cx: explicitCenter?.x ?? 0,
3299
+ cy: explicitCenter?.y ?? 0,
3300
+ rx: explicitRx ?? 1,
3301
+ ry: explicitRy ?? 1
3302
+ };
3303
+ }
3304
+ const centers = nodeBounds.map(rectCenter);
3305
+ const cx = explicitCenter?.x ?? centers.reduce((sum, c) => sum + c.x, 0) / centers.length;
3306
+ const cy = explicitCenter?.y ?? centers.reduce((sum, c) => sum + c.y, 0) / centers.length;
3307
+ const rx = explicitRx ?? Math.max(1, ...centers.map((c) => Math.abs(c.x - cx)));
3308
+ const ry = explicitRy ?? Math.max(1, ...centers.map((c) => Math.abs(c.y - cy)));
3309
+ return { cx, cy, rx, ry };
3281
3310
  }
3282
- function arcRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
3311
+ function ellipseRoute(fromBounds, toBounds, ellipse, fromAnchor, toAnchor) {
3283
3312
  const fromCenter = rectCenter(fromBounds);
3284
3313
  const toCenter = rectCenter(toBounds);
3285
- const start = resolveAnchor(fromBounds, fromAnchor, toCenter);
3286
- const end = resolveAnchor(toBounds, toAnchor, fromCenter);
3287
- const chord = { x: end.x - start.x, y: end.y - start.y };
3288
- const chordLength = Math.hypot(chord.x, chord.y);
3289
- if (chordLength < 1e-6) {
3290
- const mid = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
3291
- return [
3292
- [start, start, mid, mid],
3293
- [mid, mid, end, end]
3294
- ];
3295
- }
3296
- const axisX = { x: chord.x / chordLength, y: chord.y / chordLength };
3297
- let axisY = { x: -axisX.y, y: axisX.x };
3298
- const midpoint = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
3299
- const outwardHint = outwardNormal(midpoint, diagramCenter);
3300
- if (dot(axisY, outwardHint) < 0) {
3301
- axisY = { x: -axisY.x, y: -axisY.y };
3302
- }
3303
- const semiMajor = chordLength / 2;
3304
- const semiMinor = Math.max(12, chordLength * tension * 0.75);
3305
- const p0Local = { x: -semiMajor, y: 0 };
3306
- const cp1Local = { x: -semiMajor, y: ELLIPSE_KAPPA * semiMinor };
3307
- const cp2Local = { x: -ELLIPSE_KAPPA * semiMajor, y: semiMinor };
3308
- const pMidLocal = { x: 0, y: semiMinor };
3309
- const cp3Local = { x: ELLIPSE_KAPPA * semiMajor, y: semiMinor };
3310
- const cp4Local = { x: semiMajor, y: ELLIPSE_KAPPA * semiMinor };
3311
- const p3Local = { x: semiMajor, y: 0 };
3312
- const p0 = localToWorld(midpoint, axisX, axisY, p0Local);
3313
- const cp1 = localToWorld(midpoint, axisX, axisY, cp1Local);
3314
- const cp2 = localToWorld(midpoint, axisX, axisY, cp2Local);
3315
- const pMid = localToWorld(midpoint, axisX, axisY, pMidLocal);
3316
- const cp3 = localToWorld(midpoint, axisX, axisY, cp3Local);
3317
- const cp4 = localToWorld(midpoint, axisX, axisY, cp4Local);
3318
- const p3 = localToWorld(midpoint, axisX, axisY, p3Local);
3319
- return [
3320
- [p0, cp1, cp2, pMid],
3321
- [pMid, cp3, cp4, p3]
3322
- ];
3314
+ const p0 = resolveAnchor(fromBounds, fromAnchor, toCenter);
3315
+ const p3 = resolveAnchor(toBounds, toAnchor, fromCenter);
3316
+ const theta1 = Math.atan2(
3317
+ (fromCenter.y - ellipse.cy) / ellipse.ry,
3318
+ (fromCenter.x - ellipse.cx) / ellipse.rx
3319
+ );
3320
+ const theta2 = Math.atan2(
3321
+ (toCenter.y - ellipse.cy) / ellipse.ry,
3322
+ (toCenter.x - ellipse.cx) / ellipse.rx
3323
+ );
3324
+ let angularSpan = theta2 - theta1;
3325
+ while (angularSpan > Math.PI) angularSpan -= 2 * Math.PI;
3326
+ while (angularSpan <= -Math.PI) angularSpan += 2 * Math.PI;
3327
+ const absSpan = Math.abs(angularSpan);
3328
+ const kappa = absSpan < 1e-6 ? 0 : 4 / 3 * Math.tan(absSpan / 4);
3329
+ const tangent1 = {
3330
+ x: -ellipse.rx * Math.sin(theta1),
3331
+ y: ellipse.ry * Math.cos(theta1)
3332
+ };
3333
+ const tangent2 = {
3334
+ x: -ellipse.rx * Math.sin(theta2),
3335
+ y: ellipse.ry * Math.cos(theta2)
3336
+ };
3337
+ const len1 = Math.hypot(tangent1.x, tangent1.y) || 1;
3338
+ const len2 = Math.hypot(tangent2.x, tangent2.y) || 1;
3339
+ const norm1 = { x: tangent1.x / len1, y: tangent1.y / len1 };
3340
+ const norm2 = { x: tangent2.x / len2, y: tangent2.y / len2 };
3341
+ const chordLength = Math.hypot(p3.x - p0.x, p3.y - p0.y);
3342
+ const cpDistance = chordLength * kappa * 0.5;
3343
+ const sign = angularSpan >= 0 ? 1 : -1;
3344
+ const cp1 = {
3345
+ x: p0.x + norm1.x * cpDistance * sign,
3346
+ y: p0.y + norm1.y * cpDistance * sign
3347
+ };
3348
+ const cp2 = {
3349
+ x: p3.x - norm2.x * cpDistance * sign,
3350
+ y: p3.y - norm2.y * cpDistance * sign
3351
+ };
3352
+ return [p0, cp1, cp2, p3];
3353
+ }
3354
+ function straightRoute(fromBounds, toBounds, fromAnchor, toAnchor) {
3355
+ const fromC = rectCenter(fromBounds);
3356
+ const toC = rectCenter(toBounds);
3357
+ const p0 = resolveAnchor(fromBounds, fromAnchor, toC);
3358
+ const p3 = resolveAnchor(toBounds, toAnchor, fromC);
3359
+ return [p0, p3];
3323
3360
  }
3324
3361
  function orthogonalRoute(fromBounds, toBounds, fromAnchor, toAnchor) {
3325
3362
  const fromC = rectCenter(fromBounds);
@@ -3365,15 +3402,6 @@ function findBoundaryIntersection(p0, cp1, cp2, p3, targetRect, searchFromEnd) {
3365
3402
  }
3366
3403
  return void 0;
3367
3404
  }
3368
- function pointAlongArc(route, t) {
3369
- const [first, second] = route;
3370
- if (t <= 0.5) {
3371
- const localT2 = Math.max(0, Math.min(1, t * 2));
3372
- return bezierPointAt(first[0], first[1], first[2], first[3], localT2);
3373
- }
3374
- const localT = Math.max(0, Math.min(1, (t - 0.5) * 2));
3375
- return bezierPointAt(second[0], second[1], second[2], second[3], localT);
3376
- }
3377
3405
  function computeDiagramCenter(nodeBounds, canvasCenter) {
3378
3406
  if (nodeBounds.length === 0) {
3379
3407
  return canvasCenter ?? { x: 0, y: 0 };
@@ -3496,8 +3524,19 @@ function polylineBounds(points) {
3496
3524
  };
3497
3525
  }
3498
3526
  function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, options) {
3499
- const routing = conn.routing ?? "auto";
3500
- const strokeStyle = conn.strokeStyle ?? conn.style ?? "solid";
3527
+ let routing = conn.routing ?? "auto";
3528
+ let curveMode = conn.curveMode ?? "normal";
3529
+ if (conn.strokeStyle !== void 0) {
3530
+ console.warn("connection.strokeStyle is deprecated, use style instead");
3531
+ }
3532
+ if (routing === "arc") {
3533
+ console.warn(
3534
+ "connection routing: 'arc' is deprecated. Use routing: 'curve' with curveMode: 'ellipse' instead."
3535
+ );
3536
+ routing = "curve";
3537
+ curveMode = "ellipse";
3538
+ }
3539
+ const strokeStyle = conn.style ?? conn.strokeStyle ?? "solid";
3501
3540
  const strokeWidth = conn.width ?? conn.strokeWidth ?? 2;
3502
3541
  const tension = conn.tension ?? 0.35;
3503
3542
  const dash = dashFromStyle(strokeStyle);
@@ -3519,14 +3558,29 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
3519
3558
  ctx.globalAlpha = conn.opacity;
3520
3559
  const arrowPlacement = conn.arrowPlacement ?? "endpoint";
3521
3560
  if (routing === "curve") {
3522
- const [p0, cp1, cp2, p3] = curveRoute(
3523
- fromBounds,
3524
- toBounds,
3525
- diagramCenter,
3526
- tension,
3527
- conn.fromAnchor,
3528
- conn.toAnchor
3529
- );
3561
+ let p0;
3562
+ let cp1;
3563
+ let cp2;
3564
+ let p3;
3565
+ if (curveMode === "ellipse") {
3566
+ const ellipse = options?.ellipseParams ?? inferEllipseParams([fromBounds, toBounds]);
3567
+ [p0, cp1, cp2, p3] = ellipseRoute(
3568
+ fromBounds,
3569
+ toBounds,
3570
+ ellipse,
3571
+ conn.fromAnchor,
3572
+ conn.toAnchor
3573
+ );
3574
+ } else {
3575
+ [p0, cp1, cp2, p3] = curveRoute(
3576
+ fromBounds,
3577
+ toBounds,
3578
+ diagramCenter,
3579
+ tension,
3580
+ conn.fromAnchor,
3581
+ conn.toAnchor
3582
+ );
3583
+ }
3530
3584
  const stroke = resolveConnectionStroke(ctx, p0, p3, conn.fromColor, style.color, conn.toColor);
3531
3585
  ctx.strokeStyle = stroke;
3532
3586
  ctx.lineWidth = style.width;
@@ -3559,51 +3613,22 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
3559
3613
  }
3560
3614
  }
3561
3615
  }
3562
- } else if (routing === "arc") {
3563
- const [first, second] = arcRoute(
3564
- fromBounds,
3565
- toBounds,
3566
- diagramCenter,
3567
- tension,
3568
- conn.fromAnchor,
3569
- conn.toAnchor
3570
- );
3571
- const [p0, cp1, cp2, pMid] = first;
3572
- const [, cp3, cp4, p3] = second;
3616
+ } else if (routing === "straight") {
3617
+ const [p0, p3] = straightRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor);
3573
3618
  const stroke = resolveConnectionStroke(ctx, p0, p3, conn.fromColor, style.color, conn.toColor);
3574
3619
  ctx.strokeStyle = stroke;
3575
3620
  ctx.lineWidth = style.width;
3576
3621
  ctx.setLineDash(style.dash ?? []);
3577
3622
  ctx.beginPath();
3578
3623
  ctx.moveTo(p0.x, p0.y);
3579
- ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, pMid.x, pMid.y);
3580
- ctx.bezierCurveTo(cp3.x, cp3.y, cp4.x, cp4.y, p3.x, p3.y);
3624
+ ctx.lineTo(p3.x, p3.y);
3581
3625
  ctx.stroke();
3582
- linePoints = [p0, cp1, cp2, pMid, cp3, cp4, p3];
3626
+ linePoints = [p0, p3];
3583
3627
  startPoint = p0;
3584
3628
  endPoint = p3;
3585
- startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
3586
- endAngle = Math.atan2(p3.y - cp4.y, p3.x - cp4.x);
3587
- labelPoint = pointAlongArc([first, second], labelT);
3588
- if (arrowPlacement === "boundary") {
3589
- if (conn.arrow === "end" || conn.arrow === "both") {
3590
- const [, s_cp3, s_cp4, s_p3] = second;
3591
- const tEnd = findBoundaryIntersection(pMid, s_cp3, s_cp4, s_p3, toBounds, true);
3592
- if (tEnd !== void 0) {
3593
- endPoint = bezierPointAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
3594
- const tangent = bezierTangentAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
3595
- endAngle = Math.atan2(tangent.y, tangent.x);
3596
- }
3597
- }
3598
- if (conn.arrow === "start" || conn.arrow === "both") {
3599
- const tStart = findBoundaryIntersection(p0, cp1, cp2, pMid, fromBounds, false);
3600
- if (tStart !== void 0) {
3601
- startPoint = bezierPointAt(p0, cp1, cp2, pMid, tStart);
3602
- const tangent = bezierTangentAt(p0, cp1, cp2, pMid, tStart);
3603
- startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
3604
- }
3605
- }
3606
- }
3629
+ startAngle = Math.atan2(p0.y - p3.y, p0.x - p3.x);
3630
+ endAngle = Math.atan2(p3.y - p0.y, p3.x - p0.x);
3631
+ labelPoint = pointAlongPolyline(linePoints, labelT);
3607
3632
  } else {
3608
3633
  const hasAnchorHints = conn.fromAnchor !== void 0 || conn.toAnchor !== void 0;
3609
3634
  const useElkRoute = routing === "auto" && !hasAnchorHints && (edgeRoute?.points.length ?? 0) >= 2;
@@ -5032,10 +5057,19 @@ async function renderDesign(input, options = {}) {
5032
5057
  break;
5033
5058
  }
5034
5059
  }
5035
- const diagramCenter = spec.layout.diagramCenter ?? computeDiagramCenter(
5036
- spec.elements.filter((element) => element.type !== "connection").map((element) => elementRects.get(element.id)).filter((rect) => rect != null),
5037
- { x: spec.canvas.width / 2, y: spec.canvas.height / 2 }
5060
+ const nodeBounds = spec.elements.filter((element) => element.type !== "connection").map((element) => elementRects.get(element.id)).filter((rect) => rect != null);
5061
+ const diagramCenter = spec.layout.diagramCenter ?? computeDiagramCenter(nodeBounds, { x: spec.canvas.width / 2, y: spec.canvas.height / 2 });
5062
+ const layoutEllipseRx = "ellipseRx" in spec.layout ? spec.layout.ellipseRx : void 0;
5063
+ const layoutEllipseRy = "ellipseRy" in spec.layout ? spec.layout.ellipseRy : void 0;
5064
+ const hasEllipseConnections = spec.elements.some(
5065
+ (el) => el.type === "connection" && (el.curveMode === "ellipse" || el.routing === "arc")
5038
5066
  );
5067
+ const ellipseParams = hasEllipseConnections ? inferEllipseParams(
5068
+ nodeBounds,
5069
+ spec.layout.diagramCenter ?? diagramCenter,
5070
+ layoutEllipseRx,
5071
+ layoutEllipseRy
5072
+ ) : void 0;
5039
5073
  for (const element of spec.elements) {
5040
5074
  if (element.type !== "connection") {
5041
5075
  continue;
@@ -5049,7 +5083,15 @@ async function renderDesign(input, options = {}) {
5049
5083
  }
5050
5084
  const edgeRoute = edgeRoutes?.get(`${element.from}-${element.to}`);
5051
5085
  elements.push(
5052
- ...renderConnection(ctx, element, fromRect, toRect, theme, edgeRoute, { diagramCenter })
5086
+ ...renderConnection(
5087
+ ctx,
5088
+ element,
5089
+ fromRect,
5090
+ toRect,
5091
+ theme,
5092
+ edgeRoute,
5093
+ ellipseParams ? { diagramCenter, ellipseParams } : { diagramCenter }
5094
+ )
5053
5095
  );
5054
5096
  }
5055
5097
  if (footerRect && spec.footer) {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Cli } from 'incur';
2
- import { T as ThemeInput, D as DesignSpec, a as Rect$1, b as DrawCommand, c as Theme, d as RenderedElement, A as AnchorHint, C as ConnectionElement } from './spec.schema-B6sXTTou.js';
3
- export { e as AutoLayoutConfig, B as BuiltInTheme, f as CardElement, g as CodeBlockElement, h as ConstraintSpec, i as DEFAULT_GENERATOR_VERSION, j as DEFAULT_RAINBOW_COLORS, k as Decorator, l as DesignCardSpec, m as DesignSafeFrame, n as DesignTheme, o as DiagramElement, p as DiagramLayout, q as DiagramSpec, r as DrawArc, s as DrawBadge, t as DrawBezier, u as DrawCircle, v as DrawFontFamily, w as DrawGradientRect, x as DrawLine, y as DrawPath, z as DrawPoint, E as DrawRect, F as DrawShadow, G as DrawText, H as DrawTextRow, I as DrawTextRowSegment, J as Element, K as FlowNodeElement, L as Gradient, M as GradientOverlayDecorator, N as GradientSpec, O as GradientStop, P as GridLayoutConfig, Q as ImageElement, S as IterationMeta, U as LayoutConfig, V as LayoutSnapshot, W as ManualLayoutConfig, X as RainbowRuleDecorator, Y as RenderDesignOptions, R as RenderMetadata, Z as RenderResult, _ as ShapeElement, $ as StackLayoutConfig, a0 as TerminalElement, a1 as TextElement, a2 as ThemeInput, a3 as VignetteDecorator, a4 as WrittenArtifacts, a5 as builtInThemeBackgrounds, a6 as builtInThemes, a7 as computeSpecHash, a8 as connectionElementSchema, a9 as defaultAutoLayout, aa as defaultCanvas, ab as defaultConstraints, ac as defaultGridLayout, ad as defaultLayout, ae as defaultStackLayout, af as defaultTheme, ag as deriveSafeFrame, ah as designSpecSchema, ai as diagramElementSchema, aj as diagramLayoutSchema, ak as diagramSpecSchema, al as drawGradientRect, am as drawRainbowRule, an as drawVignette, ao as flowNodeElementSchema, ap as inferLayout, aq as inferSidecarPath, ar as parseDesignSpec, as as parseDiagramSpec, at as renderDesign, au as resolveTheme, av as writeRenderArtifacts } from './spec.schema-B6sXTTou.js';
2
+ import { T as ThemeInput, D as DesignSpec, a as Rect$1, b as DrawCommand, c as Theme, d as RenderedElement, A as AnchorHint, C as ConnectionElement } from './spec.schema-CYlOLxmK.js';
3
+ export { e as AutoLayoutConfig, B as BuiltInTheme, f as CardElement, g as CodeBlockElement, h as ConstraintSpec, i as DEFAULT_GENERATOR_VERSION, j as DEFAULT_RAINBOW_COLORS, k as Decorator, l as DesignCardSpec, m as DesignSafeFrame, n as DesignTheme, o as DiagramElement, p as DiagramLayout, q as DiagramSpec, r as DrawArc, s as DrawBadge, t as DrawBezier, u as DrawCircle, v as DrawFontFamily, w as DrawGradientRect, x as DrawLine, y as DrawPath, z as DrawPoint, E as DrawRect, F as DrawShadow, G as DrawText, H as DrawTextRow, I as DrawTextRowSegment, J as Element, K as FlowNodeElement, L as Gradient, M as GradientOverlayDecorator, N as GradientSpec, O as GradientStop, P as GridLayoutConfig, Q as ImageElement, S as IterationMeta, U as LayoutConfig, V as LayoutSnapshot, W as ManualLayoutConfig, X as RainbowRuleDecorator, Y as RenderDesignOptions, R as RenderMetadata, Z as RenderResult, _ as ShapeElement, $ as StackLayoutConfig, a0 as TerminalElement, a1 as TextElement, a2 as ThemeInput, a3 as VignetteDecorator, a4 as WrittenArtifacts, a5 as builtInThemeBackgrounds, a6 as builtInThemes, a7 as computeSpecHash, a8 as connectionElementSchema, a9 as defaultAutoLayout, aa as defaultCanvas, ab as defaultConstraints, ac as defaultGridLayout, ad as defaultLayout, ae as defaultStackLayout, af as defaultTheme, ag as deriveSafeFrame, ah as designSpecSchema, ai as diagramElementSchema, aj as diagramLayoutSchema, ak as diagramSpecSchema, al as drawGradientRect, am as drawRainbowRule, an as drawVignette, ao as flowNodeElementSchema, ap as inferLayout, aq as inferSidecarPath, ar as parseDesignSpec, as as parseDiagramSpec, at as renderDesign, au as resolveTheme, av as writeRenderArtifacts } from './spec.schema-CYlOLxmK.js';
4
4
  import { SKRSContext2D } from '@napi-rs/canvas';
5
5
  export { QaIssue, QaReferenceResult, QaReport, QaSeverity, readMetadata, runQa } from './qa.js';
6
6
  import { Highlighter } from 'shiki';
@@ -245,9 +245,16 @@ type Point$1 = {
245
245
 
246
246
  type Point = Point$1;
247
247
  type Rect = Rect$1;
248
- type ConnectionRouting = 'auto' | 'orthogonal' | 'curve' | 'arc';
248
+ type ConnectionRouting = 'auto' | 'orthogonal' | 'curve' | 'arc' | 'straight';
249
+ type ConnectionCurveMode = 'normal' | 'ellipse';
249
250
  type ConnectionArrow = 'none' | 'end' | 'start' | 'both';
250
251
  type ConnectionStrokeStyle = 'solid' | 'dashed' | 'dotted';
252
+ type EllipseParams = {
253
+ cx: number;
254
+ cy: number;
255
+ rx: number;
256
+ ry: number;
257
+ };
251
258
  type ConnectionRenderOptions = {
252
259
  fromBounds: Rect;
253
260
  toBounds: Rect;
@@ -292,6 +299,38 @@ declare function curveRoute(fromBounds: Rect, toBounds: Rect, diagramCenter: Poi
292
299
  * automatic edge anchor calculation.
293
300
  */
294
301
  declare function arcRoute(fromBounds: Rect, toBounds: Rect, diagramCenter: Point, tension: number, fromAnchor?: AnchorHint, toAnchor?: AnchorHint): [CubicBezierSegment, CubicBezierSegment];
302
+ /**
303
+ * Infer ellipse parameters from the bounding boxes of all flow-node elements.
304
+ *
305
+ * When explicit `ellipseRx`/`ellipseRy` are provided they are used directly.
306
+ * Otherwise the ellipse is fitted by computing the centroid of all node centers
307
+ * as the center, with `rx` and `ry` derived from the maximum horizontal and
308
+ * vertical distance from the centroid to any node center.
309
+ */
310
+ declare function inferEllipseParams(nodeBounds: Rect[], explicitCenter?: Point, explicitRx?: number, explicitRy?: number): EllipseParams;
311
+ /**
312
+ * Compute a cubic bezier curve that traces an arc on a shared global ellipse.
313
+ *
314
+ * Uses the generalized kappa formula: `κ = (4/3) × tan(angularSpan / 4)`
315
+ * to produce control points from the ellipse tangent vectors at the source
316
+ * and target angles.
317
+ *
318
+ * The source and target points are the edge anchors of the respective node
319
+ * bounding boxes (not points on the ellipse itself), but the control points
320
+ * are derived from the ellipse tangent at the angle each node center makes
321
+ * with the ellipse center. This produces curves that follow the global
322
+ * ellipse arc while connecting node boundaries correctly.
323
+ *
324
+ * @returns `[startPoint, controlPoint1, controlPoint2, endPoint]`
325
+ */
326
+ declare function ellipseRoute(fromBounds: Rect, toBounds: Rect, ellipse: EllipseParams, fromAnchor?: AnchorHint, toAnchor?: AnchorHint): [Point, Point, Point, Point];
327
+ /**
328
+ * Compute a straight-line route between two rectangles.
329
+ *
330
+ * When `fromAnchor` or `toAnchor` hints are provided, they override the
331
+ * automatic edge anchor calculation.
332
+ */
333
+ declare function straightRoute(fromBounds: Rect, toBounds: Rect, fromAnchor?: AnchorHint, toAnchor?: AnchorHint): [Point, Point];
295
334
  /**
296
335
  * Compute an orthogonal (right-angle) path between two rectangles.
297
336
  * Returns an array of waypoints forming a 3-segment path.
@@ -309,6 +348,7 @@ declare function bezierPointAt(p0: Point, cp1: Point, cp2: Point, p3: Point, t:
309
348
  declare function computeDiagramCenter(nodeBounds: Rect[], canvasCenter?: Point): Point;
310
349
  declare function renderConnection(ctx: SKRSContext2D, conn: ConnectionElement, fromBounds: Rect, toBounds: Rect, theme: Theme, edgeRoute?: EdgeRoute, options?: {
311
350
  diagramCenter?: Point;
351
+ ellipseParams?: EllipseParams;
312
352
  }): RenderedElement[];
313
353
 
314
354
  /**
@@ -366,4 +406,4 @@ declare function highlightCode(code: string, language: string, themeName: string
366
406
  */
367
407
  declare function disposeHighlighter(): void;
368
408
 
369
- export { type CompareImagesOptions, type CompareImagesReport, type CompareRegionScore, type CompareVerdict, type ConnectionArrow, ConnectionElement, type ConnectionRenderOptions, type ConnectionRouting, type ConnectionStrokeStyle, type CubicBezierSegment, DesignSpec, DrawCommand, type EdgeRoute, type ElkLayoutResult, type HighlightedLine, type LayoutResult, type Point, Rect$1 as Rect, RenderedElement, Theme, arcRoute, bezierPointAt, buildCardsSpec, buildCodeSpec, buildFlowchartSpec, buildTerminalSpec, cli, compareImages, computeDiagramCenter, curveRoute, disposeHighlighter, edgeAnchor, highlightCode, initHighlighter, loadFonts, orthogonalRoute, outwardNormal, rectCenter, renderConnection, renderDrawCommands, resolveShikiTheme, themeToShikiMap };
409
+ export { type CompareImagesOptions, type CompareImagesReport, type CompareRegionScore, type CompareVerdict, type ConnectionArrow, type ConnectionCurveMode, ConnectionElement, type ConnectionRenderOptions, type ConnectionRouting, type ConnectionStrokeStyle, type CubicBezierSegment, DesignSpec, DrawCommand, type EdgeRoute, type ElkLayoutResult, type EllipseParams, type HighlightedLine, type LayoutResult, type Point, Rect$1 as Rect, RenderedElement, Theme, arcRoute, bezierPointAt, buildCardsSpec, buildCodeSpec, buildFlowchartSpec, buildTerminalSpec, cli, compareImages, computeDiagramCenter, curveRoute, disposeHighlighter, edgeAnchor, ellipseRoute, highlightCode, inferEllipseParams, initHighlighter, loadFonts, orthogonalRoute, outwardNormal, rectCenter, renderConnection, renderDrawCommands, resolveShikiTheme, straightRoute, themeToShikiMap };