@diagrammo/dgmo 0.2.2 → 0.2.3

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/src/echarts.ts CHANGED
@@ -391,10 +391,11 @@ export function parseEChart(
391
391
  export function buildEChartsOption(
392
392
  parsed: ParsedEChart,
393
393
  palette: PaletteColors,
394
- _isDark: boolean
394
+ isDark: boolean
395
395
  ): EChartsOption {
396
396
  const textColor = palette.text;
397
397
  const axisLineColor = palette.border;
398
+ const gridOpacity = isDark ? 0.7 : 0.4;
398
399
  const colors = getSeriesColors(palette);
399
400
 
400
401
  if (parsed.error) {
@@ -407,9 +408,10 @@ export function buildEChartsOption(
407
408
  ? {
408
409
  text: parsed.title,
409
410
  left: 'center' as const,
411
+ top: 8,
410
412
  textStyle: {
411
413
  color: textColor,
412
- fontSize: 18,
414
+ fontSize: 20,
413
415
  fontWeight: 'bold' as const,
414
416
  fontFamily: FONT_FAMILY,
415
417
  },
@@ -452,6 +454,7 @@ export function buildEChartsOption(
452
454
  palette,
453
455
  textColor,
454
456
  axisLineColor,
457
+ gridOpacity,
455
458
  colors,
456
459
  titleConfig,
457
460
  tooltipTheme
@@ -465,6 +468,7 @@ export function buildEChartsOption(
465
468
  palette,
466
469
  textColor,
467
470
  axisLineColor,
471
+ gridOpacity,
468
472
  colors,
469
473
  titleConfig,
470
474
  tooltipTheme
@@ -704,6 +708,7 @@ function buildFunctionOption(
704
708
  palette: PaletteColors,
705
709
  textColor: string,
706
710
  axisLineColor: string,
711
+ gridOpacity: number,
707
712
  colors: string[],
708
713
  titleConfig: EChartsOption['title'],
709
714
  tooltipTheme: Record<string, unknown>
@@ -761,7 +766,7 @@ function buildFunctionOption(
761
766
  },
762
767
  },
763
768
  grid: {
764
- left: '3%',
769
+ left: '4%',
765
770
  right: '4%',
766
771
  bottom: '15%',
767
772
  top: parsed.title ? '15%' : '5%',
@@ -776,10 +781,12 @@ function buildFunctionOption(
776
781
  },
777
782
  axisLabel: {
778
783
  color: textColor,
784
+ fontSize: 16,
779
785
  },
780
786
  splitLine: {
781
787
  lineStyle: {
782
- color: palette.overlay,
788
+ color: palette.border,
789
+ opacity: gridOpacity,
783
790
  },
784
791
  },
785
792
  },
@@ -790,10 +797,12 @@ function buildFunctionOption(
790
797
  },
791
798
  axisLabel: {
792
799
  color: textColor,
800
+ fontSize: 16,
793
801
  },
794
802
  splitLine: {
795
803
  lineStyle: {
796
- color: palette.overlay,
804
+ color: palette.border,
805
+ opacity: gridOpacity,
797
806
  },
798
807
  },
799
808
  },
@@ -812,6 +821,7 @@ function buildScatterOption(
812
821
  palette: PaletteColors,
813
822
  textColor: string,
814
823
  axisLineColor: string,
824
+ gridOpacity: number,
815
825
  colors: string[],
816
826
  titleConfig: EChartsOption['title'],
817
827
  tooltipTheme: Record<string, unknown>
@@ -914,6 +924,16 @@ function buildScatterOption(
914
924
  },
915
925
  };
916
926
 
927
+ // Auto-fit axes to data range with ~10% padding
928
+ const xValues = points.map((p) => p.x);
929
+ const yValues = points.map((p) => p.y);
930
+ const xMin = Math.min(...xValues);
931
+ const xMax = Math.max(...xValues);
932
+ const yMin = Math.min(...yValues);
933
+ const yMax = Math.max(...yValues);
934
+ const xPad = (xMax - xMin) * 0.1 || 1;
935
+ const yPad = (yMax - yMin) * 0.1 || 1;
936
+
917
937
  return {
918
938
  backgroundColor: 'transparent',
919
939
  animation: false,
@@ -927,9 +947,9 @@ function buildScatterOption(
927
947
  },
928
948
  }),
929
949
  grid: {
930
- left: '3%',
950
+ left: parsed.ylabel ? '5%' : '3%',
931
951
  right: '4%',
932
- bottom: hasCategories ? '15%' : '3%',
952
+ bottom: hasCategories ? '15%' : parsed.xlabel ? '10%' : '3%',
933
953
  top: parsed.title ? '15%' : '5%',
934
954
  containLabel: true,
935
955
  },
@@ -937,20 +957,24 @@ function buildScatterOption(
937
957
  type: 'value',
938
958
  name: parsed.xlabel,
939
959
  nameLocation: 'middle',
940
- nameGap: 30,
960
+ nameGap: 40,
941
961
  nameTextStyle: {
942
962
  color: textColor,
943
- fontSize: 12,
963
+ fontSize: 18,
944
964
  },
965
+ min: Math.floor(xMin - xPad),
966
+ max: Math.ceil(xMax + xPad),
945
967
  axisLine: {
946
968
  lineStyle: { color: axisLineColor },
947
969
  },
948
970
  axisLabel: {
949
971
  color: textColor,
972
+ fontSize: 16,
950
973
  },
951
974
  splitLine: {
952
975
  lineStyle: {
953
- color: palette.overlay,
976
+ color: palette.border,
977
+ opacity: gridOpacity,
954
978
  },
955
979
  },
956
980
  },
@@ -958,20 +982,24 @@ function buildScatterOption(
958
982
  type: 'value',
959
983
  name: parsed.ylabel,
960
984
  nameLocation: 'middle',
961
- nameGap: 40,
985
+ nameGap: 50,
962
986
  nameTextStyle: {
963
987
  color: textColor,
964
- fontSize: 12,
988
+ fontSize: 18,
965
989
  },
990
+ min: Math.floor(yMin - yPad),
991
+ max: Math.ceil(yMax + yPad),
966
992
  axisLine: {
967
993
  lineStyle: { color: axisLineColor },
968
994
  },
969
995
  axisLabel: {
970
996
  color: textColor,
997
+ fontSize: 16,
971
998
  },
972
999
  splitLine: {
973
1000
  lineStyle: {
974
- color: palette.overlay,
1001
+ color: palette.border,
1002
+ opacity: gridOpacity,
975
1003
  },
976
1004
  },
977
1005
  },
@@ -1039,6 +1067,7 @@ function buildHeatmapOption(
1039
1067
  },
1040
1068
  axisLabel: {
1041
1069
  color: textColor,
1070
+ fontSize: 16,
1042
1071
  },
1043
1072
  },
1044
1073
  yAxis: {
@@ -1052,6 +1081,7 @@ function buildHeatmapOption(
1052
1081
  },
1053
1082
  axisLabel: {
1054
1083
  color: textColor,
1084
+ fontSize: 16,
1055
1085
  },
1056
1086
  },
1057
1087
  visualMap: {
@@ -1063,7 +1093,6 @@ function buildHeatmapOption(
1063
1093
  top: 'center',
1064
1094
  inRange: {
1065
1095
  color: [
1066
- palette.bg,
1067
1096
  palette.primary,
1068
1097
  palette.colors.cyan,
1069
1098
  palette.colors.yellow,
@@ -1080,7 +1109,9 @@ function buildHeatmapOption(
1080
1109
  data,
1081
1110
  label: {
1082
1111
  show: true,
1083
- color: textColor,
1112
+ color: '#ffffff',
1113
+ fontSize: 14,
1114
+ fontWeight: 'bold' as const,
1084
1115
  },
1085
1116
  emphasis: {
1086
1117
  itemStyle: {
@@ -1236,6 +1267,7 @@ function makeGridAxis(
1236
1267
  textColor: string,
1237
1268
  axisLineColor: string,
1238
1269
  splitLineColor: string,
1270
+ gridOpacity: number,
1239
1271
  label?: string,
1240
1272
  data?: string[]
1241
1273
  ): Record<string, unknown> {
@@ -1243,13 +1275,13 @@ function makeGridAxis(
1243
1275
  type,
1244
1276
  ...(data && { data }),
1245
1277
  axisLine: { lineStyle: { color: axisLineColor } },
1246
- axisLabel: { color: textColor, fontFamily: FONT_FAMILY },
1247
- splitLine: { lineStyle: { color: splitLineColor } },
1278
+ axisLabel: { color: textColor, fontSize: 16, fontFamily: FONT_FAMILY },
1279
+ splitLine: { lineStyle: { color: splitLineColor, opacity: gridOpacity } },
1248
1280
  ...(label && {
1249
1281
  name: label,
1250
1282
  nameLocation: 'middle',
1251
- nameGap: 30,
1252
- nameTextStyle: { color: textColor, fontSize: 12, fontFamily: FONT_FAMILY },
1283
+ nameGap: 40,
1284
+ nameTextStyle: { color: textColor, fontSize: 18, fontFamily: FONT_FAMILY },
1253
1285
  }),
1254
1286
  };
1255
1287
  }
@@ -1261,22 +1293,24 @@ function makeGridAxis(
1261
1293
  export function buildEChartsOptionFromChart(
1262
1294
  parsed: ParsedChart,
1263
1295
  palette: PaletteColors,
1264
- _isDark: boolean
1296
+ isDark: boolean
1265
1297
  ): EChartsOption {
1266
1298
  if (parsed.error) return {};
1267
1299
 
1268
1300
  const textColor = palette.text;
1269
1301
  const axisLineColor = palette.border;
1270
- const splitLineColor = palette.overlay;
1302
+ const splitLineColor = palette.border;
1303
+ const gridOpacity = isDark ? 0.7 : 0.4;
1271
1304
  const colors = getSeriesColors(palette);
1272
1305
 
1273
1306
  const titleConfig = parsed.title
1274
1307
  ? {
1275
1308
  text: parsed.title,
1276
1309
  left: 'center' as const,
1310
+ top: 8,
1277
1311
  textStyle: {
1278
1312
  color: textColor,
1279
- fontSize: 18,
1313
+ fontSize: 20,
1280
1314
  fontWeight: 'bold' as const,
1281
1315
  fontFamily: FONT_FAMILY,
1282
1316
  },
@@ -1291,21 +1325,21 @@ export function buildEChartsOptionFromChart(
1291
1325
 
1292
1326
  switch (parsed.type) {
1293
1327
  case 'bar':
1294
- return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme);
1328
+ return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme);
1295
1329
  case 'bar-stacked':
1296
- return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme);
1330
+ return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme);
1297
1331
  case 'line':
1298
1332
  return parsed.seriesNames
1299
- ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, colors, titleConfig, tooltipTheme)
1300
- : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, titleConfig, tooltipTheme);
1333
+ ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme)
1334
+ : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme);
1301
1335
  case 'area':
1302
- return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, titleConfig, tooltipTheme);
1336
+ return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme);
1303
1337
  case 'pie':
1304
1338
  return buildPieOption(parsed, textColor, colors, titleConfig, tooltipTheme, false);
1305
1339
  case 'doughnut':
1306
1340
  return buildPieOption(parsed, textColor, colors, titleConfig, tooltipTheme, true);
1307
1341
  case 'radar':
1308
- return buildRadarOption(parsed, palette, textColor, colors, titleConfig, tooltipTheme);
1342
+ return buildRadarOption(parsed, palette, textColor, gridOpacity, colors, titleConfig, tooltipTheme);
1309
1343
  case 'polar-area':
1310
1344
  return buildPolarAreaOption(parsed, textColor, colors, titleConfig, tooltipTheme);
1311
1345
  }
@@ -1318,6 +1352,7 @@ function buildBarOption(
1318
1352
  textColor: string,
1319
1353
  axisLineColor: string,
1320
1354
  splitLineColor: string,
1355
+ gridOpacity: number,
1321
1356
  colors: string[],
1322
1357
  titleConfig: EChartsOption['title'],
1323
1358
  tooltipTheme: Record<string, unknown>
@@ -1330,8 +1365,10 @@ function buildBarOption(
1330
1365
  itemStyle: { color: d.color ?? colors[i % colors.length] },
1331
1366
  }));
1332
1367
 
1333
- const categoryAxis = makeGridAxis('category', textColor, axisLineColor, splitLineColor, isHorizontal ? yLabel : xLabel, labels);
1334
- const valueAxis = makeGridAxis('value', textColor, axisLineColor, splitLineColor, isHorizontal ? xLabel : yLabel);
1368
+ const categoryAxis = makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels);
1369
+ const valueAxis = makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
1370
+
1371
+ // xAxis is always the bottom axis, yAxis is always the left axis in ECharts
1335
1372
 
1336
1373
  return {
1337
1374
  backgroundColor: 'transparent',
@@ -1343,9 +1380,9 @@ function buildBarOption(
1343
1380
  axisPointer: { type: 'shadow' },
1344
1381
  },
1345
1382
  grid: {
1346
- left: '3%',
1383
+ left: yLabel ? '5%' : '3%',
1347
1384
  right: '4%',
1348
- bottom: '3%',
1385
+ bottom: xLabel ? '10%' : '3%',
1349
1386
  top: parsed.title ? '15%' : '5%',
1350
1387
  containLabel: true,
1351
1388
  },
@@ -1368,6 +1405,7 @@ function buildLineOption(
1368
1405
  textColor: string,
1369
1406
  axisLineColor: string,
1370
1407
  splitLineColor: string,
1408
+ gridOpacity: number,
1371
1409
  titleConfig: EChartsOption['title'],
1372
1410
  tooltipTheme: Record<string, unknown>
1373
1411
  ): EChartsOption {
@@ -1386,14 +1424,14 @@ function buildLineOption(
1386
1424
  axisPointer: { type: 'line' },
1387
1425
  },
1388
1426
  grid: {
1389
- left: '3%',
1427
+ left: yLabel ? '5%' : '3%',
1390
1428
  right: '4%',
1391
- bottom: '3%',
1429
+ bottom: xLabel ? '10%' : '3%',
1392
1430
  top: parsed.title ? '15%' : '5%',
1393
1431
  containLabel: true,
1394
1432
  },
1395
- xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, xLabel, labels),
1396
- yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, yLabel),
1433
+ xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
1434
+ yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
1397
1435
  series: [
1398
1436
  {
1399
1437
  type: 'line',
@@ -1414,6 +1452,7 @@ function buildMultiLineOption(
1414
1452
  textColor: string,
1415
1453
  axisLineColor: string,
1416
1454
  splitLineColor: string,
1455
+ gridOpacity: number,
1417
1456
  colors: string[],
1418
1457
  titleConfig: EChartsOption['title'],
1419
1458
  tooltipTheme: Record<string, unknown>
@@ -1453,14 +1492,14 @@ function buildMultiLineOption(
1453
1492
  textStyle: { color: textColor },
1454
1493
  },
1455
1494
  grid: {
1456
- left: '3%',
1495
+ left: yLabel ? '5%' : '3%',
1457
1496
  right: '4%',
1458
1497
  bottom: '15%',
1459
1498
  top: parsed.title ? '15%' : '5%',
1460
1499
  containLabel: true,
1461
1500
  },
1462
- xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, xLabel, labels),
1463
- yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, yLabel),
1501
+ xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
1502
+ yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
1464
1503
  series,
1465
1504
  };
1466
1505
  }
@@ -1473,6 +1512,7 @@ function buildAreaOption(
1473
1512
  textColor: string,
1474
1513
  axisLineColor: string,
1475
1514
  splitLineColor: string,
1515
+ gridOpacity: number,
1476
1516
  titleConfig: EChartsOption['title'],
1477
1517
  tooltipTheme: Record<string, unknown>
1478
1518
  ): EChartsOption {
@@ -1491,14 +1531,14 @@ function buildAreaOption(
1491
1531
  axisPointer: { type: 'line' },
1492
1532
  },
1493
1533
  grid: {
1494
- left: '3%',
1534
+ left: yLabel ? '5%' : '3%',
1495
1535
  right: '4%',
1496
- bottom: '3%',
1536
+ bottom: xLabel ? '10%' : '3%',
1497
1537
  top: parsed.title ? '15%' : '5%',
1498
1538
  containLabel: true,
1499
1539
  },
1500
- xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, xLabel, labels),
1501
- yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, yLabel),
1540
+ xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
1541
+ yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
1502
1542
  series: [
1503
1543
  {
1504
1544
  type: 'line',
@@ -1544,7 +1584,7 @@ function buildPieOption(
1544
1584
  data,
1545
1585
  label: {
1546
1586
  position: 'outside',
1547
- formatter: '{b}',
1587
+ formatter: '{b} — {c} ({d}%)',
1548
1588
  color: textColor,
1549
1589
  fontFamily: FONT_FAMILY,
1550
1590
  },
@@ -1560,6 +1600,7 @@ function buildRadarOption(
1560
1600
  parsed: ParsedChart,
1561
1601
  palette: PaletteColors,
1562
1602
  textColor: string,
1603
+ gridOpacity: number,
1563
1604
  colors: string[],
1564
1605
  titleConfig: EChartsOption['title'],
1565
1606
  tooltipTheme: Record<string, unknown>
@@ -1567,7 +1608,6 @@ function buildRadarOption(
1567
1608
  const radarColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
1568
1609
  const values = parsed.data.map((d) => d.value);
1569
1610
  const maxValue = Math.max(...values) * 1.15;
1570
- const gridOpacity = 0.6;
1571
1611
 
1572
1612
  const indicator = parsed.data.map((d) => ({
1573
1613
  name: d.label,
@@ -1587,6 +1627,7 @@ function buildRadarOption(
1587
1627
  axisName: {
1588
1628
  color: textColor,
1589
1629
  fontFamily: FONT_FAMILY,
1630
+ fontSize: 16,
1590
1631
  },
1591
1632
  splitLine: {
1592
1633
  lineStyle: { color: palette.border, opacity: gridOpacity },
@@ -1653,7 +1694,7 @@ function buildPolarAreaOption(
1653
1694
  data,
1654
1695
  label: {
1655
1696
  position: 'outside',
1656
- formatter: '{b}',
1697
+ formatter: '{b} — {c} ({d}%)',
1657
1698
  color: textColor,
1658
1699
  fontFamily: FONT_FAMILY,
1659
1700
  },
@@ -1670,6 +1711,7 @@ function buildBarStackedOption(
1670
1711
  textColor: string,
1671
1712
  axisLineColor: string,
1672
1713
  splitLineColor: string,
1714
+ gridOpacity: number,
1673
1715
  colors: string[],
1674
1716
  titleConfig: EChartsOption['title'],
1675
1717
  tooltipTheme: Record<string, unknown>
@@ -1690,11 +1732,20 @@ function buildBarStackedOption(
1690
1732
  stack: 'total',
1691
1733
  data,
1692
1734
  itemStyle: { color },
1735
+ label: {
1736
+ show: true,
1737
+ position: 'inside' as const,
1738
+ formatter: '{c}',
1739
+ color: '#ffffff',
1740
+ fontSize: 14,
1741
+ fontWeight: 'bold' as const,
1742
+ fontFamily: FONT_FAMILY,
1743
+ },
1693
1744
  };
1694
1745
  });
1695
1746
 
1696
- const categoryAxis = makeGridAxis('category', textColor, axisLineColor, splitLineColor, isHorizontal ? yLabel : xLabel, labels);
1697
- const valueAxis = makeGridAxis('value', textColor, axisLineColor, splitLineColor, isHorizontal ? xLabel : yLabel);
1747
+ const categoryAxis = makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels);
1748
+ const valueAxis = makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
1698
1749
 
1699
1750
  return {
1700
1751
  backgroundColor: 'transparent',
@@ -1711,7 +1762,7 @@ function buildBarStackedOption(
1711
1762
  textStyle: { color: textColor },
1712
1763
  },
1713
1764
  grid: {
1714
- left: '3%',
1765
+ left: yLabel ? '5%' : '3%',
1715
1766
  right: '4%',
1716
1767
  bottom: '15%',
1717
1768
  top: parsed.title ? '15%' : '5%',
@@ -1787,10 +1838,11 @@ export async function renderEChartsForExport(
1787
1838
  if (!svgString) return '';
1788
1839
 
1789
1840
  // The SSR output already includes xmlns, width, height, and viewBox.
1790
- // Inject font-family on the root <svg> element for consistent rendering.
1841
+ // Inject font-family and background on the root <svg> element.
1842
+ const bgStyle = theme !== 'transparent' ? `background: ${effectivePalette.bg}; ` : '';
1791
1843
  return svgString.replace(
1792
1844
  /^<svg /,
1793
- `<svg style="font-family: ${FONT_FAMILY}" `
1845
+ `<svg style="${bgStyle}font-family: ${FONT_FAMILY}" `
1794
1846
  );
1795
1847
  } finally {
1796
1848
  chart.dispose();
package/src/index.ts CHANGED
@@ -31,7 +31,7 @@ export {
31
31
  computeTimeTicks,
32
32
  formatDateLabel,
33
33
  } from './d3';
34
- export type { ParsedD3, D3ChartType, ArcLink, ArcNodeGroup } from './d3';
34
+ export type { ParsedD3, D3ChartType, D3ExportDimensions, ArcLink, ArcNodeGroup } from './d3';
35
35
 
36
36
  export {
37
37
  parseSequenceDgmo,
@@ -34,7 +34,7 @@ export const boldPalette: PaletteConfig = {
34
34
  dark: {
35
35
  bg: '#000000',
36
36
  surface: '#111111',
37
- overlay: '#000000',
37
+ overlay: '#1a1a1a',
38
38
  border: '#333333',
39
39
  text: '#ffffff',
40
40
  textMuted: '#aaaaaa',
@@ -140,7 +140,8 @@ const UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
140
140
 
141
141
  /**
142
142
  * Extract return label from a message label string.
143
- * Priority: `<-` syntax first, then UML `method(): return` syntax.
143
+ * Priority: `<-` syntax first, then UML `method(): return` syntax,
144
+ * then shorthand ` : ` separator (splits on last occurrence).
144
145
  */
145
146
  function parseReturnLabel(rawLabel: string): {
146
147
  label: string;
@@ -160,6 +161,16 @@ function parseReturnLabel(rawLabel: string): {
160
161
  return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
161
162
  }
162
163
 
164
+ // Shorthand request : response syntax (split on last " : ")
165
+ const lastSep = rawLabel.lastIndexOf(' : ');
166
+ if (lastSep > 0) {
167
+ const reqPart = rawLabel.substring(0, lastSep).trim();
168
+ const resPart = rawLabel.substring(lastSep + 3).trim();
169
+ if (reqPart && resPart) {
170
+ return { label: reqPart, returnLabel: resPart };
171
+ }
172
+ }
173
+
163
174
  return { label: rawLabel };
164
175
  }
165
176
 
@@ -416,6 +416,7 @@ export interface SectionMessageGroup {
416
416
 
417
417
  export interface SequenceRenderOptions {
418
418
  collapsedSections?: Set<number>; // keyed by section lineNumber
419
+ exportWidth?: number; // Explicit width for CLI/export rendering (bypasses getBoundingClientRect)
419
420
  }
420
421
 
421
422
  /**
@@ -898,7 +899,11 @@ export function renderSequenceDiagram(
898
899
  {
899
900
  let fi = 0;
900
901
  for (let oi = 0; oi < allRenderSteps.length; oi++) {
901
- if (!hiddenMsgIndices.has(allRenderSteps[oi].messageIndex)) {
902
+ const step = allRenderSteps[oi];
903
+ if (
904
+ !hiddenMsgIndices.has(step.messageIndex) &&
905
+ (step.type === 'call' || step.label)
906
+ ) {
902
907
  originalToFiltered.set(oi, fi);
903
908
  fi++;
904
909
  }
@@ -1016,7 +1021,7 @@ export function renderSequenceDiagram(
1016
1021
  Math.max(lifelineLength, 40) +
1017
1022
  40;
1018
1023
 
1019
- const { width: containerWidth } = container.getBoundingClientRect();
1024
+ const containerWidth = options?.exportWidth ?? container.getBoundingClientRect().width;
1020
1025
  const svgWidth = Math.max(totalWidth, containerWidth);
1021
1026
 
1022
1027
  // Center the diagram horizontally
@@ -1103,10 +1108,10 @@ export function renderSequenceDiagram(
1103
1108
  svg
1104
1109
  .append('text')
1105
1110
  .attr('x', svgWidth / 2)
1106
- .attr('y', TOP_MARGIN + TITLE_HEIGHT * 0.7)
1111
+ .attr('y', 30)
1107
1112
  .attr('text-anchor', 'middle')
1108
1113
  .attr('fill', palette.text)
1109
- .attr('font-size', 16)
1114
+ .attr('font-size', 20)
1110
1115
  .attr('font-weight', 'bold')
1111
1116
  .text(title);
1112
1117
  }