@spectratools/graphic-designer-cli 0.11.0 → 0.12.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/renderer.js CHANGED
@@ -1005,6 +1005,35 @@ async function computeElkLayout(elements, config, safeFrame) {
1005
1005
  };
1006
1006
  }
1007
1007
 
1008
+ // src/layout/ellipse.ts
1009
+ function clampDimension(estimated, max) {
1010
+ return Math.max(1, Math.min(max, Math.floor(estimated)));
1011
+ }
1012
+ function computeEllipseLayout(elements, config, safeFrame) {
1013
+ const placeable = elements.filter((element) => element.type !== "connection");
1014
+ const positions = /* @__PURE__ */ new Map();
1015
+ if (placeable.length === 0) {
1016
+ return { positions };
1017
+ }
1018
+ const cx = config.cx ?? safeFrame.x + safeFrame.width / 2;
1019
+ const cy = config.cy ?? safeFrame.y + safeFrame.height / 2;
1020
+ const stepDegrees = 360 / placeable.length;
1021
+ for (const [index, element] of placeable.entries()) {
1022
+ const angleRadians = (config.startAngle + index * stepDegrees) * Math.PI / 180;
1023
+ const centerX = cx + config.rx * Math.cos(angleRadians);
1024
+ const centerY = cy + config.ry * Math.sin(angleRadians);
1025
+ const width = clampDimension(estimateElementWidth(element), safeFrame.width);
1026
+ const height = clampDimension(estimateElementHeight(element), safeFrame.height);
1027
+ positions.set(element.id, {
1028
+ x: Math.round(centerX - width / 2),
1029
+ y: Math.round(centerY - height / 2),
1030
+ width,
1031
+ height
1032
+ });
1033
+ }
1034
+ return { positions };
1035
+ }
1036
+
1008
1037
  // src/layout/grid.ts
1009
1038
  function computeGridLayout(elements, config, safeFrame) {
1010
1039
  const placeable = elements.filter((element) => element.type !== "connection");
@@ -1100,6 +1129,8 @@ async function computeLayout(elements, layout, safeFrame) {
1100
1129
  return computeGridLayout(elements, layout, safeFrame);
1101
1130
  case "stack":
1102
1131
  return computeStackLayout(elements, layout, safeFrame);
1132
+ case "ellipse":
1133
+ return computeEllipseLayout(elements, layout, safeFrame);
1103
1134
  case "manual":
1104
1135
  return computeManualLayout(elements, layout, safeFrame);
1105
1136
  default:
@@ -2692,6 +2723,24 @@ function fromPoints(points) {
2692
2723
  function resolveDrawFont(theme, family) {
2693
2724
  return resolveFont(theme.fonts[family], family);
2694
2725
  }
2726
+ function createDrawStrokeGradient(ctx, start, end, strokeGradient) {
2727
+ const gradient = ctx.createLinearGradient(start.x, start.y, end.x, end.y);
2728
+ gradient.addColorStop(0, strokeGradient.from);
2729
+ gradient.addColorStop(1, strokeGradient.to);
2730
+ return gradient;
2731
+ }
2732
+ function resolveDrawStroke(ctx, start, end, color, strokeGradient) {
2733
+ if (!strokeGradient) {
2734
+ return color;
2735
+ }
2736
+ return createDrawStrokeGradient(ctx, start, end, strokeGradient);
2737
+ }
2738
+ function resolveArrowFill(color, strokeGradient, position) {
2739
+ if (!strokeGradient) {
2740
+ return color;
2741
+ }
2742
+ return position === "start" ? strokeGradient.from : strokeGradient.to;
2743
+ }
2695
2744
  function measureSpacedTextWidth(ctx, text, letterSpacing) {
2696
2745
  const chars = [...text];
2697
2746
  if (chars.length === 0) {
@@ -2970,18 +3019,31 @@ function renderDrawCommands(ctx, commands, theme) {
2970
3019
  const from = { x: command.x1, y: command.y1 };
2971
3020
  const to = { x: command.x2, y: command.y2 };
2972
3021
  const lineAngle = angleBetween(from, to);
3022
+ const stroke = resolveDrawStroke(ctx, from, to, command.color, command.strokeGradient);
2973
3023
  withOpacity(ctx, command.opacity, () => {
2974
3024
  applyDrawShadow(ctx, command.shadow);
2975
3025
  drawLine(ctx, from, to, {
2976
- color: command.color,
3026
+ color: stroke,
2977
3027
  width: command.width,
2978
3028
  ...command.dash ? { dash: command.dash } : {}
2979
3029
  });
2980
3030
  if (command.arrow === "end" || command.arrow === "both") {
2981
- drawArrowhead(ctx, to, lineAngle, command.arrowSize, command.color);
3031
+ drawArrowhead(
3032
+ ctx,
3033
+ to,
3034
+ lineAngle,
3035
+ command.arrowSize,
3036
+ resolveArrowFill(command.color, command.strokeGradient, "end")
3037
+ );
2982
3038
  }
2983
3039
  if (command.arrow === "start" || command.arrow === "both") {
2984
- drawArrowhead(ctx, from, lineAngle + Math.PI, command.arrowSize, command.color);
3040
+ drawArrowhead(
3041
+ ctx,
3042
+ from,
3043
+ lineAngle + Math.PI,
3044
+ command.arrowSize,
3045
+ resolveArrowFill(command.color, command.strokeGradient, "start")
3046
+ );
2985
3047
  }
2986
3048
  });
2987
3049
  const arrowPadding = command.arrow === "none" ? 0 : command.arrowSize;
@@ -2989,7 +3051,7 @@ function renderDrawCommands(ctx, commands, theme) {
2989
3051
  id,
2990
3052
  kind: "draw",
2991
3053
  bounds: expandRect(fromPoints([from, to]), Math.max(command.width / 2, arrowPadding)),
2992
- foregroundColor: command.color
3054
+ foregroundColor: command.strokeGradient?.from ?? command.color
2993
3055
  });
2994
3056
  break;
2995
3057
  }
@@ -3023,10 +3085,17 @@ function renderDrawCommands(ctx, commands, theme) {
3023
3085
  }
3024
3086
  case "bezier": {
3025
3087
  const points = command.points;
3088
+ const stroke = resolveDrawStroke(
3089
+ ctx,
3090
+ points[0],
3091
+ points[points.length - 1],
3092
+ command.color,
3093
+ command.strokeGradient
3094
+ );
3026
3095
  withOpacity(ctx, command.opacity, () => {
3027
3096
  applyDrawShadow(ctx, command.shadow);
3028
3097
  drawBezier(ctx, points, {
3029
- color: command.color,
3098
+ color: stroke,
3030
3099
  width: command.width,
3031
3100
  ...command.dash ? { dash: command.dash } : {}
3032
3101
  });
@@ -3038,11 +3107,17 @@ function renderDrawCommands(ctx, commands, theme) {
3038
3107
  points[points.length - 1],
3039
3108
  endAngle,
3040
3109
  command.arrowSize,
3041
- command.color
3110
+ resolveArrowFill(command.color, command.strokeGradient, "end")
3042
3111
  );
3043
3112
  }
3044
3113
  if (command.arrow === "start" || command.arrow === "both") {
3045
- drawArrowhead(ctx, points[0], startAngle + Math.PI, command.arrowSize, command.color);
3114
+ drawArrowhead(
3115
+ ctx,
3116
+ points[0],
3117
+ startAngle + Math.PI,
3118
+ command.arrowSize,
3119
+ resolveArrowFill(command.color, command.strokeGradient, "start")
3120
+ );
3046
3121
  }
3047
3122
  });
3048
3123
  const arrowPadding = command.arrow === "none" ? 0 : command.arrowSize;
@@ -3050,7 +3125,7 @@ function renderDrawCommands(ctx, commands, theme) {
3050
3125
  id,
3051
3126
  kind: "draw",
3052
3127
  bounds: expandRect(fromPoints(points), Math.max(command.width / 2, arrowPadding)),
3053
- foregroundColor: command.color
3128
+ foregroundColor: command.strokeGradient?.from ?? command.color
3054
3129
  });
3055
3130
  break;
3056
3131
  }
@@ -3550,6 +3625,10 @@ var drawShadowSchema = z2.object({
3550
3625
  offsetY: z2.number().default(4)
3551
3626
  }).strict();
3552
3627
  var drawFontFamilySchema = z2.enum(["heading", "body", "mono"]);
3628
+ var strokeGradientSchema = z2.object({
3629
+ from: colorHexSchema2,
3630
+ to: colorHexSchema2
3631
+ }).strict();
3553
3632
  var drawRectSchema = z2.object({
3554
3633
  type: z2.literal("rect"),
3555
3634
  x: z2.number(),
@@ -3597,6 +3676,7 @@ var drawLineSchema = z2.object({
3597
3676
  x2: z2.number(),
3598
3677
  y2: z2.number(),
3599
3678
  color: colorHexSchema2.default("#FFFFFF"),
3679
+ strokeGradient: strokeGradientSchema.optional(),
3600
3680
  width: z2.number().min(0.5).max(32).default(2),
3601
3681
  dash: z2.array(z2.number()).max(6).optional(),
3602
3682
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
@@ -3627,6 +3707,7 @@ var drawBezierSchema = z2.object({
3627
3707
  type: z2.literal("bezier"),
3628
3708
  points: z2.array(drawPointSchema).min(2).max(20),
3629
3709
  color: colorHexSchema2.default("#FFFFFF"),
3710
+ strokeGradient: strokeGradientSchema.optional(),
3630
3711
  width: z2.number().min(0.5).max(32).default(2),
3631
3712
  dash: z2.array(z2.number()).max(6).optional(),
3632
3713
  arrow: z2.enum(["none", "end", "start", "both"]).default("none"),
@@ -3973,6 +4054,20 @@ var stackLayoutConfigSchema = z2.object({
3973
4054
  /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
3974
4055
  ellipseRy: z2.number().positive().optional()
3975
4056
  }).strict();
4057
+ var ellipseLayoutConfigSchema = z2.object({
4058
+ mode: z2.literal("ellipse"),
4059
+ cx: z2.number().optional(),
4060
+ cy: z2.number().optional(),
4061
+ rx: z2.number().positive(),
4062
+ ry: z2.number().positive(),
4063
+ startAngle: z2.number().default(-90),
4064
+ /** Explicit center used by curve/arc connection routing. */
4065
+ diagramCenter: diagramCenterSchema.optional(),
4066
+ /** Horizontal radius for shared ellipse used by curveMode: 'ellipse'. */
4067
+ ellipseRx: z2.number().positive().optional(),
4068
+ /** Vertical radius for shared ellipse used by curveMode: 'ellipse'. */
4069
+ ellipseRy: z2.number().positive().optional()
4070
+ }).strict();
3976
4071
  var manualPositionSchema = z2.object({
3977
4072
  x: z2.number().int(),
3978
4073
  y: z2.number().int(),
@@ -3993,6 +4088,7 @@ var layoutConfigSchema = z2.discriminatedUnion("mode", [
3993
4088
  autoLayoutConfigSchema,
3994
4089
  gridLayoutConfigSchema,
3995
4090
  stackLayoutConfigSchema,
4091
+ ellipseLayoutConfigSchema,
3996
4092
  manualLayoutConfigSchema
3997
4093
  ]);
3998
4094
  var constraintsSchema = z2.object({