@diagrammo/dgmo 0.5.4 → 0.6.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/src/echarts.ts CHANGED
@@ -7,7 +7,7 @@ import { injectBranding } from './branding';
7
7
  // Types
8
8
  // ============================================================
9
9
 
10
- export type EChartsChartType =
10
+ export type ExtendedChartType =
11
11
  | 'sankey'
12
12
  | 'chord'
13
13
  | 'function'
@@ -15,7 +15,7 @@ export type EChartsChartType =
15
15
  | 'heatmap'
16
16
  | 'funnel';
17
17
 
18
- export interface EChartsDataPoint {
18
+ export interface ExtendedChartDataPoint {
19
19
  label: string;
20
20
  value: number;
21
21
  color?: string;
@@ -55,14 +55,14 @@ export interface ParsedHeatmapRow {
55
55
 
56
56
  import type { DgmoError } from './diagnostics';
57
57
 
58
- export interface ParsedEChart {
59
- type: EChartsChartType;
58
+ export interface ParsedExtendedChart {
59
+ type: ExtendedChartType;
60
60
  title?: string;
61
61
  titleLineNumber?: number;
62
62
  series?: string;
63
63
  seriesNames?: string[];
64
64
  seriesNameColors?: (string | undefined)[];
65
- data: EChartsDataPoint[];
65
+ data: ExtendedChartDataPoint[];
66
66
  links?: ParsedSankeyLink[];
67
67
  functions?: ParsedFunction[];
68
68
  scatterPoints?: ParsedScatterPoint[];
@@ -87,7 +87,7 @@ export interface ParsedEChart {
87
87
  import type { PaletteColors } from './palettes';
88
88
  import { getSeriesColors, getSegmentColors } from './palettes';
89
89
  import { parseChart } from './chart';
90
- import type { ParsedChart } from './chart';
90
+ import type { ParsedChart, ChartEra } from './chart';
91
91
  import { makeDgmoError, formatDgmoError, suggest } from './diagnostics';
92
92
  import { resolveColor } from './colors';
93
93
  import { collectIndentedValues, extractColor, measureIndent, parseSeriesNames } from './utils/parsing';
@@ -104,7 +104,7 @@ const CHART_BASE: Pick<EChartsOption, 'backgroundColor' | 'animation'> = { backg
104
104
  // ============================================================
105
105
 
106
106
  /**
107
- * Parses the simple echart text format into a structured object.
107
+ * Parses extended chart content into a structured object.
108
108
  *
109
109
  * Format:
110
110
  * ```
@@ -117,12 +117,12 @@ const CHART_BASE: Pick<EChartsOption, 'backgroundColor' | 'animation'> = { backg
117
117
  * Mar: 150
118
118
  * ```
119
119
  */
120
- export function parseEChart(
120
+ export function parseExtendedChart(
121
121
  content: string,
122
122
  palette?: PaletteColors
123
- ): ParsedEChart {
123
+ ): ParsedExtendedChart {
124
124
  const lines = content.split('\n');
125
- const result: ParsedEChart = {
125
+ const result: ParsedExtendedChart = {
126
126
  type: 'scatter',
127
127
  data: [],
128
128
  diagnostics: [],
@@ -458,10 +458,12 @@ function buildChartCommons(parsed: { title?: string; error?: string | null }, pa
458
458
  }
459
459
 
460
460
  /**
461
- * Converts parsed echart data to ECharts option object.
461
+ * Converts a ParsedExtendedChart into an EChartsOption.
462
+ * Handles extended chart types: scatter, sankey, chord, function, heatmap, funnel.
463
+ * @param parsed - Result of parseExtendedChart()
462
464
  */
463
- export function buildEChartsOption(
464
- parsed: ParsedEChart,
465
+ export function buildExtendedChartOption(
466
+ parsed: ParsedExtendedChart,
465
467
  palette: PaletteColors,
466
468
  isDark: boolean
467
469
  ): EChartsOption {
@@ -548,7 +550,7 @@ export function buildEChartsOption(
548
550
  * Builds ECharts option for sankey diagrams.
549
551
  */
550
552
  function buildSankeyOption(
551
- parsed: ParsedEChart,
553
+ parsed: ParsedExtendedChart,
552
554
  textColor: string,
553
555
  colors: string[],
554
556
  titleConfig: EChartsOption['title'],
@@ -611,7 +613,7 @@ function buildSankeyOption(
611
613
  * Builds ECharts option for chord diagrams.
612
614
  */
613
615
  function buildChordOption(
614
- parsed: ParsedEChart,
616
+ parsed: ParsedExtendedChart,
615
617
  textColor: string,
616
618
  colors: string[],
617
619
  titleConfig: EChartsOption['title'],
@@ -755,7 +757,7 @@ function evaluateExpression(expr: string, x: number): number {
755
757
  * Builds ECharts option for function plots.
756
758
  */
757
759
  function buildFunctionOption(
758
- parsed: ParsedEChart,
760
+ parsed: ParsedExtendedChart,
759
761
  palette: PaletteColors,
760
762
  textColor: string,
761
763
  axisLineColor: string,
@@ -868,7 +870,7 @@ function buildFunctionOption(
868
870
  * - hasSize → dynamic symbol sizing from 3rd value
869
871
  */
870
872
  function buildScatterOption(
871
- parsed: ParsedEChart,
873
+ parsed: ParsedExtendedChart,
872
874
  palette: PaletteColors,
873
875
  textColor: string,
874
876
  axisLineColor: string,
@@ -1061,7 +1063,7 @@ function buildScatterOption(
1061
1063
  * Builds ECharts option for heatmap charts.
1062
1064
  */
1063
1065
  function buildHeatmapOption(
1064
- parsed: ParsedEChart,
1066
+ parsed: ParsedExtendedChart,
1065
1067
  palette: PaletteColors,
1066
1068
  textColor: string,
1067
1069
  axisLineColor: string,
@@ -1178,7 +1180,7 @@ function buildHeatmapOption(
1178
1180
  * Builds ECharts option for funnel charts.
1179
1181
  */
1180
1182
  function buildFunnelOption(
1181
- parsed: ParsedEChart,
1183
+ parsed: ParsedExtendedChart,
1182
1184
  textColor: string,
1183
1185
  colors: string[],
1184
1186
  titleConfig: EChartsOption['title'],
@@ -1320,7 +1322,8 @@ function makeGridAxis(
1320
1322
  label?: string,
1321
1323
  data?: string[],
1322
1324
  nameGapOverride?: number,
1323
- chartWidthHint?: number
1325
+ chartWidthHint?: number,
1326
+ intervalOverride?: (index: number, value: string) => boolean
1324
1327
  ): Record<string, unknown> {
1325
1328
  const defaultGap = type === 'value' ? 75 : 40;
1326
1329
 
@@ -1354,7 +1357,7 @@ function makeGridAxis(
1354
1357
  fontSize: type === 'category' && data ? catFontSize : 16,
1355
1358
  fontFamily: FONT_FAMILY,
1356
1359
  ...(type === 'category' && {
1357
- interval: 0,
1360
+ interval: intervalOverride ?? 0,
1358
1361
  formatter: (value: string) =>
1359
1362
  value.replace(/([a-z])([A-Z])/g, '$1\n$2'),
1360
1363
  ...catLabelExtras,
@@ -1372,9 +1375,10 @@ function makeGridAxis(
1372
1375
 
1373
1376
  /**
1374
1377
  * Converts a ParsedChart into an EChartsOption.
1375
- * Renders standard chart types (bar, line, pie, etc.) with ECharts.
1378
+ * Handles standard chart types: bar, line, area, pie, doughnut, radar, polar-area, bar-stacked, multi-line.
1379
+ * @param parsed - Result of parseChart()
1376
1380
  */
1377
- export function buildEChartsOptionFromChart(
1381
+ export function buildSimpleChartOption(
1378
1382
  parsed: ParsedChart,
1379
1383
  palette: PaletteColors,
1380
1384
  isDark: boolean,
@@ -1391,7 +1395,7 @@ export function buildEChartsOptionFromChart(
1391
1395
  return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth);
1392
1396
  case 'line':
1393
1397
  return parsed.seriesNames
1394
- ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth)
1398
+ ? buildMultiLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth)
1395
1399
  : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth);
1396
1400
  case 'area':
1397
1401
  return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth);
@@ -1471,6 +1475,61 @@ function buildBarOption(
1471
1475
  };
1472
1476
  }
1473
1477
 
1478
+ // ── Era band helpers ──────────────────────────────────────────
1479
+
1480
+ function buildIntervalCallback(
1481
+ labels: string[],
1482
+ eras: ChartEra[]
1483
+ ): (index: number, value: string) => boolean {
1484
+ const count = labels.length;
1485
+ if (count <= 8) return () => true; // show all; not `0` (ECharts auto ≠ show all)
1486
+ const snapSteps = [1, 2, 4, 5, 10, 20, 25, 50];
1487
+ const raw = Math.ceil(count / 8);
1488
+ const N = [...snapSteps].reverse().find((s) => s <= raw) ?? 1; // snap down
1489
+ const pinned = new Set<number>();
1490
+ for (let i = 0; i < count; i += N) pinned.add(i);
1491
+ for (const era of eras) {
1492
+ const si = labels.indexOf(era.start);
1493
+ const ei = labels.indexOf(era.end);
1494
+ if (si >= 0) pinned.add(si);
1495
+ if (ei >= 0) pinned.add(ei);
1496
+ }
1497
+ return (index: number) => pinned.has(index);
1498
+ }
1499
+
1500
+ function buildMarkArea(
1501
+ eras: ChartEra[],
1502
+ labels: string[],
1503
+ textColor: string,
1504
+ defaultColor: string
1505
+ ): Record<string, unknown> | undefined {
1506
+ if (eras.length === 0) return undefined;
1507
+ return {
1508
+ silent: false,
1509
+ tooltip: { show: true },
1510
+ data: eras.map((era) => {
1511
+ const startIdx = labels.indexOf(era.start);
1512
+ const endIdx = labels.indexOf(era.end);
1513
+ const bandSlots = startIdx >= 0 && endIdx >= 0 ? endIdx - startIdx : Infinity;
1514
+ const color = era.color ?? defaultColor;
1515
+ return [
1516
+ {
1517
+ name: era.label,
1518
+ xAxis: era.start,
1519
+ itemStyle: { color, opacity: 0.15 },
1520
+ label: {
1521
+ show: bandSlots >= 3,
1522
+ position: 'insideTop',
1523
+ fontSize: 11,
1524
+ color: textColor,
1525
+ },
1526
+ },
1527
+ { xAxis: era.end },
1528
+ ];
1529
+ }),
1530
+ };
1531
+ }
1532
+
1474
1533
  // ── Line ─────────────────────────────────────────────────────
1475
1534
 
1476
1535
  function buildLineOption(
@@ -1488,6 +1547,9 @@ function buildLineOption(
1488
1547
  const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
1489
1548
  const labels = parsed.data.map((d) => d.label);
1490
1549
  const values = parsed.data.map((d) => d.value);
1550
+ const eras = parsed.eras ?? [];
1551
+ const interval = buildIntervalCallback(labels, eras);
1552
+ const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
1491
1553
 
1492
1554
  return {
1493
1555
  ...CHART_BASE,
@@ -1498,7 +1560,7 @@ function buildLineOption(
1498
1560
  axisPointer: { type: 'line' },
1499
1561
  },
1500
1562
  grid: makeChartGrid({ xLabel, yLabel, hasTitle: !!parsed.title }),
1501
- xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, undefined, chartWidth),
1563
+ xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, undefined, chartWidth, interval),
1502
1564
  yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
1503
1565
  series: [
1504
1566
  {
@@ -1509,6 +1571,7 @@ function buildLineOption(
1509
1571
  lineStyle: { color: lineColor, width: 3 },
1510
1572
  itemStyle: { color: lineColor },
1511
1573
  emphasis: EMPHASIS_SELF,
1574
+ ...(markArea && { markArea }),
1512
1575
  },
1513
1576
  ],
1514
1577
  };
@@ -1518,6 +1581,7 @@ function buildLineOption(
1518
1581
 
1519
1582
  function buildMultiLineOption(
1520
1583
  parsed: ParsedChart,
1584
+ palette: PaletteColors,
1521
1585
  textColor: string,
1522
1586
  axisLineColor: string,
1523
1587
  splitLineColor: string,
@@ -1530,6 +1594,9 @@ function buildMultiLineOption(
1530
1594
  const { xLabel, yLabel } = resolveAxisLabels(parsed);
1531
1595
  const seriesNames = parsed.seriesNames ?? [];
1532
1596
  const labels = parsed.data.map((d) => d.label);
1597
+ const eras = parsed.eras ?? [];
1598
+ const interval = buildIntervalCallback(labels, eras);
1599
+ const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
1533
1600
 
1534
1601
  const series = seriesNames.map((name, idx) => {
1535
1602
  const color = parsed.seriesNameColors?.[idx] ?? colors[idx % colors.length];
@@ -1545,6 +1612,7 @@ function buildMultiLineOption(
1545
1612
  lineStyle: { color, width: 3 },
1546
1613
  itemStyle: { color },
1547
1614
  emphasis: EMPHASIS_SELF,
1615
+ ...(idx === 0 && markArea && { markArea }),
1548
1616
  };
1549
1617
  });
1550
1618
 
@@ -1562,7 +1630,7 @@ function buildMultiLineOption(
1562
1630
  textStyle: { color: textColor },
1563
1631
  },
1564
1632
  grid: makeChartGrid({ xLabel, yLabel, hasTitle: !!parsed.title, hasLegend: true }),
1565
- xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, undefined, chartWidth),
1633
+ xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, undefined, chartWidth, interval),
1566
1634
  yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
1567
1635
  series,
1568
1636
  };
@@ -1585,6 +1653,9 @@ function buildAreaOption(
1585
1653
  const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
1586
1654
  const labels = parsed.data.map((d) => d.label);
1587
1655
  const values = parsed.data.map((d) => d.value);
1656
+ const eras = parsed.eras ?? [];
1657
+ const interval = buildIntervalCallback(labels, eras);
1658
+ const markArea = buildMarkArea(eras, labels, textColor, palette.colors.blue);
1588
1659
 
1589
1660
  return {
1590
1661
  ...CHART_BASE,
@@ -1595,7 +1666,7 @@ function buildAreaOption(
1595
1666
  axisPointer: { type: 'line' },
1596
1667
  },
1597
1668
  grid: makeChartGrid({ xLabel, yLabel, hasTitle: !!parsed.title }),
1598
- xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, undefined, chartWidth),
1669
+ xAxis: makeGridAxis('category', textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, undefined, chartWidth, interval),
1599
1670
  yAxis: makeGridAxis('value', textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
1600
1671
  series: [
1601
1672
  {
@@ -1607,6 +1678,7 @@ function buildAreaOption(
1607
1678
  itemStyle: { color: lineColor },
1608
1679
  areaStyle: { opacity: 0.25 },
1609
1680
  emphasis: EMPHASIS_SELF,
1681
+ ...(markArea && { markArea }),
1610
1682
  },
1611
1683
  ],
1612
1684
  };
@@ -1852,13 +1924,17 @@ function buildBarStackedOption(
1852
1924
  const ECHART_EXPORT_WIDTH = 1200;
1853
1925
  const ECHART_EXPORT_HEIGHT = 800;
1854
1926
 
1855
- import { STANDARD_CHART_TYPES } from './dgmo-router';
1927
+ // Standard chart types handled by buildSimpleChartOption (via parseChart)
1928
+ const STANDARD_CHART_TYPES = new Set([
1929
+ 'bar', 'line', 'multi-line', 'area', 'pie', 'doughnut',
1930
+ 'radar', 'polar-area', 'bar-stacked',
1931
+ ]);
1856
1932
 
1857
1933
  /**
1858
- * Renders an ECharts diagram to SVG using server-side rendering.
1859
- * Mirrors the `renderD3ForExport` API — returns an SVG string or empty string on failure.
1934
+ * Renders an extended chart (scatter, sankey, chord, function, heatmap, funnel) to SVG using server-side rendering.
1935
+ * Mirrors the `renderForExport` API — returns an SVG string or empty string on failure.
1860
1936
  */
1861
- export async function renderEChartsForExport(
1937
+ export async function renderExtendedChartForExport(
1862
1938
  content: string,
1863
1939
  theme: 'light' | 'dark' | 'transparent',
1864
1940
  palette?: PaletteColors,
@@ -1879,11 +1955,11 @@ export async function renderEChartsForExport(
1879
1955
  if (chartType && STANDARD_CHART_TYPES.has(chartType)) {
1880
1956
  const parsed = parseChart(content, effectivePalette);
1881
1957
  if (parsed.error) return '';
1882
- option = buildEChartsOptionFromChart(parsed, effectivePalette, isDark, ECHART_EXPORT_WIDTH);
1958
+ option = buildSimpleChartOption(parsed, effectivePalette, isDark, ECHART_EXPORT_WIDTH);
1883
1959
  } else {
1884
- const parsed = parseEChart(content, effectivePalette);
1960
+ const parsed = parseExtendedChart(content, effectivePalette);
1885
1961
  if (parsed.error) return '';
1886
- option = buildEChartsOption(parsed, effectivePalette, isDark);
1962
+ option = buildExtendedChartOption(parsed, effectivePalette, isDark);
1887
1963
  }
1888
1964
  if (!option || Object.keys(option).length === 0) return '';
1889
1965
 
package/src/index.ts CHANGED
@@ -17,12 +17,11 @@ export { render } from './render';
17
17
 
18
18
  export {
19
19
  parseDgmoChartType,
20
- getDgmoFramework,
21
20
  parseDgmo,
22
- DGMO_CHART_TYPE_MAP,
23
- STANDARD_CHART_TYPES,
21
+ getRenderCategory,
22
+ isExtendedChartType,
24
23
  } from './dgmo-router';
25
- export type { DgmoFramework } from './dgmo-router';
24
+ export type { RenderCategory } from './dgmo-router';
26
25
 
27
26
  // ============================================================
28
27
  // Parsers
@@ -33,20 +32,21 @@ export type {
33
32
  ParsedChart,
34
33
  ChartType,
35
34
  ChartDataPoint,
35
+ ChartEra,
36
36
  } from './chart';
37
37
 
38
- export { parseEChart } from './echarts';
39
- export type { ParsedEChart, EChartsChartType } from './echarts';
38
+ export { parseExtendedChart } from './echarts';
39
+ export type { ParsedExtendedChart, ExtendedChartType } from './echarts';
40
40
 
41
41
  export {
42
- parseD3,
42
+ parseVisualization,
43
43
  orderArcNodes,
44
44
  parseTimelineDate,
45
45
  addDurationToDate,
46
46
  computeTimeTicks,
47
47
  formatDateLabel,
48
48
  } from './d3';
49
- export type { ParsedD3, D3ChartType, D3ExportDimensions, ArcLink, ArcNodeGroup } from './d3';
49
+ export type { ParsedVisualization, VisualizationType, D3ExportDimensions, ArcLink, ArcNodeGroup } from './d3';
50
50
 
51
51
  export {
52
52
  parseSequenceDgmo,
@@ -279,7 +279,7 @@ export { renderFlowchart, renderFlowchartForExport } from './graph/flowchart-ren
279
279
  // Config Builders (produce framework-specific config objects)
280
280
  // ============================================================
281
281
 
282
- export { buildEChartsOption, buildEChartsOptionFromChart, renderEChartsForExport } from './echarts';
282
+ export { buildExtendedChartOption, buildSimpleChartOption, renderExtendedChartForExport } from './echarts';
283
283
  export { buildMermaidQuadrant } from './dgmo-mermaid';
284
284
 
285
285
  // ============================================================
@@ -293,7 +293,7 @@ export {
293
293
  renderWordCloud,
294
294
  renderVenn,
295
295
  renderQuadrant,
296
- renderD3ForExport,
296
+ renderForExport,
297
297
  } from './d3';
298
298
 
299
299
  export {
package/src/render.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { renderD3ForExport } from './d3';
2
- import { renderEChartsForExport } from './echarts';
3
- import { parseDgmoChartType, getDgmoFramework } from './dgmo-router';
1
+ import { renderForExport } from './d3';
2
+ import { renderExtendedChartForExport } from './echarts';
3
+ import { parseDgmoChartType, getRenderCategory } from './dgmo-router';
4
4
  import { getPalette } from './palettes/registry';
5
5
 
6
6
  /**
@@ -62,15 +62,15 @@ export async function render(
62
62
  const paletteColors = getPalette(paletteName)[theme === 'dark' ? 'dark' : 'light'];
63
63
 
64
64
  const chartType = parseDgmoChartType(content);
65
- const framework = chartType ? getDgmoFramework(chartType) : null;
65
+ const category = chartType ? getRenderCategory(chartType) : null;
66
66
 
67
- if (framework === 'echart') {
68
- return renderEChartsForExport(content, theme, paletteColors, { branding });
67
+ if (category === 'data-chart') {
68
+ return renderExtendedChartForExport(content, theme, paletteColors, { branding });
69
69
  }
70
70
 
71
- // D3 and unknown/null frameworks both go through D3 renderer
71
+ // Visualization/diagram and unknown/null types all go through the unified renderer
72
72
  await ensureDom();
73
- return renderD3ForExport(content, theme, paletteColors, undefined, {
73
+ return renderForExport(content, theme, paletteColors, undefined, {
74
74
  branding,
75
75
  c4Level: options?.c4Level,
76
76
  c4System: options?.c4System,