@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/renderer.js CHANGED
@@ -1151,12 +1151,12 @@ function withAlpha2(color, alpha) {
1151
1151
  }
1152
1152
  function drawGradientRect(ctx, rect, gradient, borderRadius = 0) {
1153
1153
  const fill = gradient.type === "linear" ? createLinearRectGradient(ctx, rect, gradient.angle ?? 180) : ctx.createRadialGradient(
1154
- rect.x + rect.width / 2,
1155
- rect.y + rect.height / 2,
1156
- 0,
1157
- rect.x + rect.width / 2,
1158
- rect.y + rect.height / 2,
1159
- Math.max(rect.width, rect.height) / 2
1154
+ gradient.cx ?? rect.x + rect.width / 2,
1155
+ gradient.cy ?? rect.y + rect.height / 2,
1156
+ gradient.innerRadius ?? 0,
1157
+ gradient.cx ?? rect.x + rect.width / 2,
1158
+ gradient.cy ?? rect.y + rect.height / 2,
1159
+ gradient.outerRadius ?? Math.max(rect.width, rect.height) / 2
1160
1160
  );
1161
1161
  addGradientStops(fill, gradient.stops);
1162
1162
  ctx.save();
@@ -2009,21 +2009,61 @@ function edgeAnchor(bounds, target) {
2009
2009
  const t = absDx * hh > absDy * hw ? hw / absDx : hh / absDy;
2010
2010
  return { x: c.x + dx * t, y: c.y + dy * t };
2011
2011
  }
2012
+ function resolveAnchor(bounds, anchor, fallbackTarget) {
2013
+ if (!anchor) return edgeAnchor(bounds, fallbackTarget);
2014
+ if (typeof anchor === "string") {
2015
+ const c2 = rectCenter(bounds);
2016
+ switch (anchor) {
2017
+ case "top":
2018
+ return { x: c2.x, y: bounds.y };
2019
+ case "bottom":
2020
+ return { x: c2.x, y: bounds.y + bounds.height };
2021
+ case "left":
2022
+ return { x: bounds.x, y: c2.y };
2023
+ case "right":
2024
+ return { x: bounds.x + bounds.width, y: c2.y };
2025
+ case "center":
2026
+ return c2;
2027
+ }
2028
+ }
2029
+ const c = rectCenter(bounds);
2030
+ return {
2031
+ x: c.x + anchor.x * (bounds.width / 2),
2032
+ y: c.y + anchor.y * (bounds.height / 2)
2033
+ };
2034
+ }
2035
+ function anchorNormal(anchor, point, diagramCenter) {
2036
+ if (typeof anchor === "string") {
2037
+ switch (anchor) {
2038
+ case "top":
2039
+ return { x: 0, y: -1 };
2040
+ case "bottom":
2041
+ return { x: 0, y: 1 };
2042
+ case "left":
2043
+ return { x: -1, y: 0 };
2044
+ case "right":
2045
+ return { x: 1, y: 0 };
2046
+ case "center":
2047
+ return outwardNormal(point, diagramCenter);
2048
+ }
2049
+ }
2050
+ return outwardNormal(point, diagramCenter);
2051
+ }
2012
2052
  function outwardNormal(point, diagramCenter) {
2013
2053
  const dx = point.x - diagramCenter.x;
2014
2054
  const dy = point.y - diagramCenter.y;
2015
2055
  const len = Math.hypot(dx, dy) || 1;
2016
2056
  return { x: dx / len, y: dy / len };
2017
2057
  }
2018
- function curveRoute(fromBounds, toBounds, diagramCenter, tension) {
2058
+ function curveRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
2019
2059
  const fromCenter = rectCenter(fromBounds);
2020
2060
  const toCenter = rectCenter(toBounds);
2021
- const p0 = edgeAnchor(fromBounds, toCenter);
2022
- const p3 = edgeAnchor(toBounds, fromCenter);
2061
+ const p0 = resolveAnchor(fromBounds, fromAnchor, toCenter);
2062
+ const p3 = resolveAnchor(toBounds, toAnchor, fromCenter);
2023
2063
  const dist = Math.hypot(p3.x - p0.x, p3.y - p0.y);
2024
2064
  const offset = dist * tension;
2025
- const n0 = outwardNormal(p0, diagramCenter);
2026
- const n3 = outwardNormal(p3, diagramCenter);
2065
+ const n0 = anchorNormal(fromAnchor, p0, diagramCenter);
2066
+ const n3 = anchorNormal(toAnchor, p3, diagramCenter);
2027
2067
  const cp1 = { x: p0.x + n0.x * offset, y: p0.y + n0.y * offset };
2028
2068
  const cp2 = { x: p3.x + n3.x * offset, y: p3.y + n3.y * offset };
2029
2069
  return [p0, cp1, cp2, p3];
@@ -2037,11 +2077,11 @@ function localToWorld(origin, axisX, axisY, local) {
2037
2077
  y: origin.y + axisX.y * local.x + axisY.y * local.y
2038
2078
  };
2039
2079
  }
2040
- function arcRoute(fromBounds, toBounds, diagramCenter, tension) {
2080
+ function arcRoute(fromBounds, toBounds, diagramCenter, tension, fromAnchor, toAnchor) {
2041
2081
  const fromCenter = rectCenter(fromBounds);
2042
2082
  const toCenter = rectCenter(toBounds);
2043
- const start = edgeAnchor(fromBounds, toCenter);
2044
- const end = edgeAnchor(toBounds, fromCenter);
2083
+ const start = resolveAnchor(fromBounds, fromAnchor, toCenter);
2084
+ const end = resolveAnchor(toBounds, toAnchor, fromCenter);
2045
2085
  const chord = { x: end.x - start.x, y: end.y - start.y };
2046
2086
  const chordLength = Math.hypot(chord.x, chord.y);
2047
2087
  if (chordLength < 1e-6) {
@@ -2079,11 +2119,11 @@ function arcRoute(fromBounds, toBounds, diagramCenter, tension) {
2079
2119
  [pMid, cp3, cp4, p3]
2080
2120
  ];
2081
2121
  }
2082
- function orthogonalRoute(fromBounds, toBounds) {
2122
+ function orthogonalRoute(fromBounds, toBounds, fromAnchor, toAnchor) {
2083
2123
  const fromC = rectCenter(fromBounds);
2084
2124
  const toC = rectCenter(toBounds);
2085
- const p0 = edgeAnchor(fromBounds, toC);
2086
- const p3 = edgeAnchor(toBounds, fromC);
2125
+ const p0 = resolveAnchor(fromBounds, fromAnchor, toC);
2126
+ const p3 = resolveAnchor(toBounds, toAnchor, fromC);
2087
2127
  const midX = (p0.x + p3.x) / 2;
2088
2128
  return [p0, { x: midX, y: p0.y }, { x: midX, y: p3.y }, p3];
2089
2129
  }
@@ -2094,6 +2134,35 @@ function bezierPointAt(p0, cp1, cp2, p3, t) {
2094
2134
  y: mt * mt * mt * p0.y + 3 * mt * mt * t * cp1.y + 3 * mt * t * t * cp2.y + t * t * t * p3.y
2095
2135
  };
2096
2136
  }
2137
+ function bezierTangentAt(p0, cp1, cp2, p3, t) {
2138
+ const mt = 1 - t;
2139
+ return {
2140
+ x: 3 * mt * mt * (cp1.x - p0.x) + 6 * mt * t * (cp2.x - cp1.x) + 3 * t * t * (p3.x - cp2.x),
2141
+ y: 3 * mt * mt * (cp1.y - p0.y) + 6 * mt * t * (cp2.y - cp1.y) + 3 * t * t * (p3.y - cp2.y)
2142
+ };
2143
+ }
2144
+ function isInsideRect(point, rect) {
2145
+ return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
2146
+ }
2147
+ function findBoundaryIntersection(p0, cp1, cp2, p3, targetRect, searchFromEnd) {
2148
+ const step = 5e-3;
2149
+ if (searchFromEnd) {
2150
+ for (let t = 0.95; t >= 0.5; t -= step) {
2151
+ const pt = bezierPointAt(p0, cp1, cp2, p3, t);
2152
+ if (!isInsideRect(pt, targetRect)) {
2153
+ return t;
2154
+ }
2155
+ }
2156
+ } else {
2157
+ for (let t = 0.05; t <= 0.5; t += step) {
2158
+ const pt = bezierPointAt(p0, cp1, cp2, p3, t);
2159
+ if (!isInsideRect(pt, targetRect)) {
2160
+ return t;
2161
+ }
2162
+ }
2163
+ }
2164
+ return void 0;
2165
+ }
2097
2166
  function pointAlongArc(route, t) {
2098
2167
  const [first, second] = route;
2099
2168
  if (t <= 0.5) {
@@ -2221,8 +2290,16 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
2221
2290
  let labelPoint;
2222
2291
  ctx.save();
2223
2292
  ctx.globalAlpha = conn.opacity;
2293
+ const arrowPlacement = conn.arrowPlacement ?? "endpoint";
2224
2294
  if (routing === "curve") {
2225
- const [p0, cp1, cp2, p3] = curveRoute(fromBounds, toBounds, diagramCenter, tension);
2295
+ const [p0, cp1, cp2, p3] = curveRoute(
2296
+ fromBounds,
2297
+ toBounds,
2298
+ diagramCenter,
2299
+ tension,
2300
+ conn.fromAnchor,
2301
+ conn.toAnchor
2302
+ );
2226
2303
  ctx.strokeStyle = style.color;
2227
2304
  ctx.lineWidth = style.width;
2228
2305
  ctx.setLineDash(style.dash ?? []);
@@ -2236,8 +2313,33 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
2236
2313
  startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
2237
2314
  endAngle = Math.atan2(p3.y - cp2.y, p3.x - cp2.x);
2238
2315
  labelPoint = bezierPointAt(p0, cp1, cp2, p3, labelT);
2316
+ if (arrowPlacement === "boundary") {
2317
+ if (conn.arrow === "end" || conn.arrow === "both") {
2318
+ const tEnd = findBoundaryIntersection(p0, cp1, cp2, p3, toBounds, true);
2319
+ if (tEnd !== void 0) {
2320
+ endPoint = bezierPointAt(p0, cp1, cp2, p3, tEnd);
2321
+ const tangent = bezierTangentAt(p0, cp1, cp2, p3, tEnd);
2322
+ endAngle = Math.atan2(tangent.y, tangent.x);
2323
+ }
2324
+ }
2325
+ if (conn.arrow === "start" || conn.arrow === "both") {
2326
+ const tStart = findBoundaryIntersection(p0, cp1, cp2, p3, fromBounds, false);
2327
+ if (tStart !== void 0) {
2328
+ startPoint = bezierPointAt(p0, cp1, cp2, p3, tStart);
2329
+ const tangent = bezierTangentAt(p0, cp1, cp2, p3, tStart);
2330
+ startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
2331
+ }
2332
+ }
2333
+ }
2239
2334
  } else if (routing === "arc") {
2240
- const [first, second] = arcRoute(fromBounds, toBounds, diagramCenter, tension);
2335
+ const [first, second] = arcRoute(
2336
+ fromBounds,
2337
+ toBounds,
2338
+ diagramCenter,
2339
+ tension,
2340
+ conn.fromAnchor,
2341
+ conn.toAnchor
2342
+ );
2241
2343
  const [p0, cp1, cp2, pMid] = first;
2242
2344
  const [, cp3, cp4, p3] = second;
2243
2345
  ctx.strokeStyle = style.color;
@@ -2254,9 +2356,29 @@ function renderConnection(ctx, conn, fromBounds, toBounds, theme, edgeRoute, opt
2254
2356
  startAngle = Math.atan2(p0.y - cp1.y, p0.x - cp1.x);
2255
2357
  endAngle = Math.atan2(p3.y - cp4.y, p3.x - cp4.x);
2256
2358
  labelPoint = pointAlongArc([first, second], labelT);
2359
+ if (arrowPlacement === "boundary") {
2360
+ if (conn.arrow === "end" || conn.arrow === "both") {
2361
+ const [, s_cp3, s_cp4, s_p3] = second;
2362
+ const tEnd = findBoundaryIntersection(pMid, s_cp3, s_cp4, s_p3, toBounds, true);
2363
+ if (tEnd !== void 0) {
2364
+ endPoint = bezierPointAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
2365
+ const tangent = bezierTangentAt(pMid, s_cp3, s_cp4, s_p3, tEnd);
2366
+ endAngle = Math.atan2(tangent.y, tangent.x);
2367
+ }
2368
+ }
2369
+ if (conn.arrow === "start" || conn.arrow === "both") {
2370
+ const tStart = findBoundaryIntersection(p0, cp1, cp2, pMid, fromBounds, false);
2371
+ if (tStart !== void 0) {
2372
+ startPoint = bezierPointAt(p0, cp1, cp2, pMid, tStart);
2373
+ const tangent = bezierTangentAt(p0, cp1, cp2, pMid, tStart);
2374
+ startAngle = Math.atan2(tangent.y, tangent.x) + Math.PI;
2375
+ }
2376
+ }
2377
+ }
2257
2378
  } else {
2258
- const useElkRoute = routing === "auto" && (edgeRoute?.points.length ?? 0) >= 2;
2259
- linePoints = useElkRoute ? edgeRoute?.points ?? orthogonalRoute(fromBounds, toBounds) : orthogonalRoute(fromBounds, toBounds);
2379
+ const hasAnchorHints = conn.fromAnchor !== void 0 || conn.toAnchor !== void 0;
2380
+ const useElkRoute = routing === "auto" && !hasAnchorHints && (edgeRoute?.points.length ?? 0) >= 2;
2381
+ linePoints = useElkRoute ? edgeRoute?.points ?? orthogonalRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor) : orthogonalRoute(fromBounds, toBounds, conn.fromAnchor, conn.toAnchor);
2260
2382
  startPoint = linePoints[0];
2261
2383
  const startSegment = linePoints[1] ?? linePoints[0];
2262
2384
  const endStart = linePoints[linePoints.length - 2] ?? linePoints[0];
@@ -2488,6 +2610,13 @@ function expandRect(rect, amount) {
2488
2610
  height: rect.height + amount * 2
2489
2611
  };
2490
2612
  }
2613
+ function applyDrawShadow(ctx, shadow) {
2614
+ if (!shadow) return;
2615
+ ctx.shadowColor = shadow.color;
2616
+ ctx.shadowBlur = shadow.blur;
2617
+ ctx.shadowOffsetX = shadow.offsetX;
2618
+ ctx.shadowOffsetY = shadow.offsetY;
2619
+ }
2491
2620
  function fromPoints(points) {
2492
2621
  const minX = Math.min(...points.map((point) => point.x));
2493
2622
  const minY = Math.min(...points.map((point) => point.y));
@@ -2669,6 +2798,7 @@ function renderDrawCommands(ctx, commands, theme) {
2669
2798
  height: command.height
2670
2799
  };
2671
2800
  withOpacity(ctx, command.opacity, () => {
2801
+ applyDrawShadow(ctx, command.shadow);
2672
2802
  roundRectPath(ctx, rect, command.radius);
2673
2803
  if (command.fill) {
2674
2804
  ctx.fillStyle = command.fill;
@@ -2692,6 +2822,7 @@ function renderDrawCommands(ctx, commands, theme) {
2692
2822
  }
2693
2823
  case "circle": {
2694
2824
  withOpacity(ctx, command.opacity, () => {
2825
+ applyDrawShadow(ctx, command.shadow);
2695
2826
  ctx.beginPath();
2696
2827
  ctx.arc(command.cx, command.cy, command.radius, 0, Math.PI * 2);
2697
2828
  ctx.closePath();
@@ -2726,6 +2857,7 @@ function renderDrawCommands(ctx, commands, theme) {
2726
2857
  case "text": {
2727
2858
  const fontFamily = resolveDrawFont(theme, command.fontFamily);
2728
2859
  withOpacity(ctx, command.opacity, () => {
2860
+ applyDrawShadow(ctx, command.shadow);
2729
2861
  applyFont(ctx, {
2730
2862
  size: command.fontSize,
2731
2863
  weight: command.fontWeight,
@@ -2776,6 +2908,7 @@ function renderDrawCommands(ctx, commands, theme) {
2776
2908
  const to = { x: command.x2, y: command.y2 };
2777
2909
  const lineAngle = angleBetween(from, to);
2778
2910
  withOpacity(ctx, command.opacity, () => {
2911
+ applyDrawShadow(ctx, command.shadow);
2779
2912
  drawLine(ctx, from, to, {
2780
2913
  color: command.color,
2781
2914
  width: command.width,
@@ -2800,6 +2933,7 @@ function renderDrawCommands(ctx, commands, theme) {
2800
2933
  case "bezier": {
2801
2934
  const points = command.points;
2802
2935
  withOpacity(ctx, command.opacity, () => {
2936
+ applyDrawShadow(ctx, command.shadow);
2803
2937
  drawBezier(ctx, points, {
2804
2938
  color: command.color,
2805
2939
  width: command.width,
@@ -2833,6 +2967,7 @@ function renderDrawCommands(ctx, commands, theme) {
2833
2967
  const operations = parseSvgPath(command.d);
2834
2968
  const baseBounds = pathBounds(operations);
2835
2969
  withOpacity(ctx, command.opacity, () => {
2970
+ applyDrawShadow(ctx, command.shadow);
2836
2971
  applySvgOperations(ctx, operations);
2837
2972
  if (command.fill) {
2838
2973
  ctx.fillStyle = command.fill;
@@ -2873,9 +3008,16 @@ function renderDrawCommands(ctx, commands, theme) {
2873
3008
  height: textHeight + command.paddingY * 2
2874
3009
  };
2875
3010
  withOpacity(ctx, command.opacity, () => {
3011
+ applyDrawShadow(ctx, command.shadow);
2876
3012
  roundRectPath(ctx, rect, command.borderRadius);
2877
3013
  ctx.fillStyle = command.background;
2878
3014
  ctx.fill();
3015
+ if (command.shadow) {
3016
+ ctx.shadowColor = "transparent";
3017
+ ctx.shadowBlur = 0;
3018
+ ctx.shadowOffsetX = 0;
3019
+ ctx.shadowOffsetY = 0;
3020
+ }
2879
3021
  applyFont(ctx, {
2880
3022
  size: command.fontSize,
2881
3023
  weight: 600,
@@ -2903,6 +3045,7 @@ function renderDrawCommands(ctx, commands, theme) {
2903
3045
  height: command.height
2904
3046
  };
2905
3047
  withOpacity(ctx, command.opacity, () => {
3048
+ applyDrawShadow(ctx, command.shadow);
2906
3049
  drawGradientRect(ctx, rect, command.gradient, command.radius);
2907
3050
  });
2908
3051
  rendered.push({
@@ -2913,6 +3056,36 @@ function renderDrawCommands(ctx, commands, theme) {
2913
3056
  });
2914
3057
  break;
2915
3058
  }
3059
+ case "grid": {
3060
+ const canvasWidth = ctx.canvas.width;
3061
+ const canvasHeight = ctx.canvas.height;
3062
+ withOpacity(ctx, command.opacity, () => {
3063
+ ctx.strokeStyle = command.color;
3064
+ ctx.lineWidth = command.width;
3065
+ const startX = command.offsetX % command.spacing;
3066
+ for (let x = startX; x <= canvasWidth; x += command.spacing) {
3067
+ ctx.beginPath();
3068
+ ctx.moveTo(x, 0);
3069
+ ctx.lineTo(x, canvasHeight);
3070
+ ctx.stroke();
3071
+ }
3072
+ const startY = command.offsetY % command.spacing;
3073
+ for (let y = startY; y <= canvasHeight; y += command.spacing) {
3074
+ ctx.beginPath();
3075
+ ctx.moveTo(0, y);
3076
+ ctx.lineTo(canvasWidth, y);
3077
+ ctx.stroke();
3078
+ }
3079
+ });
3080
+ rendered.push({
3081
+ id,
3082
+ kind: "draw",
3083
+ bounds: { x: 0, y: 0, width: canvasWidth, height: canvasHeight },
3084
+ foregroundColor: command.color,
3085
+ allowOverlap: true
3086
+ });
3087
+ break;
3088
+ }
2916
3089
  }
2917
3090
  }
2918
3091
  return rendered;
@@ -3194,9 +3367,19 @@ var linearGradientSchema = z2.object({
3194
3367
  }).strict();
3195
3368
  var radialGradientSchema = z2.object({
3196
3369
  type: z2.literal("radial"),
3370
+ cx: z2.number().optional(),
3371
+ cy: z2.number().optional(),
3372
+ innerRadius: z2.number().min(0).optional(),
3373
+ outerRadius: z2.number().min(0).optional(),
3197
3374
  stops: z2.array(gradientStopSchema).min(2)
3198
3375
  }).strict();
3199
3376
  var gradientSchema = z2.discriminatedUnion("type", [linearGradientSchema, radialGradientSchema]);
3377
+ var drawShadowSchema = z2.object({
3378
+ color: colorHexSchema2.default("rgba(0,0,0,0.5)"),
3379
+ blur: z2.number().min(0).max(64).default(10),
3380
+ offsetX: z2.number().default(0),
3381
+ offsetY: z2.number().default(4)
3382
+ }).strict();
3200
3383
  var drawFontFamilySchema = z2.enum(["heading", "body", "mono"]);
3201
3384
  var drawRectSchema = z2.object({
3202
3385
  type: z2.literal("rect"),
@@ -3208,7 +3391,8 @@ var drawRectSchema = z2.object({
3208
3391
  stroke: colorHexSchema2.optional(),
3209
3392
  strokeWidth: z2.number().min(0).max(32).default(0),
3210
3393
  radius: z2.number().min(0).max(256).default(0),
3211
- opacity: z2.number().min(0).max(1).default(1)
3394
+ opacity: z2.number().min(0).max(1).default(1),
3395
+ shadow: drawShadowSchema.optional()
3212
3396
  }).strict();
3213
3397
  var drawCircleSchema = z2.object({
3214
3398
  type: z2.literal("circle"),
@@ -3218,7 +3402,8 @@ var drawCircleSchema = z2.object({
3218
3402
  fill: colorHexSchema2.optional(),
3219
3403
  stroke: colorHexSchema2.optional(),
3220
3404
  strokeWidth: z2.number().min(0).max(32).default(0),
3221
- opacity: z2.number().min(0).max(1).default(1)
3405
+ opacity: z2.number().min(0).max(1).default(1),
3406
+ shadow: drawShadowSchema.optional()
3222
3407
  }).strict();
3223
3408
  var drawTextSchema = z2.object({
3224
3409
  type: z2.literal("text"),
@@ -3233,7 +3418,8 @@ var drawTextSchema = z2.object({
3233
3418
  baseline: z2.enum(["top", "middle", "alphabetic", "bottom"]).default("alphabetic"),
3234
3419
  letterSpacing: z2.number().min(-10).max(50).default(0),
3235
3420
  maxWidth: z2.number().positive().optional(),
3236
- opacity: z2.number().min(0).max(1).default(1)
3421
+ opacity: z2.number().min(0).max(1).default(1),
3422
+ shadow: drawShadowSchema.optional()
3237
3423
  }).strict();
3238
3424
  var drawLineSchema = z2.object({
3239
3425
  type: z2.literal("line"),
@@ -3246,7 +3432,8 @@ var drawLineSchema = z2.object({
3246
3432
  dash: z2.array(z2.number()).max(6).optional(),
3247
3433
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
3248
3434
  arrowSize: z2.number().min(4).max(32).default(10),
3249
- opacity: z2.number().min(0).max(1).default(1)
3435
+ opacity: z2.number().min(0).max(1).default(1),
3436
+ shadow: drawShadowSchema.optional()
3250
3437
  }).strict();
3251
3438
  var drawPointSchema = z2.object({
3252
3439
  x: z2.number(),
@@ -3260,7 +3447,8 @@ var drawBezierSchema = z2.object({
3260
3447
  dash: z2.array(z2.number()).max(6).optional(),
3261
3448
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
3262
3449
  arrowSize: z2.number().min(4).max(32).default(10),
3263
- opacity: z2.number().min(0).max(1).default(1)
3450
+ opacity: z2.number().min(0).max(1).default(1),
3451
+ shadow: drawShadowSchema.optional()
3264
3452
  }).strict();
3265
3453
  var drawPathSchema = z2.object({
3266
3454
  type: z2.literal("path"),
@@ -3268,7 +3456,8 @@ var drawPathSchema = z2.object({
3268
3456
  fill: colorHexSchema2.optional(),
3269
3457
  stroke: colorHexSchema2.optional(),
3270
3458
  strokeWidth: z2.number().min(0).max(32).default(0),
3271
- opacity: z2.number().min(0).max(1).default(1)
3459
+ opacity: z2.number().min(0).max(1).default(1),
3460
+ shadow: drawShadowSchema.optional()
3272
3461
  }).strict();
3273
3462
  var drawBadgeSchema = z2.object({
3274
3463
  type: z2.literal("badge"),
@@ -3282,7 +3471,8 @@ var drawBadgeSchema = z2.object({
3282
3471
  paddingX: z2.number().min(0).max(64).default(10),
3283
3472
  paddingY: z2.number().min(0).max(32).default(4),
3284
3473
  borderRadius: z2.number().min(0).max(64).default(12),
3285
- opacity: z2.number().min(0).max(1).default(1)
3474
+ opacity: z2.number().min(0).max(1).default(1),
3475
+ shadow: drawShadowSchema.optional()
3286
3476
  }).strict();
3287
3477
  var drawGradientRectSchema = z2.object({
3288
3478
  type: z2.literal("gradient-rect"),
@@ -3292,7 +3482,17 @@ var drawGradientRectSchema = z2.object({
3292
3482
  height: z2.number().positive(),
3293
3483
  gradient: gradientSchema,
3294
3484
  radius: z2.number().min(0).max(256).default(0),
3295
- opacity: z2.number().min(0).max(1).default(1)
3485
+ opacity: z2.number().min(0).max(1).default(1),
3486
+ shadow: drawShadowSchema.optional()
3487
+ }).strict();
3488
+ var drawGridSchema = z2.object({
3489
+ type: z2.literal("grid"),
3490
+ spacing: z2.number().min(5).max(200).default(40),
3491
+ color: colorHexSchema2.default("#1E2D4A"),
3492
+ width: z2.number().min(0.1).max(4).default(0.5),
3493
+ opacity: z2.number().min(0).max(1).default(0.2),
3494
+ offsetX: z2.number().default(0),
3495
+ offsetY: z2.number().default(0)
3296
3496
  }).strict();
3297
3497
  var drawCommandSchema = z2.discriminatedUnion("type", [
3298
3498
  drawRectSchema,
@@ -3302,7 +3502,8 @@ var drawCommandSchema = z2.discriminatedUnion("type", [
3302
3502
  drawBezierSchema,
3303
3503
  drawPathSchema,
3304
3504
  drawBadgeSchema,
3305
- drawGradientRectSchema
3505
+ drawGradientRectSchema,
3506
+ drawGridSchema
3306
3507
  ]);
3307
3508
  var defaultCanvas = {
3308
3509
  width: 1200,
@@ -3408,6 +3609,13 @@ var flowNodeElementSchema = z2.object({
3408
3609
  badgePosition: z2.enum(["top", "inside-top"]).default("inside-top"),
3409
3610
  shadow: flowNodeShadowSchema.optional()
3410
3611
  }).strict();
3612
+ var anchorHintSchema = z2.union([
3613
+ z2.enum(["top", "bottom", "left", "right", "center"]),
3614
+ z2.object({
3615
+ x: z2.number().min(-1).max(1),
3616
+ y: z2.number().min(-1).max(1)
3617
+ }).strict()
3618
+ ]);
3411
3619
  var connectionElementSchema = z2.object({
3412
3620
  type: z2.literal("connection"),
3413
3621
  from: z2.string().min(1).max(120),
@@ -3421,9 +3629,12 @@ var connectionElementSchema = z2.object({
3421
3629
  width: z2.number().min(0.5).max(10).optional(),
3422
3630
  strokeWidth: z2.number().min(0.5).max(10).default(2),
3423
3631
  arrowSize: z2.number().min(4).max(32).optional(),
3632
+ arrowPlacement: z2.enum(["endpoint", "boundary"]).default("endpoint"),
3424
3633
  opacity: z2.number().min(0).max(1).default(1),
3425
3634
  routing: z2.enum(["auto", "orthogonal", "curve", "arc"]).default("auto"),
3426
- tension: z2.number().min(0.1).max(0.8).default(0.35)
3635
+ tension: z2.number().min(0.1).max(0.8).default(0.35),
3636
+ fromAnchor: anchorHintSchema.optional(),
3637
+ toAnchor: anchorHintSchema.optional()
3427
3638
  }).strict();
3428
3639
  var codeBlockStyleSchema = z2.object({
3429
3640
  paddingVertical: z2.number().min(0).max(128).default(56),