@spectratools/graphic-designer-cli 0.8.0 → 0.10.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 +273 -19
- package/dist/index.d.ts +6 -6
- package/dist/index.js +273 -19
- package/dist/qa.d.ts +1 -1
- package/dist/qa.js +18 -0
- package/dist/renderer.d.ts +1 -1
- package/dist/renderer.js +214 -17
- package/dist/{spec.schema-B_Z-KNqt.d.ts → spec.schema-B6sXTTou.d.ts} +1664 -1314
- package/dist/spec.schema.d.ts +1 -1
- package/dist/spec.schema.js +18 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -820,6 +820,21 @@ var drawLineSchema = z2.object({
|
|
|
820
820
|
opacity: z2.number().min(0).max(1).default(1),
|
|
821
821
|
shadow: drawShadowSchema.optional()
|
|
822
822
|
}).strict();
|
|
823
|
+
var drawArcSchema = z2.object({
|
|
824
|
+
type: z2.literal("arc"),
|
|
825
|
+
center: z2.object({
|
|
826
|
+
x: z2.number(),
|
|
827
|
+
y: z2.number()
|
|
828
|
+
}).strict(),
|
|
829
|
+
radius: z2.number().positive(),
|
|
830
|
+
startAngle: z2.number(),
|
|
831
|
+
endAngle: z2.number(),
|
|
832
|
+
color: colorHexSchema2.default("#FFFFFF"),
|
|
833
|
+
width: z2.number().min(0.5).max(32).default(2),
|
|
834
|
+
dash: z2.array(z2.number()).max(6).optional(),
|
|
835
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
836
|
+
shadow: drawShadowSchema.optional()
|
|
837
|
+
}).strict();
|
|
823
838
|
var drawPointSchema = z2.object({
|
|
824
839
|
x: z2.number(),
|
|
825
840
|
y: z2.number()
|
|
@@ -904,6 +919,7 @@ var drawCommandSchema = z2.discriminatedUnion("type", [
|
|
|
904
919
|
drawCircleSchema,
|
|
905
920
|
drawTextSchema,
|
|
906
921
|
drawLineSchema,
|
|
922
|
+
drawArcSchema,
|
|
907
923
|
drawBezierSchema,
|
|
908
924
|
drawPathSchema,
|
|
909
925
|
drawBadgeSchema,
|
|
@@ -1039,6 +1055,8 @@ var connectionElementSchema = z2.object({
|
|
|
1039
1055
|
label: z2.string().min(1).max(200).optional(),
|
|
1040
1056
|
labelPosition: z2.enum(["start", "middle", "end"]).default("middle"),
|
|
1041
1057
|
color: colorHexSchema2.optional(),
|
|
1058
|
+
fromColor: colorHexSchema2.optional(),
|
|
1059
|
+
toColor: colorHexSchema2.optional(),
|
|
1042
1060
|
width: z2.number().min(0.5).max(10).optional(),
|
|
1043
1061
|
strokeWidth: z2.number().min(0.5).max(10).default(2),
|
|
1044
1062
|
arrowSize: z2.number().min(4).max(32).optional(),
|
|
@@ -1867,6 +1885,38 @@ function renderFlowNode(ctx, node, bounds, theme) {
|
|
|
1867
1885
|
ctx.shadowOffsetX = 0;
|
|
1868
1886
|
ctx.shadowOffsetY = 0;
|
|
1869
1887
|
}
|
|
1888
|
+
if (node.accentColor) {
|
|
1889
|
+
const barWidth = node.accentBarWidth ?? 3;
|
|
1890
|
+
const effectiveRadius = node.shape === "box" ? 0 : cornerRadius;
|
|
1891
|
+
ctx.save();
|
|
1892
|
+
ctx.beginPath();
|
|
1893
|
+
ctx.roundRect(bounds.x, bounds.y, bounds.width, bounds.height, effectiveRadius);
|
|
1894
|
+
ctx.clip();
|
|
1895
|
+
ctx.fillStyle = node.accentColor;
|
|
1896
|
+
ctx.fillRect(bounds.x, bounds.y, barWidth, bounds.height);
|
|
1897
|
+
ctx.restore();
|
|
1898
|
+
}
|
|
1899
|
+
if (node.glowColor) {
|
|
1900
|
+
const glowW = node.glowWidth ?? 16;
|
|
1901
|
+
const glowOp = node.glowOpacity ?? 0.15;
|
|
1902
|
+
ctx.save();
|
|
1903
|
+
ctx.beginPath();
|
|
1904
|
+
ctx.roundRect(bounds.x, bounds.y, bounds.width, bounds.height, cornerRadius);
|
|
1905
|
+
ctx.clip();
|
|
1906
|
+
const barOffset = node.accentColor ? node.accentBarWidth ?? 3 : 0;
|
|
1907
|
+
const gradient = ctx.createLinearGradient(
|
|
1908
|
+
bounds.x + barOffset,
|
|
1909
|
+
bounds.y,
|
|
1910
|
+
bounds.x + barOffset + glowW,
|
|
1911
|
+
bounds.y
|
|
1912
|
+
);
|
|
1913
|
+
gradient.addColorStop(0, node.glowColor);
|
|
1914
|
+
gradient.addColorStop(1, "rgba(0,0,0,0)");
|
|
1915
|
+
ctx.globalAlpha = glowOp;
|
|
1916
|
+
ctx.fillStyle = gradient;
|
|
1917
|
+
ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1918
|
+
ctx.restore();
|
|
1919
|
+
}
|
|
1870
1920
|
const headingFont = resolveFont(theme.fonts.heading, "heading");
|
|
1871
1921
|
const bodyFont = resolveFont(theme.fonts.body, "body");
|
|
1872
1922
|
const monoFont = resolveFont(theme.fonts.mono, "mono");
|
|
@@ -3138,16 +3188,6 @@ function drawBezier(ctx, points, style) {
|
|
|
3138
3188
|
ctx.quadraticCurveTo(penultimate.x, penultimate.y, last.x, last.y);
|
|
3139
3189
|
ctx.stroke();
|
|
3140
3190
|
}
|
|
3141
|
-
function drawOrthogonalPath(ctx, from, to, style) {
|
|
3142
|
-
const midX = (from.x + to.x) / 2;
|
|
3143
|
-
applyLineStyle(ctx, style);
|
|
3144
|
-
ctx.beginPath();
|
|
3145
|
-
ctx.moveTo(from.x, from.y);
|
|
3146
|
-
ctx.lineTo(midX, from.y);
|
|
3147
|
-
ctx.lineTo(midX, to.y);
|
|
3148
|
-
ctx.lineTo(to.x, to.y);
|
|
3149
|
-
ctx.stroke();
|
|
3150
|
-
}
|
|
3151
3191
|
|
|
3152
3192
|
// src/renderers/connection.ts
|
|
3153
3193
|
var ELLIPSE_KAPPA = 4 * (Math.sqrt(2) - 1) / 3;
|
|
@@ -3387,11 +3427,36 @@ function pointAlongPolyline(points, t) {
|
|
|
3387
3427
|
}
|
|
3388
3428
|
return points[points.length - 1];
|
|
3389
3429
|
}
|
|
3390
|
-
function
|
|
3430
|
+
function createConnectionGradient(ctx, start, end, fromColor, baseColor, toColor) {
|
|
3431
|
+
const gradient = ctx.createLinearGradient(start.x, start.y, end.x, end.y);
|
|
3432
|
+
gradient.addColorStop(0, fromColor);
|
|
3433
|
+
gradient.addColorStop(0.5, baseColor);
|
|
3434
|
+
gradient.addColorStop(1, toColor);
|
|
3435
|
+
return gradient;
|
|
3436
|
+
}
|
|
3437
|
+
function resolveConnectionStroke(ctx, start, end, fromColor, baseColor, toColor) {
|
|
3438
|
+
if (!fromColor || !toColor) {
|
|
3439
|
+
return baseColor;
|
|
3440
|
+
}
|
|
3441
|
+
return createConnectionGradient(ctx, start, end, fromColor, baseColor, toColor);
|
|
3442
|
+
}
|
|
3443
|
+
function drawOrthogonalPathWithStroke(ctx, from, to, style, stroke) {
|
|
3444
|
+
const midX = (from.x + to.x) / 2;
|
|
3445
|
+
ctx.strokeStyle = stroke;
|
|
3446
|
+
ctx.lineWidth = style.width;
|
|
3447
|
+
ctx.setLineDash(style.dash ?? []);
|
|
3448
|
+
ctx.beginPath();
|
|
3449
|
+
ctx.moveTo(from.x, from.y);
|
|
3450
|
+
ctx.lineTo(midX, from.y);
|
|
3451
|
+
ctx.lineTo(midX, to.y);
|
|
3452
|
+
ctx.lineTo(to.x, to.y);
|
|
3453
|
+
ctx.stroke();
|
|
3454
|
+
}
|
|
3455
|
+
function drawCubicInterpolatedPath(ctx, points, style, stroke) {
|
|
3391
3456
|
if (points.length < 2) {
|
|
3392
3457
|
return;
|
|
3393
3458
|
}
|
|
3394
|
-
ctx.strokeStyle =
|
|
3459
|
+
ctx.strokeStyle = stroke;
|
|
3395
3460
|
ctx.lineWidth = style.width;
|
|
3396
3461
|
ctx.setLineDash(style.dash ?? []);
|
|
3397
3462
|
ctx.beginPath();
|
|
@@ -3462,7 +3527,8 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
|
|
|
3462
3527
|
conn.fromAnchor,
|
|
3463
3528
|
conn.toAnchor
|
|
3464
3529
|
);
|
|
3465
|
-
ctx.
|
|
3530
|
+
const stroke = resolveConnectionStroke(ctx, p0, p3, conn.fromColor, style.color, conn.toColor);
|
|
3531
|
+
ctx.strokeStyle = stroke;
|
|
3466
3532
|
ctx.lineWidth = style.width;
|
|
3467
3533
|
ctx.setLineDash(style.dash ?? []);
|
|
3468
3534
|
ctx.beginPath();
|
|
@@ -3504,7 +3570,8 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
|
|
|
3504
3570
|
);
|
|
3505
3571
|
const [p0, cp1, cp2, pMid] = first;
|
|
3506
3572
|
const [, cp3, cp4, p3] = second;
|
|
3507
|
-
ctx.
|
|
3573
|
+
const stroke = resolveConnectionStroke(ctx, p0, p3, conn.fromColor, style.color, conn.toColor);
|
|
3574
|
+
ctx.strokeStyle = stroke;
|
|
3508
3575
|
ctx.lineWidth = style.width;
|
|
3509
3576
|
ctx.setLineDash(style.dash ?? []);
|
|
3510
3577
|
ctx.beginPath();
|
|
@@ -3547,10 +3614,18 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
|
|
|
3547
3614
|
endPoint = linePoints[linePoints.length - 1] ?? linePoints[0];
|
|
3548
3615
|
startAngle = Math.atan2(startSegment.y - linePoints[0].y, startSegment.x - linePoints[0].x) + Math.PI;
|
|
3549
3616
|
endAngle = Math.atan2(endPoint.y - endStart.y, endPoint.x - endStart.x);
|
|
3617
|
+
const stroke = resolveConnectionStroke(
|
|
3618
|
+
ctx,
|
|
3619
|
+
startPoint,
|
|
3620
|
+
endPoint,
|
|
3621
|
+
conn.fromColor,
|
|
3622
|
+
style.color,
|
|
3623
|
+
conn.toColor
|
|
3624
|
+
);
|
|
3550
3625
|
if (useElkRoute) {
|
|
3551
|
-
drawCubicInterpolatedPath(ctx, linePoints, style);
|
|
3626
|
+
drawCubicInterpolatedPath(ctx, linePoints, style, stroke);
|
|
3552
3627
|
} else {
|
|
3553
|
-
|
|
3628
|
+
drawOrthogonalPathWithStroke(ctx, startPoint, endPoint, style, stroke);
|
|
3554
3629
|
}
|
|
3555
3630
|
labelPoint = pointAlongPolyline(linePoints, labelT);
|
|
3556
3631
|
}
|
|
@@ -3855,6 +3930,9 @@ function measureTextBounds(ctx, options) {
|
|
|
3855
3930
|
function angleBetween(from, to) {
|
|
3856
3931
|
return Math.atan2(to.y - from.y, to.x - from.x);
|
|
3857
3932
|
}
|
|
3933
|
+
function degreesToRadians(angle) {
|
|
3934
|
+
return angle * Math.PI / 180;
|
|
3935
|
+
}
|
|
3858
3936
|
function pathBounds(operations) {
|
|
3859
3937
|
let minX = Number.POSITIVE_INFINITY;
|
|
3860
3938
|
let minY = Number.POSITIVE_INFINITY;
|
|
@@ -4092,6 +4170,34 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
4092
4170
|
});
|
|
4093
4171
|
break;
|
|
4094
4172
|
}
|
|
4173
|
+
case "arc": {
|
|
4174
|
+
const startAngle = degreesToRadians(command.startAngle);
|
|
4175
|
+
const endAngle = degreesToRadians(command.endAngle);
|
|
4176
|
+
withOpacity(ctx, command.opacity, () => {
|
|
4177
|
+
applyDrawShadow(ctx, command.shadow);
|
|
4178
|
+
ctx.beginPath();
|
|
4179
|
+
ctx.setLineDash(command.dash ?? []);
|
|
4180
|
+
ctx.lineWidth = command.width;
|
|
4181
|
+
ctx.strokeStyle = command.color;
|
|
4182
|
+
ctx.arc(command.center.x, command.center.y, command.radius, startAngle, endAngle);
|
|
4183
|
+
ctx.stroke();
|
|
4184
|
+
});
|
|
4185
|
+
rendered.push({
|
|
4186
|
+
id,
|
|
4187
|
+
kind: "draw",
|
|
4188
|
+
bounds: expandRect(
|
|
4189
|
+
{
|
|
4190
|
+
x: command.center.x - command.radius,
|
|
4191
|
+
y: command.center.y - command.radius,
|
|
4192
|
+
width: command.radius * 2,
|
|
4193
|
+
height: command.radius * 2
|
|
4194
|
+
},
|
|
4195
|
+
command.width / 2
|
|
4196
|
+
),
|
|
4197
|
+
foregroundColor: command.color
|
|
4198
|
+
});
|
|
4199
|
+
break;
|
|
4200
|
+
}
|
|
4095
4201
|
case "bezier": {
|
|
4096
4202
|
const points = command.points;
|
|
4097
4203
|
withOpacity(ctx, command.opacity, () => {
|
|
@@ -4248,6 +4354,84 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
4248
4354
|
});
|
|
4249
4355
|
break;
|
|
4250
4356
|
}
|
|
4357
|
+
case "text-row": {
|
|
4358
|
+
const segments = command.segments;
|
|
4359
|
+
if (segments.length === 0) break;
|
|
4360
|
+
const resolveSegment = (seg) => ({
|
|
4361
|
+
text: seg.text,
|
|
4362
|
+
fontSize: seg.fontSize ?? command.defaultFontSize,
|
|
4363
|
+
fontWeight: seg.fontWeight ?? command.defaultFontWeight,
|
|
4364
|
+
fontFamily: resolveDrawFont(theme, seg.fontFamily ?? command.defaultFontFamily),
|
|
4365
|
+
color: seg.color ?? command.defaultColor
|
|
4366
|
+
});
|
|
4367
|
+
const measured = [];
|
|
4368
|
+
let totalWidth = 0;
|
|
4369
|
+
let maxAscent = 0;
|
|
4370
|
+
let maxDescent = 0;
|
|
4371
|
+
for (const seg of segments) {
|
|
4372
|
+
const resolved = resolveSegment(seg);
|
|
4373
|
+
applyFont(ctx, {
|
|
4374
|
+
size: resolved.fontSize,
|
|
4375
|
+
weight: resolved.fontWeight,
|
|
4376
|
+
family: resolved.fontFamily
|
|
4377
|
+
});
|
|
4378
|
+
const metrics = ctx.measureText(resolved.text);
|
|
4379
|
+
const width = metrics.width;
|
|
4380
|
+
const ascent = metrics.actualBoundingBoxAscent || 0;
|
|
4381
|
+
const descent = metrics.actualBoundingBoxDescent || 0;
|
|
4382
|
+
totalWidth += width;
|
|
4383
|
+
maxAscent = Math.max(maxAscent, ascent);
|
|
4384
|
+
maxDescent = Math.max(maxDescent, descent);
|
|
4385
|
+
measured.push({ width, resolved });
|
|
4386
|
+
}
|
|
4387
|
+
let cursorX;
|
|
4388
|
+
if (command.align === "center") {
|
|
4389
|
+
cursorX = command.x - totalWidth / 2;
|
|
4390
|
+
} else if (command.align === "right") {
|
|
4391
|
+
cursorX = command.x - totalWidth;
|
|
4392
|
+
} else {
|
|
4393
|
+
cursorX = command.x;
|
|
4394
|
+
}
|
|
4395
|
+
const startX = cursorX;
|
|
4396
|
+
withOpacity(ctx, command.opacity, () => {
|
|
4397
|
+
ctx.textBaseline = command.baseline;
|
|
4398
|
+
for (const { width, resolved } of measured) {
|
|
4399
|
+
applyFont(ctx, {
|
|
4400
|
+
size: resolved.fontSize,
|
|
4401
|
+
weight: resolved.fontWeight,
|
|
4402
|
+
family: resolved.fontFamily
|
|
4403
|
+
});
|
|
4404
|
+
ctx.fillStyle = resolved.color;
|
|
4405
|
+
ctx.textAlign = "left";
|
|
4406
|
+
ctx.fillText(resolved.text, cursorX, command.y);
|
|
4407
|
+
cursorX += width;
|
|
4408
|
+
}
|
|
4409
|
+
});
|
|
4410
|
+
const height = Math.max(1, maxAscent + maxDescent);
|
|
4411
|
+
let topY;
|
|
4412
|
+
if (command.baseline === "top") {
|
|
4413
|
+
topY = command.y;
|
|
4414
|
+
} else if (command.baseline === "middle") {
|
|
4415
|
+
topY = command.y - height / 2;
|
|
4416
|
+
} else if (command.baseline === "bottom") {
|
|
4417
|
+
topY = command.y - height;
|
|
4418
|
+
} else {
|
|
4419
|
+
topY = command.y - maxAscent;
|
|
4420
|
+
}
|
|
4421
|
+
rendered.push({
|
|
4422
|
+
id,
|
|
4423
|
+
kind: "draw",
|
|
4424
|
+
bounds: {
|
|
4425
|
+
x: startX,
|
|
4426
|
+
y: topY,
|
|
4427
|
+
width: Math.max(1, totalWidth),
|
|
4428
|
+
height
|
|
4429
|
+
},
|
|
4430
|
+
foregroundColor: command.defaultColor,
|
|
4431
|
+
backgroundColor: theme.background
|
|
4432
|
+
});
|
|
4433
|
+
break;
|
|
4434
|
+
}
|
|
4251
4435
|
}
|
|
4252
4436
|
}
|
|
4253
4437
|
return rendered;
|
|
@@ -4657,6 +4841,18 @@ async function renderDesign(input, options = {}) {
|
|
|
4657
4841
|
const specHash = computeSpecHash(spec);
|
|
4658
4842
|
const generatorVersion = options.generatorVersion ?? DEFAULT_GENERATOR_VERSION;
|
|
4659
4843
|
const renderedAt = options.renderedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4844
|
+
const iteration = options.iteration;
|
|
4845
|
+
if (iteration) {
|
|
4846
|
+
if (!Number.isInteger(iteration.iteration) || iteration.iteration <= 0) {
|
|
4847
|
+
throw new Error("Iteration metadata requires iteration to be a positive integer.");
|
|
4848
|
+
}
|
|
4849
|
+
if (iteration.maxIterations != null && (!Number.isInteger(iteration.maxIterations) || iteration.maxIterations <= 0)) {
|
|
4850
|
+
throw new Error("Iteration metadata requires maxIterations to be a positive integer.");
|
|
4851
|
+
}
|
|
4852
|
+
if (iteration.maxIterations != null && iteration.maxIterations < iteration.iteration) {
|
|
4853
|
+
throw new Error("Iteration metadata requires maxIterations to be >= iteration.");
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4660
4856
|
const renderScale = resolveRenderScale(spec);
|
|
4661
4857
|
const canvas = createCanvas(spec.canvas.width * renderScale, spec.canvas.height * renderScale);
|
|
4662
4858
|
const ctx = canvas.getContext("2d");
|
|
@@ -4897,7 +5093,8 @@ async function renderDesign(input, options = {}) {
|
|
|
4897
5093
|
layout: {
|
|
4898
5094
|
safeFrame,
|
|
4899
5095
|
elements
|
|
4900
|
-
}
|
|
5096
|
+
},
|
|
5097
|
+
...iteration ? { iteration } : {}
|
|
4901
5098
|
};
|
|
4902
5099
|
return {
|
|
4903
5100
|
png: pngBuffer,
|
|
@@ -5204,6 +5401,12 @@ var renderOutputSchema = z3.object({
|
|
|
5204
5401
|
artifactHash: z3.string(),
|
|
5205
5402
|
specHash: z3.string(),
|
|
5206
5403
|
layoutMode: z3.string(),
|
|
5404
|
+
iteration: z3.object({
|
|
5405
|
+
current: z3.number().int().positive(),
|
|
5406
|
+
max: z3.number().int().positive(),
|
|
5407
|
+
isLast: z3.boolean(),
|
|
5408
|
+
notes: z3.string().optional()
|
|
5409
|
+
}).optional(),
|
|
5207
5410
|
qa: z3.object({
|
|
5208
5411
|
pass: z3.boolean(),
|
|
5209
5412
|
issueCount: z3.number(),
|
|
@@ -5290,8 +5493,30 @@ function readCodeRange(code, start, end) {
|
|
|
5290
5493
|
const lines = code.split(/\r?\n/u);
|
|
5291
5494
|
return lines.slice(start - 1, end).join("\n");
|
|
5292
5495
|
}
|
|
5496
|
+
function parseIterationMeta(options) {
|
|
5497
|
+
if (options.iteration == null) {
|
|
5498
|
+
if (options.maxIterations != null || options.iterationNotes || options.previousHash) {
|
|
5499
|
+
throw new Error(
|
|
5500
|
+
"--iteration is required when using --max-iterations, --iteration-notes, or --previous-hash."
|
|
5501
|
+
);
|
|
5502
|
+
}
|
|
5503
|
+
return void 0;
|
|
5504
|
+
}
|
|
5505
|
+
if (options.maxIterations != null && options.maxIterations < options.iteration) {
|
|
5506
|
+
throw new Error("--max-iterations must be greater than or equal to --iteration.");
|
|
5507
|
+
}
|
|
5508
|
+
return {
|
|
5509
|
+
iteration: options.iteration,
|
|
5510
|
+
...options.maxIterations != null ? { maxIterations: options.maxIterations } : {},
|
|
5511
|
+
...options.iterationNotes ? { notes: options.iterationNotes } : {},
|
|
5512
|
+
...options.previousHash ? { previousHash: options.previousHash } : {}
|
|
5513
|
+
};
|
|
5514
|
+
}
|
|
5293
5515
|
async function runRenderPipeline(spec, options) {
|
|
5294
|
-
const renderResult = await renderDesign(spec, {
|
|
5516
|
+
const renderResult = await renderDesign(spec, {
|
|
5517
|
+
generatorVersion: pkg.version,
|
|
5518
|
+
...options.iteration ? { iteration: options.iteration } : {}
|
|
5519
|
+
});
|
|
5295
5520
|
const written = await writeRenderArtifacts(renderResult, options.out);
|
|
5296
5521
|
const specPath = options.specOut ? resolve4(options.specOut) : specPathFor(written.metadataPath);
|
|
5297
5522
|
await mkdir2(dirname3(specPath), { recursive: true });
|
|
@@ -5308,6 +5533,14 @@ async function runRenderPipeline(spec, options) {
|
|
|
5308
5533
|
artifactHash: written.metadata.artifactHash,
|
|
5309
5534
|
specHash: written.metadata.specHash,
|
|
5310
5535
|
layoutMode: spec.layout.mode,
|
|
5536
|
+
...written.metadata.iteration ? {
|
|
5537
|
+
iteration: {
|
|
5538
|
+
current: written.metadata.iteration.iteration,
|
|
5539
|
+
max: written.metadata.iteration.maxIterations ?? written.metadata.iteration.iteration,
|
|
5540
|
+
isLast: (written.metadata.iteration.maxIterations ?? written.metadata.iteration.iteration) === written.metadata.iteration.iteration,
|
|
5541
|
+
...written.metadata.iteration.notes ? { notes: written.metadata.iteration.notes } : {}
|
|
5542
|
+
}
|
|
5543
|
+
} : {},
|
|
5311
5544
|
qa: {
|
|
5312
5545
|
pass: qa.pass,
|
|
5313
5546
|
issueCount: qa.issues.length,
|
|
@@ -5321,6 +5554,10 @@ cli.command("render", {
|
|
|
5321
5554
|
spec: z3.string().describe('Path to DesignSpec JSON file (or "-" to read JSON from stdin)'),
|
|
5322
5555
|
out: z3.string().describe("Output file path (.png) or output directory"),
|
|
5323
5556
|
specOut: z3.string().optional().describe("Optional explicit output path for normalized spec JSON"),
|
|
5557
|
+
iteration: z3.number().int().positive().optional().describe("Optional iteration number for iterative workflows (1-indexed)"),
|
|
5558
|
+
iterationNotes: z3.string().optional().describe("Optional notes for the current iteration metadata"),
|
|
5559
|
+
maxIterations: z3.number().int().positive().optional().describe("Optional maximum planned iteration count"),
|
|
5560
|
+
previousHash: z3.string().optional().describe("Optional artifact hash from the previous iteration"),
|
|
5324
5561
|
allowQaFail: z3.boolean().default(false).describe("Allow render success even if QA fails")
|
|
5325
5562
|
}),
|
|
5326
5563
|
output: renderOutputSchema,
|
|
@@ -5335,9 +5572,26 @@ cli.command("render", {
|
|
|
5335
5572
|
],
|
|
5336
5573
|
async run(c) {
|
|
5337
5574
|
const spec = parseDesignSpec(await readJson(c.options.spec));
|
|
5575
|
+
let iteration;
|
|
5576
|
+
try {
|
|
5577
|
+
iteration = parseIterationMeta({
|
|
5578
|
+
...c.options.iteration != null ? { iteration: c.options.iteration } : {},
|
|
5579
|
+
...c.options.maxIterations != null ? { maxIterations: c.options.maxIterations } : {},
|
|
5580
|
+
...c.options.iterationNotes ? { iterationNotes: c.options.iterationNotes } : {},
|
|
5581
|
+
...c.options.previousHash ? { previousHash: c.options.previousHash } : {}
|
|
5582
|
+
});
|
|
5583
|
+
} catch (error) {
|
|
5584
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5585
|
+
return c.error({
|
|
5586
|
+
code: "INVALID_ITERATION_OPTIONS",
|
|
5587
|
+
message,
|
|
5588
|
+
retryable: false
|
|
5589
|
+
});
|
|
5590
|
+
}
|
|
5338
5591
|
const runReport = await runRenderPipeline(spec, {
|
|
5339
5592
|
out: c.options.out,
|
|
5340
|
-
...c.options.specOut ? { specOut: c.options.specOut } : {}
|
|
5593
|
+
...c.options.specOut ? { specOut: c.options.specOut } : {},
|
|
5594
|
+
...iteration ? { iteration } : {}
|
|
5341
5595
|
});
|
|
5342
5596
|
if (!runReport.qa.pass && !c.options.allowQaFail) {
|
|
5343
5597
|
return c.error({
|
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
|
|
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';
|
|
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';
|
|
@@ -223,10 +223,10 @@ type ElkLayoutResult = LayoutResult & {
|
|
|
223
223
|
/**
|
|
224
224
|
* Render an array of freestyle draw commands onto a canvas context.
|
|
225
225
|
*
|
|
226
|
-
* Supports
|
|
227
|
-
* `path`, `badge`,
|
|
228
|
-
*
|
|
229
|
-
* downstream QA checks.
|
|
226
|
+
* Supports draw command types including `rect`, `circle`, `text`, `line`,
|
|
227
|
+
* `arc`, `bezier`, `path`, `badge`, `gradient-rect`, `grid`, and `text-row`.
|
|
228
|
+
* Each command is rendered in order and produces a corresponding
|
|
229
|
+
* {@link RenderedElement} with computed bounds for downstream QA checks.
|
|
230
230
|
*
|
|
231
231
|
* @param ctx - The `@napi-rs/canvas` 2D rendering context to draw into.
|
|
232
232
|
* @param commands - Array of {@link DrawCommand} objects from the design spec's
|