@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 +158 -116
- package/dist/index.d.ts +44 -4
- package/dist/index.js +162 -66
- package/dist/qa.d.ts +1 -1
- package/dist/qa.js +29 -7
- package/dist/renderer.d.ts +1 -1
- package/dist/renderer.js +158 -116
- package/dist/{spec.schema-B6sXTTou.d.ts → spec.schema-CYlOLxmK.d.ts} +1395 -1221
- package/dist/spec.schema.d.ts +1 -1
- package/dist/spec.schema.js +29 -7
- package/package.json +1 -1
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
|
-
|
|
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,
|
|
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 =
|
|
2786
|
-
ctx.strokeStyle =
|
|
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
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
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
|
|
3311
|
+
function ellipseRoute(fromBounds, toBounds, ellipse, fromAnchor, toAnchor) {
|
|
3283
3312
|
const fromCenter = rectCenter(fromBounds);
|
|
3284
3313
|
const toCenter = rectCenter(toBounds);
|
|
3285
|
-
const
|
|
3286
|
-
const
|
|
3287
|
-
const
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
const
|
|
3299
|
-
const
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
const
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
const
|
|
3309
|
-
const
|
|
3310
|
-
const
|
|
3311
|
-
const
|
|
3312
|
-
const
|
|
3313
|
-
const
|
|
3314
|
-
const
|
|
3315
|
-
const
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
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
|
-
|
|
3500
|
-
|
|
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
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
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 === "
|
|
3563
|
-
const [
|
|
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.
|
|
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,
|
|
3626
|
+
linePoints = [p0, p3];
|
|
3583
3627
|
startPoint = p0;
|
|
3584
3628
|
endPoint = p3;
|
|
3585
|
-
startAngle = Math.atan2(p0.y -
|
|
3586
|
-
endAngle = Math.atan2(p3.y -
|
|
3587
|
-
labelPoint =
|
|
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
|
|
5036
|
-
|
|
5037
|
-
|
|
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(
|
|
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-
|
|
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-
|
|
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 };
|