@spectratools/graphic-designer-cli 0.6.0 → 0.7.1
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 +242 -31
- package/dist/index.d.ts +15 -5
- package/dist/index.js +242 -31
- package/dist/qa.d.ts +1 -1
- package/dist/qa.js +48 -10
- package/dist/renderer.d.ts +1 -1
- package/dist/renderer.js +242 -31
- package/dist/{spec.schema-Dm_wOLTd.d.ts → spec.schema-BDvtn_mJ.d.ts} +1456 -21
- package/dist/spec.schema.d.ts +1 -1
- package/dist/spec.schema.js +48 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -752,9 +752,19 @@ var linearGradientSchema = z2.object({
|
|
|
752
752
|
}).strict();
|
|
753
753
|
var radialGradientSchema = z2.object({
|
|
754
754
|
type: z2.literal("radial"),
|
|
755
|
+
cx: z2.number().optional(),
|
|
756
|
+
cy: z2.number().optional(),
|
|
757
|
+
innerRadius: z2.number().min(0).optional(),
|
|
758
|
+
outerRadius: z2.number().min(0).optional(),
|
|
755
759
|
stops: z2.array(gradientStopSchema).min(2)
|
|
756
760
|
}).strict();
|
|
757
761
|
var gradientSchema = z2.discriminatedUnion("type", [linearGradientSchema, radialGradientSchema]);
|
|
762
|
+
var drawShadowSchema = z2.object({
|
|
763
|
+
color: colorHexSchema2.default("rgba(0,0,0,0.5)"),
|
|
764
|
+
blur: z2.number().min(0).max(64).default(10),
|
|
765
|
+
offsetX: z2.number().default(0),
|
|
766
|
+
offsetY: z2.number().default(4)
|
|
767
|
+
}).strict();
|
|
758
768
|
var drawFontFamilySchema = z2.enum(["heading", "body", "mono"]);
|
|
759
769
|
var drawRectSchema = z2.object({
|
|
760
770
|
type: z2.literal("rect"),
|
|
@@ -766,7 +776,8 @@ var drawRectSchema = z2.object({
|
|
|
766
776
|
stroke: colorHexSchema2.optional(),
|
|
767
777
|
strokeWidth: z2.number().min(0).max(32).default(0),
|
|
768
778
|
radius: z2.number().min(0).max(256).default(0),
|
|
769
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
779
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
780
|
+
shadow: drawShadowSchema.optional()
|
|
770
781
|
}).strict();
|
|
771
782
|
var drawCircleSchema = z2.object({
|
|
772
783
|
type: z2.literal("circle"),
|
|
@@ -776,7 +787,8 @@ var drawCircleSchema = z2.object({
|
|
|
776
787
|
fill: colorHexSchema2.optional(),
|
|
777
788
|
stroke: colorHexSchema2.optional(),
|
|
778
789
|
strokeWidth: z2.number().min(0).max(32).default(0),
|
|
779
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
790
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
791
|
+
shadow: drawShadowSchema.optional()
|
|
780
792
|
}).strict();
|
|
781
793
|
var drawTextSchema = z2.object({
|
|
782
794
|
type: z2.literal("text"),
|
|
@@ -791,7 +803,8 @@ var drawTextSchema = z2.object({
|
|
|
791
803
|
baseline: z2.enum(["top", "middle", "alphabetic", "bottom"]).default("alphabetic"),
|
|
792
804
|
letterSpacing: z2.number().min(-10).max(50).default(0),
|
|
793
805
|
maxWidth: z2.number().positive().optional(),
|
|
794
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
806
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
807
|
+
shadow: drawShadowSchema.optional()
|
|
795
808
|
}).strict();
|
|
796
809
|
var drawLineSchema = z2.object({
|
|
797
810
|
type: z2.literal("line"),
|
|
@@ -804,7 +817,8 @@ var drawLineSchema = z2.object({
|
|
|
804
817
|
dash: z2.array(z2.number()).max(6).optional(),
|
|
805
818
|
arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
|
|
806
819
|
arrowSize: z2.number().min(4).max(32).default(10),
|
|
807
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
820
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
821
|
+
shadow: drawShadowSchema.optional()
|
|
808
822
|
}).strict();
|
|
809
823
|
var drawPointSchema = z2.object({
|
|
810
824
|
x: z2.number(),
|
|
@@ -818,7 +832,8 @@ var drawBezierSchema = z2.object({
|
|
|
818
832
|
dash: z2.array(z2.number()).max(6).optional(),
|
|
819
833
|
arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
|
|
820
834
|
arrowSize: z2.number().min(4).max(32).default(10),
|
|
821
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
835
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
836
|
+
shadow: drawShadowSchema.optional()
|
|
822
837
|
}).strict();
|
|
823
838
|
var drawPathSchema = z2.object({
|
|
824
839
|
type: z2.literal("path"),
|
|
@@ -826,7 +841,8 @@ var drawPathSchema = z2.object({
|
|
|
826
841
|
fill: colorHexSchema2.optional(),
|
|
827
842
|
stroke: colorHexSchema2.optional(),
|
|
828
843
|
strokeWidth: z2.number().min(0).max(32).default(0),
|
|
829
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
844
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
845
|
+
shadow: drawShadowSchema.optional()
|
|
830
846
|
}).strict();
|
|
831
847
|
var drawBadgeSchema = z2.object({
|
|
832
848
|
type: z2.literal("badge"),
|
|
@@ -840,7 +856,8 @@ var drawBadgeSchema = z2.object({
|
|
|
840
856
|
paddingX: z2.number().min(0).max(64).default(10),
|
|
841
857
|
paddingY: z2.number().min(0).max(32).default(4),
|
|
842
858
|
borderRadius: z2.number().min(0).max(64).default(12),
|
|
843
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
859
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
860
|
+
shadow: drawShadowSchema.optional()
|
|
844
861
|
}).strict();
|
|
845
862
|
var drawGradientRectSchema = z2.object({
|
|
846
863
|
type: z2.literal("gradient-rect"),
|
|
@@ -850,7 +867,17 @@ var drawGradientRectSchema = z2.object({
|
|
|
850
867
|
height: z2.number().positive(),
|
|
851
868
|
gradient: gradientSchema,
|
|
852
869
|
radius: z2.number().min(0).max(256).default(0),
|
|
853
|
-
opacity: z2.number().min(0).max(1).default(1)
|
|
870
|
+
opacity: z2.number().min(0).max(1).default(1),
|
|
871
|
+
shadow: drawShadowSchema.optional()
|
|
872
|
+
}).strict();
|
|
873
|
+
var drawGridSchema = z2.object({
|
|
874
|
+
type: z2.literal("grid"),
|
|
875
|
+
spacing: z2.number().min(5).max(200).default(40),
|
|
876
|
+
color: colorHexSchema2.default("#1E2D4A"),
|
|
877
|
+
width: z2.number().min(0.1).max(4).default(0.5),
|
|
878
|
+
opacity: z2.number().min(0).max(1).default(0.2),
|
|
879
|
+
offsetX: z2.number().default(0),
|
|
880
|
+
offsetY: z2.number().default(0)
|
|
854
881
|
}).strict();
|
|
855
882
|
var drawCommandSchema = z2.discriminatedUnion("type", [
|
|
856
883
|
drawRectSchema,
|
|
@@ -860,7 +887,8 @@ var drawCommandSchema = z2.discriminatedUnion("type", [
|
|
|
860
887
|
drawBezierSchema,
|
|
861
888
|
drawPathSchema,
|
|
862
889
|
drawBadgeSchema,
|
|
863
|
-
drawGradientRectSchema
|
|
890
|
+
drawGradientRectSchema,
|
|
891
|
+
drawGridSchema
|
|
864
892
|
]);
|
|
865
893
|
var defaultCanvas = {
|
|
866
894
|
width: 1200,
|
|
@@ -966,6 +994,13 @@ var flowNodeElementSchema = z2.object({
|
|
|
966
994
|
badgePosition: z2.enum(["top", "inside-top"]).default("inside-top"),
|
|
967
995
|
shadow: flowNodeShadowSchema.optional()
|
|
968
996
|
}).strict();
|
|
997
|
+
var anchorHintSchema = z2.union([
|
|
998
|
+
z2.enum(["top", "bottom", "left", "right", "center"]),
|
|
999
|
+
z2.object({
|
|
1000
|
+
x: z2.number().min(-1).max(1),
|
|
1001
|
+
y: z2.number().min(-1).max(1)
|
|
1002
|
+
}).strict()
|
|
1003
|
+
]);
|
|
969
1004
|
var connectionElementSchema = z2.object({
|
|
970
1005
|
type: z2.literal("connection"),
|
|
971
1006
|
from: z2.string().min(1).max(120),
|
|
@@ -979,9 +1014,12 @@ var connectionElementSchema = z2.object({
|
|
|
979
1014
|
width: z2.number().min(0.5).max(10).optional(),
|
|
980
1015
|
strokeWidth: z2.number().min(0.5).max(10).default(2),
|
|
981
1016
|
arrowSize: z2.number().min(4).max(32).optional(),
|
|
1017
|
+
arrowPlacement: z2.enum(["endpoint", "boundary"]).default("endpoint"),
|
|
982
1018
|
opacity: z2.number().min(0).max(1).default(1),
|
|
983
1019
|
routing: z2.enum(["auto", "orthogonal", "curve", "arc"]).default("auto"),
|
|
984
|
-
tension: z2.number().min(0.1).max(0.8).default(0.35)
|
|
1020
|
+
tension: z2.number().min(0.1).max(0.8).default(0.35),
|
|
1021
|
+
fromAnchor: anchorHintSchema.optional(),
|
|
1022
|
+
toAnchor: anchorHintSchema.optional()
|
|
985
1023
|
}).strict();
|
|
986
1024
|
var codeBlockStyleSchema = z2.object({
|
|
987
1025
|
paddingVertical: z2.number().min(0).max(128).default(56),
|
|
@@ -2479,12 +2517,12 @@ function withAlpha2(color, alpha) {
|
|
|
2479
2517
|
}
|
|
2480
2518
|
function drawGradientRect(ctx, rect, gradient, borderRadius = 0) {
|
|
2481
2519
|
const fill = gradient.type === "linear" ? createLinearRectGradient(ctx, rect, gradient.angle ?? 180) : ctx.createRadialGradient(
|
|
2482
|
-
rect.x + rect.width / 2,
|
|
2483
|
-
rect.y + rect.height / 2,
|
|
2484
|
-
0,
|
|
2485
|
-
rect.x + rect.width / 2,
|
|
2486
|
-
rect.y + rect.height / 2,
|
|
2487
|
-
Math.max(rect.width, rect.height) / 2
|
|
2520
|
+
gradient.cx ?? rect.x + rect.width / 2,
|
|
2521
|
+
gradient.cy ?? rect.y + rect.height / 2,
|
|
2522
|
+
gradient.innerRadius ?? 0,
|
|
2523
|
+
gradient.cx ?? rect.x + rect.width / 2,
|
|
2524
|
+
gradient.cy ?? rect.y + rect.height / 2,
|
|
2525
|
+
gradient.outerRadius ?? Math.max(rect.width, rect.height) / 2
|
|
2488
2526
|
);
|
|
2489
2527
|
addGradientStops(fill, gradient.stops);
|
|
2490
2528
|
ctx.save();
|
|
@@ -3105,21 +3143,61 @@ function edgeAnchor(bounds, target) {
|
|
|
3105
3143
|
const t = absDx * hh > absDy * hw ? hw / absDx : hh / absDy;
|
|
3106
3144
|
return { x: c.x + dx * t, y: c.y + dy * t };
|
|
3107
3145
|
}
|
|
3146
|
+
function resolveAnchor(bounds, anchor, fallbackTarget) {
|
|
3147
|
+
if (!anchor) return edgeAnchor(bounds, fallbackTarget);
|
|
3148
|
+
if (typeof anchor === "string") {
|
|
3149
|
+
const c2 = rectCenter(bounds);
|
|
3150
|
+
switch (anchor) {
|
|
3151
|
+
case "top":
|
|
3152
|
+
return { x: c2.x, y: bounds.y };
|
|
3153
|
+
case "bottom":
|
|
3154
|
+
return { x: c2.x, y: bounds.y + bounds.height };
|
|
3155
|
+
case "left":
|
|
3156
|
+
return { x: bounds.x, y: c2.y };
|
|
3157
|
+
case "right":
|
|
3158
|
+
return { x: bounds.x + bounds.width, y: c2.y };
|
|
3159
|
+
case "center":
|
|
3160
|
+
return c2;
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
const c = rectCenter(bounds);
|
|
3164
|
+
return {
|
|
3165
|
+
x: c.x + anchor.x * (bounds.width / 2),
|
|
3166
|
+
y: c.y + anchor.y * (bounds.height / 2)
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
function anchorNormal(anchor, point, diagramCenter) {
|
|
3170
|
+
if (typeof anchor === "string") {
|
|
3171
|
+
switch (anchor) {
|
|
3172
|
+
case "top":
|
|
3173
|
+
return { x: 0, y: -1 };
|
|
3174
|
+
case "bottom":
|
|
3175
|
+
return { x: 0, y: 1 };
|
|
3176
|
+
case "left":
|
|
3177
|
+
return { x: -1, y: 0 };
|
|
3178
|
+
case "right":
|
|
3179
|
+
return { x: 1, y: 0 };
|
|
3180
|
+
case "center":
|
|
3181
|
+
return outwardNormal(point, diagramCenter);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
return outwardNormal(point, diagramCenter);
|
|
3185
|
+
}
|
|
3108
3186
|
function outwardNormal(point, diagramCenter) {
|
|
3109
3187
|
const dx = point.x - diagramCenter.x;
|
|
3110
3188
|
const dy = point.y - diagramCenter.y;
|
|
3111
3189
|
const len = Math.hypot(dx, dy) || 1;
|
|
3112
3190
|
return { x: dx / len, y: dy / len };
|
|
3113
3191
|
}
|
|
3114
|
-
function curveRoute(fromBounds, toBounds, diagramCenter, tension) {
|
|
3192
|
+
function curveRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
|
|
3115
3193
|
const fromCenter = rectCenter(fromBounds);
|
|
3116
3194
|
const toCenter = rectCenter(toBounds);
|
|
3117
|
-
const p0 =
|
|
3118
|
-
const p3 =
|
|
3195
|
+
const p0 = resolveAnchor(fromBounds, fromAnchor, toCenter);
|
|
3196
|
+
const p3 = resolveAnchor(toBounds, toAnchor, fromCenter);
|
|
3119
3197
|
const dist = Math.hypot(p3.x - p0.x, p3.y - p0.y);
|
|
3120
3198
|
const offset = dist * tension;
|
|
3121
|
-
const n0 =
|
|
3122
|
-
const n3 =
|
|
3199
|
+
const n0 = anchorNormal(fromAnchor, p0, diagramCenter);
|
|
3200
|
+
const n3 = anchorNormal(toAnchor, p3, diagramCenter);
|
|
3123
3201
|
const cp1 = { x: p0.x + n0.x * offset, y: p0.y + n0.y * offset };
|
|
3124
3202
|
const cp2 = { x: p3.x + n3.x * offset, y: p3.y + n3.y * offset };
|
|
3125
3203
|
return [p0, cp1, cp2, p3];
|
|
@@ -3133,11 +3211,11 @@ function localToWorld(origin, axisX, axisY, local) {
|
|
|
3133
3211
|
y: origin.y + axisX.y * local.x + axisY.y * local.y
|
|
3134
3212
|
};
|
|
3135
3213
|
}
|
|
3136
|
-
function arcRoute(fromBounds, toBounds, diagramCenter, tension) {
|
|
3214
|
+
function arcRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
|
|
3137
3215
|
const fromCenter = rectCenter(fromBounds);
|
|
3138
3216
|
const toCenter = rectCenter(toBounds);
|
|
3139
|
-
const start =
|
|
3140
|
-
const end =
|
|
3217
|
+
const start = resolveAnchor(fromBounds, fromAnchor, toCenter);
|
|
3218
|
+
const end = resolveAnchor(toBounds, toAnchor, fromCenter);
|
|
3141
3219
|
const chord = { x: end.x - start.x, y: end.y - start.y };
|
|
3142
3220
|
const chordLength = Math.hypot(chord.x, chord.y);
|
|
3143
3221
|
if (chordLength < 1e-6) {
|
|
@@ -3175,11 +3253,11 @@ function arcRoute(fromBounds, toBounds, diagramCenter, tension) {
|
|
|
3175
3253
|
[pMid, cp3, cp4, p3]
|
|
3176
3254
|
];
|
|
3177
3255
|
}
|
|
3178
|
-
function orthogonalRoute(fromBounds, toBounds) {
|
|
3256
|
+
function orthogonalRoute(fromBounds, toBounds, fromAnchor, toAnchor) {
|
|
3179
3257
|
const fromC = rectCenter(fromBounds);
|
|
3180
3258
|
const toC = rectCenter(toBounds);
|
|
3181
|
-
const p0 =
|
|
3182
|
-
const p3 =
|
|
3259
|
+
const p0 = resolveAnchor(fromBounds, fromAnchor, toC);
|
|
3260
|
+
const p3 = resolveAnchor(toBounds, toAnchor, fromC);
|
|
3183
3261
|
const midX = (p0.x + p3.x) / 2;
|
|
3184
3262
|
return [p0, { x: midX, y: p0.y }, { x: midX, y: p3.y }, p3];
|
|
3185
3263
|
}
|
|
@@ -3190,6 +3268,35 @@ function bezierPointAt(p0, cp1, cp2, p3, t) {
|
|
|
3190
3268
|
y: mt * mt * mt * p0.y + 3 * mt * mt * t * cp1.y + 3 * mt * t * t * cp2.y + t * t * t * p3.y
|
|
3191
3269
|
};
|
|
3192
3270
|
}
|
|
3271
|
+
function bezierTangentAt(p0, cp1, cp2, p3, t) {
|
|
3272
|
+
const mt = 1 - t;
|
|
3273
|
+
return {
|
|
3274
|
+
x: 3 * mt * mt * (cp1.x - p0.x) + 6 * mt * t * (cp2.x - cp1.x) + 3 * t * t * (p3.x - cp2.x),
|
|
3275
|
+
y: 3 * mt * mt * (cp1.y - p0.y) + 6 * mt * t * (cp2.y - cp1.y) + 3 * t * t * (p3.y - cp2.y)
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
function isInsideRect(point, rect) {
|
|
3279
|
+
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
3280
|
+
}
|
|
3281
|
+
function findBoundaryIntersection(p0, cp1, cp2, p3, targetRect, searchFromEnd) {
|
|
3282
|
+
const step = 5e-3;
|
|
3283
|
+
if (searchFromEnd) {
|
|
3284
|
+
for (let t = 0.95; t >= 0.5; t -= step) {
|
|
3285
|
+
const pt = bezierPointAt(p0, cp1, cp2, p3, t);
|
|
3286
|
+
if (!isInsideRect(pt, targetRect)) {
|
|
3287
|
+
return t;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
} else {
|
|
3291
|
+
for (let t = 0.05; t <= 0.5; t += step) {
|
|
3292
|
+
const pt = bezierPointAt(p0, cp1, cp2, p3, t);
|
|
3293
|
+
if (!isInsideRect(pt, targetRect)) {
|
|
3294
|
+
return t;
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
return void 0;
|
|
3299
|
+
}
|
|
3193
3300
|
function pointAlongArc(route, t) {
|
|
3194
3301
|
const [first, second] = route;
|
|
3195
3302
|
if (t <= 0.5) {
|
|
@@ -3317,8 +3424,16 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
|
|
|
3317
3424
|
let labelPoint;
|
|
3318
3425
|
ctx.save();
|
|
3319
3426
|
ctx.globalAlpha = conn.opacity;
|
|
3427
|
+
const arrowPlacement = conn.arrowPlacement ?? "endpoint";
|
|
3320
3428
|
if (routing === "curve") {
|
|
3321
|
-
const [p0, cp1, cp2, p3] = curveRoute(
|
|
3429
|
+
const [p0, cp1, cp2, p3] = curveRoute(
|
|
3430
|
+
fromBounds,
|
|
3431
|
+
toBounds,
|
|
3432
|
+
diagramCenter,
|
|
3433
|
+
tension,
|
|
3434
|
+
conn.fromAnchor,
|
|
3435
|
+
conn.toAnchor
|
|
3436
|
+
);
|
|
3322
3437
|
ctx.strokeStyle = style.color;
|
|
3323
3438
|
ctx.lineWidth = style.width;
|
|
3324
3439
|
ctx.setLineDash(style.dash ?? []);
|
|
@@ -3332,8 +3447,33 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
|
|
|
3332
3447
|
startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
|
|
3333
3448
|
endAngle = Math.atan2(p3.y - cp2.y, p3.x - cp2.x);
|
|
3334
3449
|
labelPoint = bezierPointAt(p0, cp1, cp2, p3, labelT);
|
|
3450
|
+
if (arrowPlacement === "boundary") {
|
|
3451
|
+
if (conn.arrow === "end" || conn.arrow === "both") {
|
|
3452
|
+
const tEnd = findBoundaryIntersection(p0, cp1, cp2, p3, toBounds, true);
|
|
3453
|
+
if (tEnd !== void 0) {
|
|
3454
|
+
endPoint = bezierPointAt(p0, cp1, cp2, p3, tEnd);
|
|
3455
|
+
const tangent = bezierTangentAt(p0, cp1, cp2, p3, tEnd);
|
|
3456
|
+
endAngle = Math.atan2(tangent.y, tangent.x);
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
if (conn.arrow === "start" || conn.arrow === "both") {
|
|
3460
|
+
const tStart = findBoundaryIntersection(p0, cp1, cp2, p3, fromBounds, false);
|
|
3461
|
+
if (tStart !== void 0) {
|
|
3462
|
+
startPoint = bezierPointAt(p0, cp1, cp2, p3, tStart);
|
|
3463
|
+
const tangent = bezierTangentAt(p0, cp1, cp2, p3, tStart);
|
|
3464
|
+
startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3335
3468
|
} else if (routing === "arc") {
|
|
3336
|
-
const [first, second] = arcRoute(
|
|
3469
|
+
const [first, second] = arcRoute(
|
|
3470
|
+
fromBounds,
|
|
3471
|
+
toBounds,
|
|
3472
|
+
diagramCenter,
|
|
3473
|
+
tension,
|
|
3474
|
+
conn.fromAnchor,
|
|
3475
|
+
conn.toAnchor
|
|
3476
|
+
);
|
|
3337
3477
|
const [p0, cp1, cp2, pMid] = first;
|
|
3338
3478
|
const [, cp3, cp4, p3] = second;
|
|
3339
3479
|
ctx.strokeStyle = style.color;
|
|
@@ -3350,9 +3490,29 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
|
|
|
3350
3490
|
startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
|
|
3351
3491
|
endAngle = Math.atan2(p3.y - cp4.y, p3.x - cp4.x);
|
|
3352
3492
|
labelPoint = pointAlongArc([first, second], labelT);
|
|
3493
|
+
if (arrowPlacement === "boundary") {
|
|
3494
|
+
if (conn.arrow === "end" || conn.arrow === "both") {
|
|
3495
|
+
const [, s_cp3, s_cp4, s_p3] = second;
|
|
3496
|
+
const tEnd = findBoundaryIntersection(pMid, s_cp3, s_cp4, s_p3, toBounds, true);
|
|
3497
|
+
if (tEnd !== void 0) {
|
|
3498
|
+
endPoint = bezierPointAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
|
|
3499
|
+
const tangent = bezierTangentAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
|
|
3500
|
+
endAngle = Math.atan2(tangent.y, tangent.x);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
if (conn.arrow === "start" || conn.arrow === "both") {
|
|
3504
|
+
const tStart = findBoundaryIntersection(p0, cp1, cp2, pMid, fromBounds, false);
|
|
3505
|
+
if (tStart !== void 0) {
|
|
3506
|
+
startPoint = bezierPointAt(p0, cp1, cp2, pMid, tStart);
|
|
3507
|
+
const tangent = bezierTangentAt(p0, cp1, cp2, pMid, tStart);
|
|
3508
|
+
startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3353
3512
|
} else {
|
|
3354
|
-
const
|
|
3355
|
-
|
|
3513
|
+
const hasAnchorHints = conn.fromAnchor !== void 0 || conn.toAnchor !== void 0;
|
|
3514
|
+
const useElkRoute = routing === "auto" && !hasAnchorHints && (edgeRoute?.points.length ?? 0) >= 2;
|
|
3515
|
+
linePoints = useElkRoute ? edgeRoute?.points ?? orthogonalRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor) : orthogonalRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor);
|
|
3356
3516
|
startPoint = linePoints[0];
|
|
3357
3517
|
const startSegment = linePoints[1] ?? linePoints[0];
|
|
3358
3518
|
const endStart = linePoints[linePoints.length - 2] ?? linePoints[0];
|
|
@@ -3584,6 +3744,13 @@ function expandRect(rect, amount) {
|
|
|
3584
3744
|
height: rect.height + amount * 2
|
|
3585
3745
|
};
|
|
3586
3746
|
}
|
|
3747
|
+
function applyDrawShadow(ctx, shadow) {
|
|
3748
|
+
if (!shadow) return;
|
|
3749
|
+
ctx.shadowColor = shadow.color;
|
|
3750
|
+
ctx.shadowBlur = shadow.blur;
|
|
3751
|
+
ctx.shadowOffsetX = shadow.offsetX;
|
|
3752
|
+
ctx.shadowOffsetY = shadow.offsetY;
|
|
3753
|
+
}
|
|
3587
3754
|
function fromPoints(points) {
|
|
3588
3755
|
const minX = Math.min(...points.map((point) => point.x));
|
|
3589
3756
|
const minY = Math.min(...points.map((point) => point.y));
|
|
@@ -3765,6 +3932,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3765
3932
|
height: command.height
|
|
3766
3933
|
};
|
|
3767
3934
|
withOpacity(ctx, command.opacity, () => {
|
|
3935
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3768
3936
|
roundRectPath(ctx, rect, command.radius);
|
|
3769
3937
|
if (command.fill) {
|
|
3770
3938
|
ctx.fillStyle = command.fill;
|
|
@@ -3788,6 +3956,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3788
3956
|
}
|
|
3789
3957
|
case "circle": {
|
|
3790
3958
|
withOpacity(ctx, command.opacity, () => {
|
|
3959
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3791
3960
|
ctx.beginPath();
|
|
3792
3961
|
ctx.arc(command.cx, command.cy, command.radius, 0, Math.PI * 2);
|
|
3793
3962
|
ctx.closePath();
|
|
@@ -3822,6 +3991,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3822
3991
|
case "text": {
|
|
3823
3992
|
const fontFamily = resolveDrawFont(theme, command.fontFamily);
|
|
3824
3993
|
withOpacity(ctx, command.opacity, () => {
|
|
3994
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3825
3995
|
applyFont(ctx, {
|
|
3826
3996
|
size: command.fontSize,
|
|
3827
3997
|
weight: command.fontWeight,
|
|
@@ -3872,6 +4042,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3872
4042
|
const to = { x: command.x2, y: command.y2 };
|
|
3873
4043
|
const lineAngle = angleBetween(from, to);
|
|
3874
4044
|
withOpacity(ctx, command.opacity, () => {
|
|
4045
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3875
4046
|
drawLine(ctx, from, to, {
|
|
3876
4047
|
color: command.color,
|
|
3877
4048
|
width: command.width,
|
|
@@ -3896,6 +4067,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3896
4067
|
case "bezier": {
|
|
3897
4068
|
const points = command.points;
|
|
3898
4069
|
withOpacity(ctx, command.opacity, () => {
|
|
4070
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3899
4071
|
drawBezier(ctx, points, {
|
|
3900
4072
|
color: command.color,
|
|
3901
4073
|
width: command.width,
|
|
@@ -3929,6 +4101,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3929
4101
|
const operations = parseSvgPath(command.d);
|
|
3930
4102
|
const baseBounds = pathBounds(operations);
|
|
3931
4103
|
withOpacity(ctx, command.opacity, () => {
|
|
4104
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3932
4105
|
applySvgOperations(ctx, operations);
|
|
3933
4106
|
if (command.fill) {
|
|
3934
4107
|
ctx.fillStyle = command.fill;
|
|
@@ -3969,9 +4142,16 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3969
4142
|
height: textHeight + command.paddingY * 2
|
|
3970
4143
|
};
|
|
3971
4144
|
withOpacity(ctx, command.opacity, () => {
|
|
4145
|
+
applyDrawShadow(ctx, command.shadow);
|
|
3972
4146
|
roundRectPath(ctx, rect, command.borderRadius);
|
|
3973
4147
|
ctx.fillStyle = command.background;
|
|
3974
4148
|
ctx.fill();
|
|
4149
|
+
if (command.shadow) {
|
|
4150
|
+
ctx.shadowColor = "transparent";
|
|
4151
|
+
ctx.shadowBlur = 0;
|
|
4152
|
+
ctx.shadowOffsetX = 0;
|
|
4153
|
+
ctx.shadowOffsetY = 0;
|
|
4154
|
+
}
|
|
3975
4155
|
applyFont(ctx, {
|
|
3976
4156
|
size: command.fontSize,
|
|
3977
4157
|
weight: 600,
|
|
@@ -3999,6 +4179,7 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
3999
4179
|
height: command.height
|
|
4000
4180
|
};
|
|
4001
4181
|
withOpacity(ctx, command.opacity, () => {
|
|
4182
|
+
applyDrawShadow(ctx, command.shadow);
|
|
4002
4183
|
drawGradientRect(ctx, rect, command.gradient, command.radius);
|
|
4003
4184
|
});
|
|
4004
4185
|
rendered.push({
|
|
@@ -4009,6 +4190,36 @@ function renderDrawCommands(ctx, commands, theme) {
|
|
|
4009
4190
|
});
|
|
4010
4191
|
break;
|
|
4011
4192
|
}
|
|
4193
|
+
case "grid": {
|
|
4194
|
+
const canvasWidth = ctx.canvas.width;
|
|
4195
|
+
const canvasHeight = ctx.canvas.height;
|
|
4196
|
+
withOpacity(ctx, command.opacity, () => {
|
|
4197
|
+
ctx.strokeStyle = command.color;
|
|
4198
|
+
ctx.lineWidth = command.width;
|
|
4199
|
+
const startX = command.offsetX % command.spacing;
|
|
4200
|
+
for (let x = startX; x <= canvasWidth; x += command.spacing) {
|
|
4201
|
+
ctx.beginPath();
|
|
4202
|
+
ctx.moveTo(x, 0);
|
|
4203
|
+
ctx.lineTo(x, canvasHeight);
|
|
4204
|
+
ctx.stroke();
|
|
4205
|
+
}
|
|
4206
|
+
const startY = command.offsetY % command.spacing;
|
|
4207
|
+
for (let y = startY; y <= canvasHeight; y += command.spacing) {
|
|
4208
|
+
ctx.beginPath();
|
|
4209
|
+
ctx.moveTo(0, y);
|
|
4210
|
+
ctx.lineTo(canvasWidth, y);
|
|
4211
|
+
ctx.stroke();
|
|
4212
|
+
}
|
|
4213
|
+
});
|
|
4214
|
+
rendered.push({
|
|
4215
|
+
id,
|
|
4216
|
+
kind: "draw",
|
|
4217
|
+
bounds: { x: 0, y: 0, width: canvasWidth, height: canvasHeight },
|
|
4218
|
+
foregroundColor: command.color,
|
|
4219
|
+
allowOverlap: true
|
|
4220
|
+
});
|
|
4221
|
+
break;
|
|
4222
|
+
}
|
|
4012
4223
|
}
|
|
4013
4224
|
}
|
|
4014
4225
|
return rendered;
|
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, C as ConnectionElement } from './spec.schema-
|
|
3
|
-
export {
|
|
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-BDvtn_mJ.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 DrawBadge, s as DrawBezier, t as DrawCircle, u as DrawFontFamily, v as DrawGradientRect, w as DrawLine, x as DrawPath, y as DrawPoint, z as DrawRect, E as DrawShadow, F as DrawText, G as Element, H as FlowNodeElement, I as Gradient, J as GradientOverlayDecorator, K as GradientSpec, L as GradientStop, M as GridLayoutConfig, N as ImageElement, O as LayoutConfig, P as LayoutSnapshot, Q as ManualLayoutConfig, S as RainbowRuleDecorator, R as RenderMetadata, U as RenderResult, V as ShapeElement, W as StackLayoutConfig, X as TerminalElement, Y as TextElement, Z as ThemeInput, _ as VignetteDecorator, $ as WrittenArtifacts, a0 as builtInThemeBackgrounds, a1 as builtInThemes, a2 as computeSpecHash, a3 as connectionElementSchema, a4 as defaultAutoLayout, a5 as defaultCanvas, a6 as defaultConstraints, a7 as defaultGridLayout, a8 as defaultLayout, a9 as defaultStackLayout, aa as defaultTheme, ab as deriveSafeFrame, ac as designSpecSchema, ad as diagramElementSchema, ae as diagramLayoutSchema, af as diagramSpecSchema, ag as drawGradientRect, ah as drawRainbowRule, ai as drawVignette, aj as flowNodeElementSchema, ak as inferLayout, al as inferSidecarPath, am as parseDesignSpec, an as parseDiagramSpec, ao as renderDesign, ap as resolveTheme, aq as writeRenderArtifacts } from './spec.schema-BDvtn_mJ.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';
|
|
@@ -275,21 +275,31 @@ declare function outwardNormal(point: Point, diagramCenter: Point): Point;
|
|
|
275
275
|
/**
|
|
276
276
|
* Compute a cubic bezier curve that bows outward from the diagram center.
|
|
277
277
|
* Returns `[startPoint, controlPoint1, controlPoint2, endPoint]`.
|
|
278
|
+
*
|
|
279
|
+
* When `fromAnchor` or `toAnchor` hints are provided, they override the
|
|
280
|
+
* automatic edge anchor calculation and the outward normal is derived from
|
|
281
|
+
* the hint direction instead of from the diagram center.
|
|
278
282
|
*/
|
|
279
|
-
declare function curveRoute(fromBounds: Rect, toBounds: Rect, diagramCenter: Point, tension: number): [Point, Point, Point, Point];
|
|
283
|
+
declare function curveRoute(fromBounds: Rect, toBounds: Rect, diagramCenter: Point, tension: number, fromAnchor?: AnchorHint, toAnchor?: AnchorHint): [Point, Point, Point, Point];
|
|
280
284
|
/**
|
|
281
285
|
* Approximate an outward-bowing half-ellipse with two cubic bezier segments.
|
|
282
286
|
*
|
|
283
287
|
* Uses the classic kappa constant (`4 * (sqrt(2) - 1) / 3`) for quarter-ellipse
|
|
284
288
|
* control points, producing a stable arc from source edge anchor to target edge
|
|
285
289
|
* anchor.
|
|
290
|
+
*
|
|
291
|
+
* When `fromAnchor` or `toAnchor` hints are provided, they override the
|
|
292
|
+
* automatic edge anchor calculation.
|
|
286
293
|
*/
|
|
287
|
-
declare function arcRoute(fromBounds: Rect, toBounds: Rect, diagramCenter: Point, tension: number): [CubicBezierSegment, CubicBezierSegment];
|
|
294
|
+
declare function arcRoute(fromBounds: Rect, toBounds: Rect, diagramCenter: Point, tension: number, fromAnchor?: AnchorHint, toAnchor?: AnchorHint): [CubicBezierSegment, CubicBezierSegment];
|
|
288
295
|
/**
|
|
289
296
|
* Compute an orthogonal (right-angle) path between two rectangles.
|
|
290
297
|
* Returns an array of waypoints forming a 3-segment path.
|
|
298
|
+
*
|
|
299
|
+
* When `fromAnchor` or `toAnchor` hints are provided, they override the
|
|
300
|
+
* automatic edge anchor calculation.
|
|
291
301
|
*/
|
|
292
|
-
declare function orthogonalRoute(fromBounds: Rect, toBounds: Rect): Point[];
|
|
302
|
+
declare function orthogonalRoute(fromBounds: Rect, toBounds: Rect, fromAnchor?: AnchorHint, toAnchor?: AnchorHint): Point[];
|
|
293
303
|
/** Evaluate cubic bezier at parameter `t`. */
|
|
294
304
|
declare function bezierPointAt(p0: Point, cp1: Point, cp2: Point, p3: Point, t: number): Point;
|
|
295
305
|
/**
|