@diagrammo/dgmo 0.3.0 → 0.3.2

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.d.cts CHANGED
@@ -334,7 +334,7 @@ declare function buildEChartsOption(parsed: ParsedEChart, palette: PaletteColors
334
334
  * Converts a ParsedChart into an EChartsOption.
335
335
  * Renders standard chart types (bar, line, pie, etc.) with ECharts.
336
336
  */
337
- declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean): EChartsOption;
337
+ declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean, chartWidth?: number): EChartsOption;
338
338
  /**
339
339
  * Renders an ECharts diagram to SVG using server-side rendering.
340
340
  * Mirrors the `renderD3ForExport` API — returns an SVG string or empty string on failure.
@@ -579,6 +579,8 @@ interface SequenceMessage {
579
579
  lineNumber: number;
580
580
  async?: boolean;
581
581
  bidirectional?: boolean;
582
+ /** Standalone return — the message itself IS a return (dashed arrow, no call). */
583
+ standaloneReturn?: boolean;
582
584
  }
583
585
  /**
584
586
  * A conditional or loop block in the sequence diagram.
package/dist/index.d.ts CHANGED
@@ -334,7 +334,7 @@ declare function buildEChartsOption(parsed: ParsedEChart, palette: PaletteColors
334
334
  * Converts a ParsedChart into an EChartsOption.
335
335
  * Renders standard chart types (bar, line, pie, etc.) with ECharts.
336
336
  */
337
- declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean): EChartsOption;
337
+ declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean, chartWidth?: number): EChartsOption;
338
338
  /**
339
339
  * Renders an ECharts diagram to SVG using server-side rendering.
340
340
  * Mirrors the `renderD3ForExport` API — returns an SVG string or empty string on failure.
@@ -579,6 +579,8 @@ interface SequenceMessage {
579
579
  lineNumber: number;
580
580
  async?: boolean;
581
581
  bidirectional?: boolean;
582
+ /** Standalone return — the message itself IS a return (dashed arrow, no call). */
583
+ standaloneReturn?: boolean;
582
584
  }
583
585
  /**
584
586
  * A conditional or loop block in the sequence diagram.
package/dist/index.js CHANGED
@@ -1253,6 +1253,67 @@ var init_palettes = __esm({
1253
1253
  }
1254
1254
  });
1255
1255
 
1256
+ // src/utils/parsing.ts
1257
+ function measureIndent(line7) {
1258
+ let indent = 0;
1259
+ for (const ch of line7) {
1260
+ if (ch === " ") indent++;
1261
+ else if (ch === " ") indent += 4;
1262
+ else break;
1263
+ }
1264
+ return indent;
1265
+ }
1266
+ function extractColor(label, palette) {
1267
+ const m = label.match(COLOR_SUFFIX_RE);
1268
+ if (!m) return { label };
1269
+ const colorName = m[1].trim();
1270
+ return {
1271
+ label: label.substring(0, m.index).trim(),
1272
+ color: resolveColor(colorName, palette)
1273
+ };
1274
+ }
1275
+ function collectIndentedValues(lines, startIndex) {
1276
+ const values = [];
1277
+ let j = startIndex + 1;
1278
+ for (; j < lines.length; j++) {
1279
+ const raw = lines[j];
1280
+ const trimmed = raw.trim();
1281
+ if (!trimmed) continue;
1282
+ if (trimmed.startsWith("//")) continue;
1283
+ if (raw[0] !== " " && raw[0] !== " ") break;
1284
+ values.push(trimmed.replace(/,\s*$/, ""));
1285
+ }
1286
+ return { values, newIndex: j - 1 };
1287
+ }
1288
+ function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map()) {
1289
+ const metadata = {};
1290
+ for (let j = 1; j < segments.length; j++) {
1291
+ for (const part of segments[j].split(",")) {
1292
+ const trimmedPart = part.trim();
1293
+ if (!trimmedPart) continue;
1294
+ const colonIdx = trimmedPart.indexOf(":");
1295
+ if (colonIdx > 0) {
1296
+ const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
1297
+ const key = aliasMap.get(rawKey) ?? rawKey;
1298
+ const value = trimmedPart.substring(colonIdx + 1).trim();
1299
+ metadata[key] = value;
1300
+ }
1301
+ }
1302
+ }
1303
+ return metadata;
1304
+ }
1305
+ var COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_RE;
1306
+ var init_parsing = __esm({
1307
+ "src/utils/parsing.ts"() {
1308
+ "use strict";
1309
+ init_colors();
1310
+ COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1311
+ CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
1312
+ TITLE_RE = /^title\s*:\s*(.+)/i;
1313
+ OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
1314
+ }
1315
+ });
1316
+
1256
1317
  // src/sequence/participant-inference.ts
1257
1318
  function inferParticipantType(name) {
1258
1319
  for (const rule of PARTICIPANT_RULES) {
@@ -1569,54 +1630,6 @@ var init_arrows = __esm({
1569
1630
  }
1570
1631
  });
1571
1632
 
1572
- // src/utils/parsing.ts
1573
- function measureIndent(line7) {
1574
- let indent = 0;
1575
- for (const ch of line7) {
1576
- if (ch === " ") indent++;
1577
- else if (ch === " ") indent += 4;
1578
- else break;
1579
- }
1580
- return indent;
1581
- }
1582
- function extractColor(label, palette) {
1583
- const m = label.match(COLOR_SUFFIX_RE);
1584
- if (!m) return { label };
1585
- const colorName = m[1].trim();
1586
- return {
1587
- label: label.substring(0, m.index).trim(),
1588
- color: resolveColor(colorName, palette)
1589
- };
1590
- }
1591
- function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map()) {
1592
- const metadata = {};
1593
- for (let j = 1; j < segments.length; j++) {
1594
- for (const part of segments[j].split(",")) {
1595
- const trimmedPart = part.trim();
1596
- if (!trimmedPart) continue;
1597
- const colonIdx = trimmedPart.indexOf(":");
1598
- if (colonIdx > 0) {
1599
- const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
1600
- const key = aliasMap.get(rawKey) ?? rawKey;
1601
- const value = trimmedPart.substring(colonIdx + 1).trim();
1602
- metadata[key] = value;
1603
- }
1604
- }
1605
- }
1606
- return metadata;
1607
- }
1608
- var COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_RE;
1609
- var init_parsing = __esm({
1610
- "src/utils/parsing.ts"() {
1611
- "use strict";
1612
- init_colors();
1613
- COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
1614
- CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
1615
- TITLE_RE = /^title\s*:\s*(.+)/i;
1616
- OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
1617
- }
1618
- });
1619
-
1620
1633
  // src/sequence/parser.ts
1621
1634
  var parser_exports = {};
1622
1635
  __export(parser_exports, {
@@ -1637,6 +1650,13 @@ function isSequenceNote(el) {
1637
1650
  }
1638
1651
  function parseReturnLabel(rawLabel) {
1639
1652
  if (!rawLabel) return { label: "" };
1653
+ const standaloneMatch = rawLabel.match(/^<-\s*(.*)$/);
1654
+ if (standaloneMatch) {
1655
+ return {
1656
+ label: standaloneMatch[1].trim(),
1657
+ standaloneReturn: true
1658
+ };
1659
+ }
1640
1660
  const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
1641
1661
  if (arrowReturn) {
1642
1662
  return { label: arrowReturn[1].trim(), returnLabel: arrowReturn[2].trim() };
@@ -1974,14 +1994,15 @@ function parseSequenceDgmo(content) {
1974
1994
  const to = arrowMatch[2];
1975
1995
  lastMsgFrom = from;
1976
1996
  const rawLabel = arrowMatch[3]?.trim() || "";
1977
- const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
1997
+ const { label, returnLabel, standaloneReturn } = isAsync ? { label: rawLabel, returnLabel: void 0, standaloneReturn: void 0 } : parseReturnLabel(rawLabel);
1978
1998
  const msg = {
1979
1999
  from,
1980
2000
  to,
1981
2001
  label,
1982
2002
  returnLabel,
1983
2003
  lineNumber,
1984
- ...isAsync ? { async: true } : {}
2004
+ ...isAsync ? { async: true } : {},
2005
+ ...standaloneReturn ? { standaloneReturn: true } : {}
1985
2006
  };
1986
2007
  result.messages.push(msg);
1987
2008
  currentContainer().push(msg);
@@ -3121,8 +3142,16 @@ function parseChart(content, palette) {
3121
3142
  continue;
3122
3143
  }
3123
3144
  if (key === "series") {
3124
- result.series = value;
3125
- const rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
3145
+ let rawNames;
3146
+ if (value) {
3147
+ result.series = value;
3148
+ rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
3149
+ } else {
3150
+ const collected = collectIndentedValues(lines, i);
3151
+ i = collected.newIndex;
3152
+ rawNames = collected.values;
3153
+ result.series = rawNames.join(", ");
3154
+ }
3126
3155
  const names = [];
3127
3156
  const nameColors = [];
3128
3157
  for (const raw of rawNames) {
@@ -3201,6 +3230,7 @@ var init_chart = __esm({
3201
3230
  "use strict";
3202
3231
  init_colors();
3203
3232
  init_diagnostics();
3233
+ init_parsing();
3204
3234
  VALID_TYPES = /* @__PURE__ */ new Set([
3205
3235
  "bar",
3206
3236
  "line",
@@ -3277,8 +3307,16 @@ function parseEChart(content, palette) {
3277
3307
  continue;
3278
3308
  }
3279
3309
  if (key === "series") {
3280
- result.series = value;
3281
- const rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
3310
+ let rawNames;
3311
+ if (value) {
3312
+ result.series = value;
3313
+ rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
3314
+ } else {
3315
+ const collected = collectIndentedValues(lines, i);
3316
+ i = collected.newIndex;
3317
+ rawNames = collected.values;
3318
+ result.series = rawNames.join(", ");
3319
+ }
3282
3320
  const names = [];
3283
3321
  const nameColors = [];
3284
3322
  for (const raw of rawNames) {
@@ -3314,11 +3352,23 @@ function parseEChart(content, palette) {
3314
3352
  continue;
3315
3353
  }
3316
3354
  if (key === "columns") {
3317
- result.columns = value.split(",").map((s) => s.trim());
3355
+ if (value) {
3356
+ result.columns = value.split(",").map((s) => s.trim());
3357
+ } else {
3358
+ const collected = collectIndentedValues(lines, i);
3359
+ i = collected.newIndex;
3360
+ result.columns = collected.values;
3361
+ }
3318
3362
  continue;
3319
3363
  }
3320
3364
  if (key === "rows") {
3321
- result.rows = value.split(",").map((s) => s.trim());
3365
+ if (value) {
3366
+ result.rows = value.split(",").map((s) => s.trim());
3367
+ } else {
3368
+ const collected = collectIndentedValues(lines, i);
3369
+ i = collected.newIndex;
3370
+ result.rows = collected.values;
3371
+ }
3322
3372
  continue;
3323
3373
  }
3324
3374
  if (key === "x") {
@@ -4146,19 +4196,36 @@ function resolveAxisLabels(parsed) {
4146
4196
  yLabel: parsed.ylabel ?? (isHorizontal ? void 0 : parsed.label)
4147
4197
  };
4148
4198
  }
4149
- function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacity, label, data, nameGapOverride) {
4199
+ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacity, label, data, nameGapOverride, chartWidthHint) {
4150
4200
  const defaultGap = type === "value" ? 75 : 40;
4201
+ let catFontSize = 16;
4202
+ let catLabelExtras = {};
4203
+ if (type === "category" && data && data.length > 0) {
4204
+ const maxLabelLen = Math.max(...data.map((l) => l.length));
4205
+ const count = data.length;
4206
+ if (count > 10 || maxLabelLen > 20) catFontSize = 10;
4207
+ else if (count > 5 || maxLabelLen > 14) catFontSize = 11;
4208
+ else if (maxLabelLen > 8) catFontSize = 12;
4209
+ if (chartWidthHint && count > 0) {
4210
+ const availPerLabel = Math.floor(chartWidthHint * 0.85 / count);
4211
+ catLabelExtras = {
4212
+ width: availPerLabel,
4213
+ overflow: "break"
4214
+ };
4215
+ }
4216
+ }
4151
4217
  return {
4152
4218
  type,
4153
4219
  ...data && { data },
4154
4220
  axisLine: { lineStyle: { color: axisLineColor } },
4155
4221
  axisLabel: {
4156
4222
  color: textColor,
4157
- fontSize: type === "category" && data ? data.length > 10 ? 11 : data.length > 5 ? 12 : 16 : 16,
4223
+ fontSize: type === "category" && data ? catFontSize : 16,
4158
4224
  fontFamily: FONT_FAMILY,
4159
4225
  ...type === "category" && {
4160
4226
  interval: 0,
4161
- formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2").replace(/ /g, "\n")
4227
+ formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2"),
4228
+ ...catLabelExtras
4162
4229
  }
4163
4230
  },
4164
4231
  splitLine: { lineStyle: { color: splitLineColor, opacity: gridOpacity } },
@@ -4170,7 +4237,7 @@ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacit
4170
4237
  }
4171
4238
  };
4172
4239
  }
4173
- function buildEChartsOptionFromChart(parsed, palette, isDark) {
4240
+ function buildEChartsOptionFromChart(parsed, palette, isDark, chartWidth) {
4174
4241
  if (parsed.error) return {};
4175
4242
  const textColor = palette.text;
4176
4243
  const axisLineColor = palette.border;
@@ -4195,13 +4262,13 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
4195
4262
  };
4196
4263
  switch (parsed.type) {
4197
4264
  case "bar":
4198
- return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme);
4265
+ return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth);
4199
4266
  case "bar-stacked":
4200
- return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme);
4267
+ return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth);
4201
4268
  case "line":
4202
- return parsed.seriesNames ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme);
4269
+ return parsed.seriesNames ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth);
4203
4270
  case "area":
4204
- return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme);
4271
+ return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth);
4205
4272
  case "pie":
4206
4273
  return buildPieOption(parsed, textColor, getSegmentColors(palette, parsed.data.length), titleConfig, tooltipTheme, false);
4207
4274
  case "doughnut":
@@ -4212,7 +4279,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
4212
4279
  return buildPolarAreaOption(parsed, textColor, getSegmentColors(palette, parsed.data.length), titleConfig, tooltipTheme);
4213
4280
  }
4214
4281
  }
4215
- function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) {
4282
+ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) {
4216
4283
  const { xLabel, yLabel } = resolveAxisLabels(parsed);
4217
4284
  const isHorizontal = parsed.orientation === "horizontal";
4218
4285
  const labels = parsed.data.map((d) => d.label);
@@ -4221,7 +4288,7 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
4221
4288
  itemStyle: { color: d.color ?? colors[i % colors.length] }
4222
4289
  }));
4223
4290
  const hCatGap = isHorizontal && yLabel ? Math.max(40, Math.max(...labels.map((l) => l.length)) * 8 + 16) : void 0;
4224
- const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap);
4291
+ const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap, !isHorizontal ? chartWidth : void 0);
4225
4292
  const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
4226
4293
  return {
4227
4294
  backgroundColor: "transparent",
@@ -4253,7 +4320,7 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
4253
4320
  ]
4254
4321
  };
4255
4322
  }
4256
- function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme) {
4323
+ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth) {
4257
4324
  const { xLabel, yLabel } = resolveAxisLabels(parsed);
4258
4325
  const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
4259
4326
  const labels = parsed.data.map((d) => d.label);
@@ -4274,7 +4341,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
4274
4341
  top: parsed.title ? "15%" : "5%",
4275
4342
  containLabel: true
4276
4343
  },
4277
- xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
4344
+ xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, void 0, chartWidth),
4278
4345
  yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
4279
4346
  series: [
4280
4347
  {
@@ -4292,7 +4359,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
4292
4359
  ]
4293
4360
  };
4294
4361
  }
4295
- function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) {
4362
+ function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) {
4296
4363
  const { xLabel, yLabel } = resolveAxisLabels(parsed);
4297
4364
  const seriesNames = parsed.seriesNames ?? [];
4298
4365
  const labels = parsed.data.map((d) => d.label);
@@ -4336,12 +4403,12 @@ function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor,
4336
4403
  top: parsed.title ? "15%" : "5%",
4337
4404
  containLabel: true
4338
4405
  },
4339
- xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
4406
+ xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, void 0, chartWidth),
4340
4407
  yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
4341
4408
  series
4342
4409
  };
4343
4410
  }
4344
- function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme) {
4411
+ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth) {
4345
4412
  const { xLabel, yLabel } = resolveAxisLabels(parsed);
4346
4413
  const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
4347
4414
  const labels = parsed.data.map((d) => d.label);
@@ -4362,7 +4429,7 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
4362
4429
  top: parsed.title ? "15%" : "5%",
4363
4430
  containLabel: true
4364
4431
  },
4365
- xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
4432
+ xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, void 0, chartWidth),
4366
4433
  yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
4367
4434
  series: [
4368
4435
  {
@@ -4522,7 +4589,7 @@ function buildPolarAreaOption(parsed, textColor, colors, titleConfig, tooltipThe
4522
4589
  ]
4523
4590
  };
4524
4591
  }
4525
- function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) {
4592
+ function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) {
4526
4593
  const { xLabel, yLabel } = resolveAxisLabels(parsed);
4527
4594
  const isHorizontal = parsed.orientation === "horizontal";
4528
4595
  const seriesNames = parsed.seriesNames ?? [];
@@ -4554,8 +4621,9 @@ function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor,
4554
4621
  };
4555
4622
  });
4556
4623
  const hCatGap = isHorizontal && yLabel ? Math.max(40, Math.max(...labels.map((l) => l.length)) * 8 + 16) : void 0;
4557
- const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap);
4558
- const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
4624
+ const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap, !isHorizontal ? chartWidth : void 0);
4625
+ const hValueGap = isHorizontal && xLabel ? 40 : void 0;
4626
+ const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel, void 0, hValueGap);
4559
4627
  return {
4560
4628
  backgroundColor: "transparent",
4561
4629
  animation: false,
@@ -4592,7 +4660,7 @@ async function renderEChartsForExport(content, theme, palette, options) {
4592
4660
  if (chartType && STANDARD_CHART_TYPES.has(chartType)) {
4593
4661
  const parsed = parseChart(content, effectivePalette);
4594
4662
  if (parsed.error) return "";
4595
- option = buildEChartsOptionFromChart(parsed, effectivePalette, isDark);
4663
+ option = buildEChartsOptionFromChart(parsed, effectivePalette, isDark, ECHART_EXPORT_WIDTH);
4596
4664
  } else {
4597
4665
  const parsed = parseEChart(content, effectivePalette);
4598
4666
  if (parsed.error) return "";
@@ -4633,6 +4701,7 @@ var init_echarts = __esm({
4633
4701
  init_palettes();
4634
4702
  init_chart();
4635
4703
  init_diagnostics();
4704
+ init_parsing();
4636
4705
  ECHART_EXPORT_WIDTH = 1200;
4637
4706
  ECHART_EXPORT_HEIGHT = 800;
4638
4707
  STANDARD_CHART_TYPES = /* @__PURE__ */ new Set([
@@ -12119,6 +12188,22 @@ function buildRenderSequence(messages) {
12119
12188
  messageIndex: top.messageIndex
12120
12189
  });
12121
12190
  }
12191
+ if (msg.standaloneReturn) {
12192
+ for (let si = stack.length - 1; si >= 0; si--) {
12193
+ if (stack[si].from === msg.to && stack[si].to === msg.from) {
12194
+ stack.splice(si, 1);
12195
+ break;
12196
+ }
12197
+ }
12198
+ steps.push({
12199
+ type: "return",
12200
+ from: msg.from,
12201
+ to: msg.to,
12202
+ label: msg.label,
12203
+ messageIndex: mi
12204
+ });
12205
+ continue;
12206
+ }
12122
12207
  steps.push({
12123
12208
  type: "call",
12124
12209
  from: msg.from,
@@ -13404,18 +13489,34 @@ function parseD3(content, palette) {
13404
13489
  }
13405
13490
  }
13406
13491
  if (result.type === "quadrant") {
13407
- const xAxisMatch = line7.match(/^x-axis\s*:\s*(.+)/i);
13492
+ const xAxisMatch = line7.match(/^x-axis\s*:\s*(.*)/i);
13408
13493
  if (xAxisMatch) {
13409
- const parts = xAxisMatch[1].split(",").map((s) => s.trim());
13494
+ const val = xAxisMatch[1].trim();
13495
+ let parts;
13496
+ if (val) {
13497
+ parts = val.split(",").map((s) => s.trim());
13498
+ } else {
13499
+ const collected = collectIndentedValues(lines, i);
13500
+ i = collected.newIndex;
13501
+ parts = collected.values;
13502
+ }
13410
13503
  if (parts.length >= 2) {
13411
13504
  result.quadrantXAxis = [parts[0], parts[1]];
13412
13505
  result.quadrantXAxisLineNumber = lineNumber;
13413
13506
  }
13414
13507
  continue;
13415
13508
  }
13416
- const yAxisMatch = line7.match(/^y-axis\s*:\s*(.+)/i);
13509
+ const yAxisMatch = line7.match(/^y-axis\s*:\s*(.*)/i);
13417
13510
  if (yAxisMatch) {
13418
- const parts = yAxisMatch[1].split(",").map((s) => s.trim());
13511
+ const val = yAxisMatch[1].trim();
13512
+ let parts;
13513
+ if (val) {
13514
+ parts = val.split(",").map((s) => s.trim());
13515
+ } else {
13516
+ const collected = collectIndentedValues(lines, i);
13517
+ i = collected.newIndex;
13518
+ parts = collected.values;
13519
+ }
13419
13520
  if (parts.length >= 2) {
13420
13521
  result.quadrantYAxis = [parts[0], parts[1]];
13421
13522
  result.quadrantYAxisLineNumber = lineNumber;
@@ -13697,6 +13798,19 @@ function tokenizeFreeformText(text) {
13697
13798
  }
13698
13799
  return Array.from(counts.entries()).map(([text2, count]) => ({ text: text2, weight: count, lineNumber: 0 })).sort((a, b) => b.weight - a.weight);
13699
13800
  }
13801
+ function resolveVerticalCollisions(items, minGap) {
13802
+ if (items.length === 0) return [];
13803
+ const sorted = items.map((it, i) => ({ ...it, idx: i })).sort((a, b) => a.naturalY - b.naturalY);
13804
+ const adjustedY = new Array(items.length);
13805
+ let prevBottom = -Infinity;
13806
+ for (const item of sorted) {
13807
+ const halfH = item.height / 2;
13808
+ const top = Math.max(item.naturalY - halfH, prevBottom + minGap);
13809
+ adjustedY[item.idx] = top + halfH;
13810
+ prevBottom = top + item.height;
13811
+ }
13812
+ return adjustedY;
13813
+ }
13700
13814
  function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
13701
13815
  d3Selection9.select(container).selectAll(":not([data-d3-tooltip])").remove();
13702
13816
  const { periods, data, title } = parsed;
@@ -13747,25 +13861,80 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
13747
13861
  g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
13748
13862
  }
13749
13863
  const lineGen = d3Shape6.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
13750
- data.forEach((item, idx) => {
13864
+ const seriesInfo = data.map((item, idx) => {
13751
13865
  const color = item.color ?? colors[idx % colors.length];
13752
- const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
13753
13866
  const firstVal = item.values[0];
13754
13867
  const lastVal = item.values[item.values.length - 1];
13755
13868
  const absChange = lastVal - firstVal;
13756
13869
  const pctChange = firstVal !== 0 ? absChange / firstVal * 100 : null;
13757
13870
  const sign = absChange > 0 ? "+" : "";
13758
- const pctPart = pctChange !== null ? ` (${sign}${pctChange.toFixed(1)}%)` : "";
13759
- const tipLines = [`${sign}${absChange}`];
13871
+ const tipLines = [`${sign}${parseFloat(absChange.toFixed(2))}`];
13760
13872
  if (pctChange !== null) tipLines.push(`${sign}${pctChange.toFixed(1)}%`);
13761
13873
  const tipHtml = tipLines.join("<br>");
13874
+ const lastX = xScale(periods[periods.length - 1]);
13875
+ const labelText = `${lastVal} \u2014 ${item.label}`;
13876
+ const availableWidth = rightMargin - 15;
13877
+ const maxChars = Math.floor(availableWidth / SLOPE_CHAR_WIDTH);
13878
+ let labelLineCount = 1;
13879
+ let wrappedLines = null;
13880
+ if (labelText.length > maxChars) {
13881
+ const words = labelText.split(/\s+/);
13882
+ const lines = [];
13883
+ let current = "";
13884
+ for (const word of words) {
13885
+ const test = current ? `${current} ${word}` : word;
13886
+ if (test.length > maxChars && current) {
13887
+ lines.push(current);
13888
+ current = word;
13889
+ } else {
13890
+ current = test;
13891
+ }
13892
+ }
13893
+ if (current) lines.push(current);
13894
+ labelLineCount = lines.length;
13895
+ wrappedLines = lines;
13896
+ }
13897
+ const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
13898
+ const labelHeight = labelLineCount === 1 ? SLOPE_LABEL_FONT_SIZE : labelLineCount * lineHeight;
13899
+ return {
13900
+ item,
13901
+ idx,
13902
+ color,
13903
+ firstVal,
13904
+ lastVal,
13905
+ tipHtml,
13906
+ lastX,
13907
+ labelText,
13908
+ maxChars,
13909
+ wrappedLines,
13910
+ labelHeight
13911
+ };
13912
+ });
13913
+ const leftLabelHeight = 20;
13914
+ const leftLabelCollisions = /* @__PURE__ */ new Map();
13915
+ for (let pi = 0; pi < periods.length - 1; pi++) {
13916
+ const entries = data.map((item) => ({
13917
+ naturalY: yScale(item.values[pi]),
13918
+ height: leftLabelHeight
13919
+ }));
13920
+ leftLabelCollisions.set(pi, resolveVerticalCollisions(entries, 4));
13921
+ }
13922
+ const rightEntries = seriesInfo.map((si) => ({
13923
+ naturalY: yScale(si.lastVal),
13924
+ height: Math.max(si.labelHeight, SLOPE_LABEL_FONT_SIZE * 1.4)
13925
+ }));
13926
+ const rightAdjustedY = resolveVerticalCollisions(rightEntries, 4);
13927
+ data.forEach((item, idx) => {
13928
+ const si = seriesInfo[idx];
13929
+ const color = si.color;
13930
+ const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
13762
13931
  seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", color).attr("stroke-width", 2.5).attr("d", lineGen);
13763
13932
  seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", 14).attr("d", lineGen).style("cursor", onClickItem ? "pointer" : "default").on(
13764
13933
  "mouseenter",
13765
- (event) => showTooltip(tooltip, tipHtml, event)
13934
+ (event) => showTooltip(tooltip, si.tipHtml, event)
13766
13935
  ).on(
13767
13936
  "mousemove",
13768
- (event) => showTooltip(tooltip, tipHtml, event)
13937
+ (event) => showTooltip(tooltip, si.tipHtml, event)
13769
13938
  ).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
13770
13939
  if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
13771
13940
  });
@@ -13774,46 +13943,30 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
13774
13943
  const y = yScale(val);
13775
13944
  seriesG.append("circle").attr("cx", x).attr("cy", y).attr("r", 4).attr("fill", color).attr("stroke", bgColor).attr("stroke-width", 1.5).style("cursor", onClickItem ? "pointer" : "default").on(
13776
13945
  "mouseenter",
13777
- (event) => showTooltip(tooltip, tipHtml, event)
13946
+ (event) => showTooltip(tooltip, si.tipHtml, event)
13778
13947
  ).on(
13779
13948
  "mousemove",
13780
- (event) => showTooltip(tooltip, tipHtml, event)
13949
+ (event) => showTooltip(tooltip, si.tipHtml, event)
13781
13950
  ).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
13782
13951
  if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
13783
13952
  });
13784
13953
  const isFirst = i === 0;
13785
13954
  const isLast = i === periods.length - 1;
13786
13955
  if (!isLast) {
13787
- seriesG.append("text").attr("x", isFirst ? x - 10 : x).attr("y", y).attr("dy", "0.35em").attr("text-anchor", isFirst ? "end" : "middle").attr("fill", textColor).attr("font-size", "16px").text(val.toString());
13956
+ const adjustedY = leftLabelCollisions.get(i)[idx];
13957
+ seriesG.append("text").attr("x", isFirst ? x - 10 : x).attr("y", adjustedY).attr("dy", "0.35em").attr("text-anchor", isFirst ? "end" : "middle").attr("fill", color).attr("font-size", "16px").text(val.toString());
13788
13958
  }
13789
13959
  });
13790
- const lastX = xScale(periods[periods.length - 1]);
13791
- const lastY = yScale(lastVal);
13792
- const labelText = `${lastVal} \u2014 ${item.label}`;
13793
- const availableWidth = rightMargin - 15;
13794
- const maxChars = Math.floor(availableWidth / SLOPE_CHAR_WIDTH);
13795
- const labelEl = seriesG.append("text").attr("x", lastX + 10).attr("y", lastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", `${SLOPE_LABEL_FONT_SIZE}px`).attr("font-weight", "500");
13796
- if (labelText.length <= maxChars) {
13797
- labelEl.attr("dy", "0.35em").text(labelText);
13960
+ const adjustedLastY = rightAdjustedY[idx];
13961
+ const labelEl = seriesG.append("text").attr("x", si.lastX + 10).attr("y", adjustedLastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", `${SLOPE_LABEL_FONT_SIZE}px`).attr("font-weight", "500");
13962
+ if (!si.wrappedLines) {
13963
+ labelEl.attr("dy", "0.35em").text(si.labelText);
13798
13964
  } else {
13799
- const words = labelText.split(/\s+/);
13800
- const lines = [];
13801
- let current = "";
13802
- for (const word of words) {
13803
- const test = current ? `${current} ${word}` : word;
13804
- if (test.length > maxChars && current) {
13805
- lines.push(current);
13806
- current = word;
13807
- } else {
13808
- current = test;
13809
- }
13810
- }
13811
- if (current) lines.push(current);
13812
13965
  const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
13813
- const totalHeight = (lines.length - 1) * lineHeight;
13966
+ const totalHeight = (si.wrappedLines.length - 1) * lineHeight;
13814
13967
  const startDy = -totalHeight / 2;
13815
- lines.forEach((line7, li) => {
13816
- labelEl.append("tspan").attr("x", lastX + 10).attr(
13968
+ si.wrappedLines.forEach((line7, li) => {
13969
+ labelEl.append("tspan").attr("x", si.lastX + 10).attr(
13817
13970
  "dy",
13818
13971
  li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
13819
13972
  ).text(line7);
@@ -16226,6 +16379,7 @@ var init_d3 = __esm({
16226
16379
  init_colors();
16227
16380
  init_palettes();
16228
16381
  init_diagnostics();
16382
+ init_parsing();
16229
16383
  DEFAULT_CLOUD_OPTIONS = {
16230
16384
  rotate: "none",
16231
16385
  max: 0,