@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/index.js CHANGED
@@ -761,9 +761,19 @@ var linearGradientSchema = z2.object({
761
761
  }).strict();
762
762
  var radialGradientSchema = z2.object({
763
763
  type: z2.literal("radial"),
764
+ cx: z2.number().optional(),
765
+ cy: z2.number().optional(),
766
+ innerRadius: z2.number().min(0).optional(),
767
+ outerRadius: z2.number().min(0).optional(),
764
768
  stops: z2.array(gradientStopSchema).min(2)
765
769
  }).strict();
766
770
  var gradientSchema = z2.discriminatedUnion("type", [linearGradientSchema, radialGradientSchema]);
771
+ var drawShadowSchema = z2.object({
772
+ color: colorHexSchema2.default("rgba(0,0,0,0.5)"),
773
+ blur: z2.number().min(0).max(64).default(10),
774
+ offsetX: z2.number().default(0),
775
+ offsetY: z2.number().default(4)
776
+ }).strict();
767
777
  var drawFontFamilySchema = z2.enum(["heading", "body", "mono"]);
768
778
  var drawRectSchema = z2.object({
769
779
  type: z2.literal("rect"),
@@ -775,7 +785,8 @@ var drawRectSchema = z2.object({
775
785
  stroke: colorHexSchema2.optional(),
776
786
  strokeWidth: z2.number().min(0).max(32).default(0),
777
787
  radius: z2.number().min(0).max(256).default(0),
778
- opacity: z2.number().min(0).max(1).default(1)
788
+ opacity: z2.number().min(0).max(1).default(1),
789
+ shadow: drawShadowSchema.optional()
779
790
  }).strict();
780
791
  var drawCircleSchema = z2.object({
781
792
  type: z2.literal("circle"),
@@ -785,7 +796,8 @@ var drawCircleSchema = z2.object({
785
796
  fill: colorHexSchema2.optional(),
786
797
  stroke: colorHexSchema2.optional(),
787
798
  strokeWidth: z2.number().min(0).max(32).default(0),
788
- opacity: z2.number().min(0).max(1).default(1)
799
+ opacity: z2.number().min(0).max(1).default(1),
800
+ shadow: drawShadowSchema.optional()
789
801
  }).strict();
790
802
  var drawTextSchema = z2.object({
791
803
  type: z2.literal("text"),
@@ -800,7 +812,8 @@ var drawTextSchema = z2.object({
800
812
  baseline: z2.enum(["top", "middle", "alphabetic", "bottom"]).default("alphabetic"),
801
813
  letterSpacing: z2.number().min(-10).max(50).default(0),
802
814
  maxWidth: z2.number().positive().optional(),
803
- opacity: z2.number().min(0).max(1).default(1)
815
+ opacity: z2.number().min(0).max(1).default(1),
816
+ shadow: drawShadowSchema.optional()
804
817
  }).strict();
805
818
  var drawLineSchema = z2.object({
806
819
  type: z2.literal("line"),
@@ -813,7 +826,8 @@ var drawLineSchema = z2.object({
813
826
  dash: z2.array(z2.number()).max(6).optional(),
814
827
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
815
828
  arrowSize: z2.number().min(4).max(32).default(10),
816
- opacity: z2.number().min(0).max(1).default(1)
829
+ opacity: z2.number().min(0).max(1).default(1),
830
+ shadow: drawShadowSchema.optional()
817
831
  }).strict();
818
832
  var drawPointSchema = z2.object({
819
833
  x: z2.number(),
@@ -827,7 +841,8 @@ var drawBezierSchema = z2.object({
827
841
  dash: z2.array(z2.number()).max(6).optional(),
828
842
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
829
843
  arrowSize: z2.number().min(4).max(32).default(10),
830
- opacity: z2.number().min(0).max(1).default(1)
844
+ opacity: z2.number().min(0).max(1).default(1),
845
+ shadow: drawShadowSchema.optional()
831
846
  }).strict();
832
847
  var drawPathSchema = z2.object({
833
848
  type: z2.literal("path"),
@@ -835,7 +850,8 @@ var drawPathSchema = z2.object({
835
850
  fill: colorHexSchema2.optional(),
836
851
  stroke: colorHexSchema2.optional(),
837
852
  strokeWidth: z2.number().min(0).max(32).default(0),
838
- opacity: z2.number().min(0).max(1).default(1)
853
+ opacity: z2.number().min(0).max(1).default(1),
854
+ shadow: drawShadowSchema.optional()
839
855
  }).strict();
840
856
  var drawBadgeSchema = z2.object({
841
857
  type: z2.literal("badge"),
@@ -849,7 +865,8 @@ var drawBadgeSchema = z2.object({
849
865
  paddingX: z2.number().min(0).max(64).default(10),
850
866
  paddingY: z2.number().min(0).max(32).default(4),
851
867
  borderRadius: z2.number().min(0).max(64).default(12),
852
- opacity: z2.number().min(0).max(1).default(1)
868
+ opacity: z2.number().min(0).max(1).default(1),
869
+ shadow: drawShadowSchema.optional()
853
870
  }).strict();
854
871
  var drawGradientRectSchema = z2.object({
855
872
  type: z2.literal("gradient-rect"),
@@ -859,7 +876,17 @@ var drawGradientRectSchema = z2.object({
859
876
  height: z2.number().positive(),
860
877
  gradient: gradientSchema,
861
878
  radius: z2.number().min(0).max(256).default(0),
862
- opacity: z2.number().min(0).max(1).default(1)
879
+ opacity: z2.number().min(0).max(1).default(1),
880
+ shadow: drawShadowSchema.optional()
881
+ }).strict();
882
+ var drawGridSchema = z2.object({
883
+ type: z2.literal("grid"),
884
+ spacing: z2.number().min(5).max(200).default(40),
885
+ color: colorHexSchema2.default("#1E2D4A"),
886
+ width: z2.number().min(0.1).max(4).default(0.5),
887
+ opacity: z2.number().min(0).max(1).default(0.2),
888
+ offsetX: z2.number().default(0),
889
+ offsetY: z2.number().default(0)
863
890
  }).strict();
864
891
  var drawCommandSchema = z2.discriminatedUnion("type", [
865
892
  drawRectSchema,
@@ -869,7 +896,8 @@ var drawCommandSchema = z2.discriminatedUnion("type", [
869
896
  drawBezierSchema,
870
897
  drawPathSchema,
871
898
  drawBadgeSchema,
872
- drawGradientRectSchema
899
+ drawGradientRectSchema,
900
+ drawGridSchema
873
901
  ]);
874
902
  var defaultCanvas = {
875
903
  width: 1200,
@@ -976,6 +1004,13 @@ var flowNodeElementSchema = z2.object({
976
1004
  badgePosition: z2.enum(["top", "inside-top"]).default("inside-top"),
977
1005
  shadow: flowNodeShadowSchema.optional()
978
1006
  }).strict();
1007
+ var anchorHintSchema = z2.union([
1008
+ z2.enum(["top", "bottom", "left", "right", "center"]),
1009
+ z2.object({
1010
+ x: z2.number().min(-1).max(1),
1011
+ y: z2.number().min(-1).max(1)
1012
+ }).strict()
1013
+ ]);
979
1014
  var connectionElementSchema = z2.object({
980
1015
  type: z2.literal("connection"),
981
1016
  from: z2.string().min(1).max(120),
@@ -989,9 +1024,12 @@ var connectionElementSchema = z2.object({
989
1024
  width: z2.number().min(0.5).max(10).optional(),
990
1025
  strokeWidth: z2.number().min(0.5).max(10).default(2),
991
1026
  arrowSize: z2.number().min(4).max(32).optional(),
1027
+ arrowPlacement: z2.enum(["endpoint", "boundary"]).default("endpoint"),
992
1028
  opacity: z2.number().min(0).max(1).default(1),
993
1029
  routing: z2.enum(["auto", "orthogonal", "curve", "arc"]).default("auto"),
994
- tension: z2.number().min(0.1).max(0.8).default(0.35)
1030
+ tension: z2.number().min(0.1).max(0.8).default(0.35),
1031
+ fromAnchor: anchorHintSchema.optional(),
1032
+ toAnchor: anchorHintSchema.optional()
995
1033
  }).strict();
996
1034
  var codeBlockStyleSchema = z2.object({
997
1035
  paddingVertical: z2.number().min(0).max(128).default(56),
@@ -2492,12 +2530,12 @@ function withAlpha2(color, alpha) {
2492
2530
  }
2493
2531
  function drawGradientRect(ctx, rect, gradient, borderRadius = 0) {
2494
2532
  const fill = gradient.type === "linear" ? createLinearRectGradient(ctx, rect, gradient.angle ?? 180) : ctx.createRadialGradient(
2495
- rect.x + rect.width / 2,
2496
- rect.y + rect.height / 2,
2497
- 0,
2498
- rect.x + rect.width / 2,
2499
- rect.y + rect.height / 2,
2500
- Math.max(rect.width, rect.height) / 2
2533
+ gradient.cx ?? rect.x + rect.width / 2,
2534
+ gradient.cy ?? rect.y + rect.height / 2,
2535
+ gradient.innerRadius ?? 0,
2536
+ gradient.cx ?? rect.x + rect.width / 2,
2537
+ gradient.cy ?? rect.y + rect.height / 2,
2538
+ gradient.outerRadius ?? Math.max(rect.width, rect.height) / 2
2501
2539
  );
2502
2540
  addGradientStops(fill, gradient.stops);
2503
2541
  ctx.save();
@@ -3122,21 +3160,61 @@ function edgeAnchor(bounds, target) {
3122
3160
  const t = absDx * hh > absDy * hw ? hw / absDx : hh / absDy;
3123
3161
  return { x: c.x + dx * t, y: c.y + dy * t };
3124
3162
  }
3163
+ function resolveAnchor(bounds, anchor, fallbackTarget) {
3164
+ if (!anchor) return edgeAnchor(bounds, fallbackTarget);
3165
+ if (typeof anchor === "string") {
3166
+ const c2 = rectCenter(bounds);
3167
+ switch (anchor) {
3168
+ case "top":
3169
+ return { x: c2.x, y: bounds.y };
3170
+ case "bottom":
3171
+ return { x: c2.x, y: bounds.y + bounds.height };
3172
+ case "left":
3173
+ return { x: bounds.x, y: c2.y };
3174
+ case "right":
3175
+ return { x: bounds.x + bounds.width, y: c2.y };
3176
+ case "center":
3177
+ return c2;
3178
+ }
3179
+ }
3180
+ const c = rectCenter(bounds);
3181
+ return {
3182
+ x: c.x + anchor.x * (bounds.width / 2),
3183
+ y: c.y + anchor.y * (bounds.height / 2)
3184
+ };
3185
+ }
3186
+ function anchorNormal(anchor, point, diagramCenter) {
3187
+ if (typeof anchor === "string") {
3188
+ switch (anchor) {
3189
+ case "top":
3190
+ return { x: 0, y: -1 };
3191
+ case "bottom":
3192
+ return { x: 0, y: 1 };
3193
+ case "left":
3194
+ return { x: -1, y: 0 };
3195
+ case "right":
3196
+ return { x: 1, y: 0 };
3197
+ case "center":
3198
+ return outwardNormal(point, diagramCenter);
3199
+ }
3200
+ }
3201
+ return outwardNormal(point, diagramCenter);
3202
+ }
3125
3203
  function outwardNormal(point, diagramCenter) {
3126
3204
  const dx = point.x - diagramCenter.x;
3127
3205
  const dy = point.y - diagramCenter.y;
3128
3206
  const len = Math.hypot(dx, dy) || 1;
3129
3207
  return { x: dx / len, y: dy / len };
3130
3208
  }
3131
- function curveRoute(fromBounds, toBounds, diagramCenter, tension) {
3209
+ function curveRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
3132
3210
  const fromCenter = rectCenter(fromBounds);
3133
3211
  const toCenter = rectCenter(toBounds);
3134
- const p0 = edgeAnchor(fromBounds, toCenter);
3135
- const p3 = edgeAnchor(toBounds, fromCenter);
3212
+ const p0 = resolveAnchor(fromBounds, fromAnchor, toCenter);
3213
+ const p3 = resolveAnchor(toBounds, toAnchor, fromCenter);
3136
3214
  const dist = Math.hypot(p3.x - p0.x, p3.y - p0.y);
3137
3215
  const offset = dist * tension;
3138
- const n0 = outwardNormal(p0, diagramCenter);
3139
- const n3 = outwardNormal(p3, diagramCenter);
3216
+ const n0 = anchorNormal(fromAnchor, p0, diagramCenter);
3217
+ const n3 = anchorNormal(toAnchor, p3, diagramCenter);
3140
3218
  const cp1 = { x: p0.x + n0.x * offset, y: p0.y + n0.y * offset };
3141
3219
  const cp2 = { x: p3.x + n3.x * offset, y: p3.y + n3.y * offset };
3142
3220
  return [p0, cp1, cp2, p3];
@@ -3150,11 +3228,11 @@ function localToWorld(origin, axisX, axisY, local) {
3150
3228
  y: origin.y + axisX.y * local.x + axisY.y * local.y
3151
3229
  };
3152
3230
  }
3153
- function arcRoute(fromBounds, toBounds, diagramCenter, tension) {
3231
+ function arcRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
3154
3232
  const fromCenter = rectCenter(fromBounds);
3155
3233
  const toCenter = rectCenter(toBounds);
3156
- const start = edgeAnchor(fromBounds, toCenter);
3157
- const end = edgeAnchor(toBounds, fromCenter);
3234
+ const start = resolveAnchor(fromBounds, fromAnchor, toCenter);
3235
+ const end = resolveAnchor(toBounds, toAnchor, fromCenter);
3158
3236
  const chord = { x: end.x - start.x, y: end.y - start.y };
3159
3237
  const chordLength = Math.hypot(chord.x, chord.y);
3160
3238
  if (chordLength < 1e-6) {
@@ -3192,11 +3270,11 @@ function arcRoute(fromBounds, toBounds, diagramCenter, tension) {
3192
3270
  [pMid, cp3, cp4, p3]
3193
3271
  ];
3194
3272
  }
3195
- function orthogonalRoute(fromBounds, toBounds) {
3273
+ function orthogonalRoute(fromBounds, toBounds, fromAnchor, toAnchor) {
3196
3274
  const fromC = rectCenter(fromBounds);
3197
3275
  const toC = rectCenter(toBounds);
3198
- const p0 = edgeAnchor(fromBounds, toC);
3199
- const p3 = edgeAnchor(toBounds, fromC);
3276
+ const p0 = resolveAnchor(fromBounds, fromAnchor, toC);
3277
+ const p3 = resolveAnchor(toBounds, toAnchor, fromC);
3200
3278
  const midX = (p0.x + p3.x) / 2;
3201
3279
  return [p0, { x: midX, y: p0.y }, { x: midX, y: p3.y }, p3];
3202
3280
  }
@@ -3207,6 +3285,35 @@ function bezierPointAt(p0, cp1, cp2, p3, t) {
3207
3285
  y: mt * mt * mt * p0.y + 3 * mt * mt * t * cp1.y + 3 * mt * t * t * cp2.y + t * t * t * p3.y
3208
3286
  };
3209
3287
  }
3288
+ function bezierTangentAt(p0, cp1, cp2, p3, t) {
3289
+ const mt = 1 - t;
3290
+ return {
3291
+ x: 3 * mt * mt * (cp1.x - p0.x) + 6 * mt * t * (cp2.x - cp1.x) + 3 * t * t * (p3.x - cp2.x),
3292
+ y: 3 * mt * mt * (cp1.y - p0.y) + 6 * mt * t * (cp2.y - cp1.y) + 3 * t * t * (p3.y - cp2.y)
3293
+ };
3294
+ }
3295
+ function isInsideRect(point, rect) {
3296
+ return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
3297
+ }
3298
+ function findBoundaryIntersection(p0, cp1, cp2, p3, targetRect, searchFromEnd) {
3299
+ const step = 5e-3;
3300
+ if (searchFromEnd) {
3301
+ for (let t = 0.95; t >= 0.5; t -= step) {
3302
+ const pt = bezierPointAt(p0, cp1, cp2, p3, t);
3303
+ if (!isInsideRect(pt, targetRect)) {
3304
+ return t;
3305
+ }
3306
+ }
3307
+ } else {
3308
+ for (let t = 0.05; t <= 0.5; t += step) {
3309
+ const pt = bezierPointAt(p0, cp1, cp2, p3, t);
3310
+ if (!isInsideRect(pt, targetRect)) {
3311
+ return t;
3312
+ }
3313
+ }
3314
+ }
3315
+ return void 0;
3316
+ }
3210
3317
  function pointAlongArc(route, t) {
3211
3318
  const [first, second] = route;
3212
3319
  if (t <= 0.5) {
@@ -3334,8 +3441,16 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
3334
3441
  let labelPoint;
3335
3442
  ctx.save();
3336
3443
  ctx.globalAlpha = conn.opacity;
3444
+ const arrowPlacement = conn.arrowPlacement ?? "endpoint";
3337
3445
  if (routing === "curve") {
3338
- const [p0, cp1, cp2, p3] = curveRoute(fromBounds, toBounds, diagramCenter, tension);
3446
+ const [p0, cp1, cp2, p3] = curveRoute(
3447
+ fromBounds,
3448
+ toBounds,
3449
+ diagramCenter,
3450
+ tension,
3451
+ conn.fromAnchor,
3452
+ conn.toAnchor
3453
+ );
3339
3454
  ctx.strokeStyle = style.color;
3340
3455
  ctx.lineWidth = style.width;
3341
3456
  ctx.setLineDash(style.dash ?? []);
@@ -3349,8 +3464,33 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
3349
3464
  startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
3350
3465
  endAngle = Math.atan2(p3.y - cp2.y, p3.x - cp2.x);
3351
3466
  labelPoint = bezierPointAt(p0, cp1, cp2, p3, labelT);
3467
+ if (arrowPlacement === "boundary") {
3468
+ if (conn.arrow === "end" || conn.arrow === "both") {
3469
+ const tEnd = findBoundaryIntersection(p0, cp1, cp2, p3, toBounds, true);
3470
+ if (tEnd !== void 0) {
3471
+ endPoint = bezierPointAt(p0, cp1, cp2, p3, tEnd);
3472
+ const tangent = bezierTangentAt(p0, cp1, cp2, p3, tEnd);
3473
+ endAngle = Math.atan2(tangent.y, tangent.x);
3474
+ }
3475
+ }
3476
+ if (conn.arrow === "start" || conn.arrow === "both") {
3477
+ const tStart = findBoundaryIntersection(p0, cp1, cp2, p3, fromBounds, false);
3478
+ if (tStart !== void 0) {
3479
+ startPoint = bezierPointAt(p0, cp1, cp2, p3, tStart);
3480
+ const tangent = bezierTangentAt(p0, cp1, cp2, p3, tStart);
3481
+ startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
3482
+ }
3483
+ }
3484
+ }
3352
3485
  } else if (routing === "arc") {
3353
- const [first, second] = arcRoute(fromBounds, toBounds, diagramCenter, tension);
3486
+ const [first, second] = arcRoute(
3487
+ fromBounds,
3488
+ toBounds,
3489
+ diagramCenter,
3490
+ tension,
3491
+ conn.fromAnchor,
3492
+ conn.toAnchor
3493
+ );
3354
3494
  const [p0, cp1, cp2, pMid] = first;
3355
3495
  const [, cp3, cp4, p3] = second;
3356
3496
  ctx.strokeStyle = style.color;
@@ -3367,9 +3507,29 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
3367
3507
  startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
3368
3508
  endAngle = Math.atan2(p3.y - cp4.y, p3.x - cp4.x);
3369
3509
  labelPoint = pointAlongArc([first, second], labelT);
3510
+ if (arrowPlacement === "boundary") {
3511
+ if (conn.arrow === "end" || conn.arrow === "both") {
3512
+ const [, s_cp3, s_cp4, s_p3] = second;
3513
+ const tEnd = findBoundaryIntersection(pMid, s_cp3, s_cp4, s_p3, toBounds, true);
3514
+ if (tEnd !== void 0) {
3515
+ endPoint = bezierPointAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
3516
+ const tangent = bezierTangentAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
3517
+ endAngle = Math.atan2(tangent.y, tangent.x);
3518
+ }
3519
+ }
3520
+ if (conn.arrow === "start" || conn.arrow === "both") {
3521
+ const tStart = findBoundaryIntersection(p0, cp1, cp2, pMid, fromBounds, false);
3522
+ if (tStart !== void 0) {
3523
+ startPoint = bezierPointAt(p0, cp1, cp2, pMid, tStart);
3524
+ const tangent = bezierTangentAt(p0, cp1, cp2, pMid, tStart);
3525
+ startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
3526
+ }
3527
+ }
3528
+ }
3370
3529
  } else {
3371
- const useElkRoute = routing === "auto" && (edgeRoute?.points.length ?? 0) >= 2;
3372
- linePoints = useElkRoute ? edgeRoute?.points ?? orthogonalRoute(fromBounds, toBounds) : orthogonalRoute(fromBounds, toBounds);
3530
+ const hasAnchorHints = conn.fromAnchor !== void 0 || conn.toAnchor !== void 0;
3531
+ const useElkRoute = routing === "auto" && !hasAnchorHints && (edgeRoute?.points.length ?? 0) >= 2;
3532
+ linePoints = useElkRoute ? edgeRoute?.points ?? orthogonalRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor) : orthogonalRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor);
3373
3533
  startPoint = linePoints[0];
3374
3534
  const startSegment = linePoints[1] ?? linePoints[0];
3375
3535
  const endStart = linePoints[linePoints.length - 2] ?? linePoints[0];
@@ -3601,6 +3761,13 @@ function expandRect(rect, amount) {
3601
3761
  height: rect.height + amount * 2
3602
3762
  };
3603
3763
  }
3764
+ function applyDrawShadow(ctx, shadow) {
3765
+ if (!shadow) return;
3766
+ ctx.shadowColor = shadow.color;
3767
+ ctx.shadowBlur = shadow.blur;
3768
+ ctx.shadowOffsetX = shadow.offsetX;
3769
+ ctx.shadowOffsetY = shadow.offsetY;
3770
+ }
3604
3771
  function fromPoints(points) {
3605
3772
  const minX = Math.min(...points.map((point) => point.x));
3606
3773
  const minY = Math.min(...points.map((point) => point.y));
@@ -3782,6 +3949,7 @@ function renderDrawCommands(ctx, commands, theme) {
3782
3949
  height: command.height
3783
3950
  };
3784
3951
  withOpacity(ctx, command.opacity, () => {
3952
+ applyDrawShadow(ctx, command.shadow);
3785
3953
  roundRectPath(ctx, rect, command.radius);
3786
3954
  if (command.fill) {
3787
3955
  ctx.fillStyle = command.fill;
@@ -3805,6 +3973,7 @@ function renderDrawCommands(ctx, commands, theme) {
3805
3973
  }
3806
3974
  case "circle": {
3807
3975
  withOpacity(ctx, command.opacity, () => {
3976
+ applyDrawShadow(ctx, command.shadow);
3808
3977
  ctx.beginPath();
3809
3978
  ctx.arc(command.cx, command.cy, command.radius, 0, Math.PI * 2);
3810
3979
  ctx.closePath();
@@ -3839,6 +4008,7 @@ function renderDrawCommands(ctx, commands, theme) {
3839
4008
  case "text": {
3840
4009
  const fontFamily = resolveDrawFont(theme, command.fontFamily);
3841
4010
  withOpacity(ctx, command.opacity, () => {
4011
+ applyDrawShadow(ctx, command.shadow);
3842
4012
  applyFont(ctx, {
3843
4013
  size: command.fontSize,
3844
4014
  weight: command.fontWeight,
@@ -3889,6 +4059,7 @@ function renderDrawCommands(ctx, commands, theme) {
3889
4059
  const to = { x: command.x2, y: command.y2 };
3890
4060
  const lineAngle = angleBetween(from, to);
3891
4061
  withOpacity(ctx, command.opacity, () => {
4062
+ applyDrawShadow(ctx, command.shadow);
3892
4063
  drawLine(ctx, from, to, {
3893
4064
  color: command.color,
3894
4065
  width: command.width,
@@ -3913,6 +4084,7 @@ function renderDrawCommands(ctx, commands, theme) {
3913
4084
  case "bezier": {
3914
4085
  const points = command.points;
3915
4086
  withOpacity(ctx, command.opacity, () => {
4087
+ applyDrawShadow(ctx, command.shadow);
3916
4088
  drawBezier(ctx, points, {
3917
4089
  color: command.color,
3918
4090
  width: command.width,
@@ -3946,6 +4118,7 @@ function renderDrawCommands(ctx, commands, theme) {
3946
4118
  const operations = parseSvgPath(command.d);
3947
4119
  const baseBounds = pathBounds(operations);
3948
4120
  withOpacity(ctx, command.opacity, () => {
4121
+ applyDrawShadow(ctx, command.shadow);
3949
4122
  applySvgOperations(ctx, operations);
3950
4123
  if (command.fill) {
3951
4124
  ctx.fillStyle = command.fill;
@@ -3986,9 +4159,16 @@ function renderDrawCommands(ctx, commands, theme) {
3986
4159
  height: textHeight + command.paddingY * 2
3987
4160
  };
3988
4161
  withOpacity(ctx, command.opacity, () => {
4162
+ applyDrawShadow(ctx, command.shadow);
3989
4163
  roundRectPath(ctx, rect, command.borderRadius);
3990
4164
  ctx.fillStyle = command.background;
3991
4165
  ctx.fill();
4166
+ if (command.shadow) {
4167
+ ctx.shadowColor = "transparent";
4168
+ ctx.shadowBlur = 0;
4169
+ ctx.shadowOffsetX = 0;
4170
+ ctx.shadowOffsetY = 0;
4171
+ }
3992
4172
  applyFont(ctx, {
3993
4173
  size: command.fontSize,
3994
4174
  weight: 600,
@@ -4016,6 +4196,7 @@ function renderDrawCommands(ctx, commands, theme) {
4016
4196
  height: command.height
4017
4197
  };
4018
4198
  withOpacity(ctx, command.opacity, () => {
4199
+ applyDrawShadow(ctx, command.shadow);
4019
4200
  drawGradientRect(ctx, rect, command.gradient, command.radius);
4020
4201
  });
4021
4202
  rendered.push({
@@ -4026,6 +4207,36 @@ function renderDrawCommands(ctx, commands, theme) {
4026
4207
  });
4027
4208
  break;
4028
4209
  }
4210
+ case "grid": {
4211
+ const canvasWidth = ctx.canvas.width;
4212
+ const canvasHeight = ctx.canvas.height;
4213
+ withOpacity(ctx, command.opacity, () => {
4214
+ ctx.strokeStyle = command.color;
4215
+ ctx.lineWidth = command.width;
4216
+ const startX = command.offsetX % command.spacing;
4217
+ for (let x = startX; x <= canvasWidth; x += command.spacing) {
4218
+ ctx.beginPath();
4219
+ ctx.moveTo(x, 0);
4220
+ ctx.lineTo(x, canvasHeight);
4221
+ ctx.stroke();
4222
+ }
4223
+ const startY = command.offsetY % command.spacing;
4224
+ for (let y = startY; y <= canvasHeight; y += command.spacing) {
4225
+ ctx.beginPath();
4226
+ ctx.moveTo(0, y);
4227
+ ctx.lineTo(canvasWidth, y);
4228
+ ctx.stroke();
4229
+ }
4230
+ });
4231
+ rendered.push({
4232
+ id,
4233
+ kind: "draw",
4234
+ bounds: { x: 0, y: 0, width: canvasWidth, height: canvasHeight },
4235
+ foregroundColor: command.color,
4236
+ allowOverlap: true
4237
+ });
4238
+ break;
4239
+ }
4029
4240
  }
4030
4241
  }
4031
4242
  return rendered;
package/dist/qa.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { R as RenderMetadata, D as DesignSpec } from './spec.schema-Dm_wOLTd.js';
1
+ import { R as RenderMetadata, D as DesignSpec } from './spec.schema-BDvtn_mJ.js';
2
2
  import 'zod';
3
3
  import '@napi-rs/canvas';
4
4
 
package/dist/qa.js CHANGED
@@ -495,9 +495,19 @@ var linearGradientSchema = z2.object({
495
495
  }).strict();
496
496
  var radialGradientSchema = z2.object({
497
497
  type: z2.literal("radial"),
498
+ cx: z2.number().optional(),
499
+ cy: z2.number().optional(),
500
+ innerRadius: z2.number().min(0).optional(),
501
+ outerRadius: z2.number().min(0).optional(),
498
502
  stops: z2.array(gradientStopSchema).min(2)
499
503
  }).strict();
500
504
  var gradientSchema = z2.discriminatedUnion("type", [linearGradientSchema, radialGradientSchema]);
505
+ var drawShadowSchema = z2.object({
506
+ color: colorHexSchema2.default("rgba(0,0,0,0.5)"),
507
+ blur: z2.number().min(0).max(64).default(10),
508
+ offsetX: z2.number().default(0),
509
+ offsetY: z2.number().default(4)
510
+ }).strict();
501
511
  var drawFontFamilySchema = z2.enum(["heading", "body", "mono"]);
502
512
  var drawRectSchema = z2.object({
503
513
  type: z2.literal("rect"),
@@ -509,7 +519,8 @@ var drawRectSchema = z2.object({
509
519
  stroke: colorHexSchema2.optional(),
510
520
  strokeWidth: z2.number().min(0).max(32).default(0),
511
521
  radius: z2.number().min(0).max(256).default(0),
512
- opacity: z2.number().min(0).max(1).default(1)
522
+ opacity: z2.number().min(0).max(1).default(1),
523
+ shadow: drawShadowSchema.optional()
513
524
  }).strict();
514
525
  var drawCircleSchema = z2.object({
515
526
  type: z2.literal("circle"),
@@ -519,7 +530,8 @@ var drawCircleSchema = z2.object({
519
530
  fill: colorHexSchema2.optional(),
520
531
  stroke: colorHexSchema2.optional(),
521
532
  strokeWidth: z2.number().min(0).max(32).default(0),
522
- opacity: z2.number().min(0).max(1).default(1)
533
+ opacity: z2.number().min(0).max(1).default(1),
534
+ shadow: drawShadowSchema.optional()
523
535
  }).strict();
524
536
  var drawTextSchema = z2.object({
525
537
  type: z2.literal("text"),
@@ -534,7 +546,8 @@ var drawTextSchema = z2.object({
534
546
  baseline: z2.enum(["top", "middle", "alphabetic", "bottom"]).default("alphabetic"),
535
547
  letterSpacing: z2.number().min(-10).max(50).default(0),
536
548
  maxWidth: z2.number().positive().optional(),
537
- opacity: z2.number().min(0).max(1).default(1)
549
+ opacity: z2.number().min(0).max(1).default(1),
550
+ shadow: drawShadowSchema.optional()
538
551
  }).strict();
539
552
  var drawLineSchema = z2.object({
540
553
  type: z2.literal("line"),
@@ -547,7 +560,8 @@ var drawLineSchema = z2.object({
547
560
  dash: z2.array(z2.number()).max(6).optional(),
548
561
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
549
562
  arrowSize: z2.number().min(4).max(32).default(10),
550
- opacity: z2.number().min(0).max(1).default(1)
563
+ opacity: z2.number().min(0).max(1).default(1),
564
+ shadow: drawShadowSchema.optional()
551
565
  }).strict();
552
566
  var drawPointSchema = z2.object({
553
567
  x: z2.number(),
@@ -561,7 +575,8 @@ var drawBezierSchema = z2.object({
561
575
  dash: z2.array(z2.number()).max(6).optional(),
562
576
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
563
577
  arrowSize: z2.number().min(4).max(32).default(10),
564
- opacity: z2.number().min(0).max(1).default(1)
578
+ opacity: z2.number().min(0).max(1).default(1),
579
+ shadow: drawShadowSchema.optional()
565
580
  }).strict();
566
581
  var drawPathSchema = z2.object({
567
582
  type: z2.literal("path"),
@@ -569,7 +584,8 @@ var drawPathSchema = z2.object({
569
584
  fill: colorHexSchema2.optional(),
570
585
  stroke: colorHexSchema2.optional(),
571
586
  strokeWidth: z2.number().min(0).max(32).default(0),
572
- opacity: z2.number().min(0).max(1).default(1)
587
+ opacity: z2.number().min(0).max(1).default(1),
588
+ shadow: drawShadowSchema.optional()
573
589
  }).strict();
574
590
  var drawBadgeSchema = z2.object({
575
591
  type: z2.literal("badge"),
@@ -583,7 +599,8 @@ var drawBadgeSchema = z2.object({
583
599
  paddingX: z2.number().min(0).max(64).default(10),
584
600
  paddingY: z2.number().min(0).max(32).default(4),
585
601
  borderRadius: z2.number().min(0).max(64).default(12),
586
- opacity: z2.number().min(0).max(1).default(1)
602
+ opacity: z2.number().min(0).max(1).default(1),
603
+ shadow: drawShadowSchema.optional()
587
604
  }).strict();
588
605
  var drawGradientRectSchema = z2.object({
589
606
  type: z2.literal("gradient-rect"),
@@ -593,7 +610,17 @@ var drawGradientRectSchema = z2.object({
593
610
  height: z2.number().positive(),
594
611
  gradient: gradientSchema,
595
612
  radius: z2.number().min(0).max(256).default(0),
596
- opacity: z2.number().min(0).max(1).default(1)
613
+ opacity: z2.number().min(0).max(1).default(1),
614
+ shadow: drawShadowSchema.optional()
615
+ }).strict();
616
+ var drawGridSchema = z2.object({
617
+ type: z2.literal("grid"),
618
+ spacing: z2.number().min(5).max(200).default(40),
619
+ color: colorHexSchema2.default("#1E2D4A"),
620
+ width: z2.number().min(0.1).max(4).default(0.5),
621
+ opacity: z2.number().min(0).max(1).default(0.2),
622
+ offsetX: z2.number().default(0),
623
+ offsetY: z2.number().default(0)
597
624
  }).strict();
598
625
  var drawCommandSchema = z2.discriminatedUnion("type", [
599
626
  drawRectSchema,
@@ -603,7 +630,8 @@ var drawCommandSchema = z2.discriminatedUnion("type", [
603
630
  drawBezierSchema,
604
631
  drawPathSchema,
605
632
  drawBadgeSchema,
606
- drawGradientRectSchema
633
+ drawGradientRectSchema,
634
+ drawGridSchema
607
635
  ]);
608
636
  var defaultCanvas = {
609
637
  width: 1200,
@@ -709,6 +737,13 @@ var flowNodeElementSchema = z2.object({
709
737
  badgePosition: z2.enum(["top", "inside-top"]).default("inside-top"),
710
738
  shadow: flowNodeShadowSchema.optional()
711
739
  }).strict();
740
+ var anchorHintSchema = z2.union([
741
+ z2.enum(["top", "bottom", "left", "right", "center"]),
742
+ z2.object({
743
+ x: z2.number().min(-1).max(1),
744
+ y: z2.number().min(-1).max(1)
745
+ }).strict()
746
+ ]);
712
747
  var connectionElementSchema = z2.object({
713
748
  type: z2.literal("connection"),
714
749
  from: z2.string().min(1).max(120),
@@ -722,9 +757,12 @@ var connectionElementSchema = z2.object({
722
757
  width: z2.number().min(0.5).max(10).optional(),
723
758
  strokeWidth: z2.number().min(0.5).max(10).default(2),
724
759
  arrowSize: z2.number().min(4).max(32).optional(),
760
+ arrowPlacement: z2.enum(["endpoint", "boundary"]).default("endpoint"),
725
761
  opacity: z2.number().min(0).max(1).default(1),
726
762
  routing: z2.enum(["auto", "orthogonal", "curve", "arc"]).default("auto"),
727
- tension: z2.number().min(0.1).max(0.8).default(0.35)
763
+ tension: z2.number().min(0.1).max(0.8).default(0.35),
764
+ fromAnchor: anchorHintSchema.optional(),
765
+ toAnchor: anchorHintSchema.optional()
728
766
  }).strict();
729
767
  var codeBlockStyleSchema = z2.object({
730
768
  paddingVertical: z2.number().min(0).max(128).default(56),
@@ -1,3 +1,3 @@
1
- export { h as DEFAULT_GENERATOR_VERSION, N as LayoutSnapshot, a as Rect, R as RenderMetadata, Q as RenderResult, d as RenderedElement, Z as WrittenArtifacts, a0 as computeSpecHash, aj as inferSidecarPath, am as renderDesign, ao as writeRenderArtifacts } from './spec.schema-Dm_wOLTd.js';
1
+ export { i as DEFAULT_GENERATOR_VERSION, P as LayoutSnapshot, a as Rect, R as RenderMetadata, U as RenderResult, d as RenderedElement, $ as WrittenArtifacts, a2 as computeSpecHash, al as inferSidecarPath, ao as renderDesign, aq as writeRenderArtifacts } from './spec.schema-BDvtn_mJ.js';
2
2
  import 'zod';
3
3
  import '@napi-rs/canvas';