@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/README.md +75 -44
- package/dist/cli.cjs +141 -141
- package/dist/index.cjs +197 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -44
- package/dist/index.d.ts +49 -44
- package/dist/index.js +189 -90
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +27 -1
- package/package.json +1 -1
- package/src/chart.ts +27 -0
- package/src/cli.ts +2 -2
- package/src/d3.ts +19 -24
- package/src/dgmo-router.ts +74 -20
- package/src/echarts.ts +110 -34
- package/src/index.ts +10 -10
- package/src/render.ts +8 -8
|
@@ -116,9 +116,35 @@ Multi-series: comma-separated values matching the `series` list. Single series o
|
|
|
116
116
|
|
|
117
117
|
Options: `series`, `xlabel`, `ylabel`, `labels`.
|
|
118
118
|
|
|
119
|
+
**Era bands** — annotate named time periods with background shading:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
chart: line
|
|
123
|
+
title: U.S. Strategic Petroleum Reserve
|
|
124
|
+
ylabel: Million Barrels
|
|
125
|
+
|
|
126
|
+
era '81 -> '89: Reagan (red)
|
|
127
|
+
era '89 -> '93: Bush (red)
|
|
128
|
+
era '93 -> '01: Clinton (blue)
|
|
129
|
+
|
|
130
|
+
'81: 230
|
|
131
|
+
'85: 493
|
|
132
|
+
'89: 580
|
|
133
|
+
'93: 587
|
|
134
|
+
'01: 550
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Syntax: `era <start> -> <end>: <label> [(<color>)]`
|
|
138
|
+
|
|
139
|
+
- `start` and `end` must exactly match category labels in the data
|
|
140
|
+
- Color is optional; defaults to the palette's blue
|
|
141
|
+
- Band label is hidden if the era spans fewer than 3 category slots
|
|
142
|
+
- Works on `line`, `multi-line`, and `area` charts
|
|
143
|
+
- Era boundary labels are always pinned visible on the x-axis even when auto-skip is active
|
|
144
|
+
|
|
119
145
|
### area
|
|
120
146
|
|
|
121
|
-
Same syntax as `line
|
|
147
|
+
Same syntax as `line`, including era bands. Renders as a filled area chart.
|
|
122
148
|
|
|
123
149
|
### pie
|
|
124
150
|
|
package/package.json
CHANGED
package/src/chart.ts
CHANGED
|
@@ -20,6 +20,13 @@ export interface ChartDataPoint {
|
|
|
20
20
|
lineNumber: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export interface ChartEra {
|
|
24
|
+
start: string; // exact category label, e.g. "'77"
|
|
25
|
+
end: string; // exact category label, e.g. "'81"
|
|
26
|
+
label: string; // display name, e.g. "Carter"
|
|
27
|
+
color: string | null; // resolved CSS color, or null → palette default
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
import type { DgmoError } from './diagnostics';
|
|
24
31
|
|
|
25
32
|
export interface ParsedChart {
|
|
@@ -36,6 +43,7 @@ export interface ParsedChart {
|
|
|
36
43
|
label?: string;
|
|
37
44
|
labels?: 'name' | 'value' | 'percent' | 'full';
|
|
38
45
|
data: ChartDataPoint[];
|
|
46
|
+
eras?: ChartEra[];
|
|
39
47
|
diagnostics: DgmoError[];
|
|
40
48
|
error: string | null;
|
|
41
49
|
}
|
|
@@ -87,9 +95,11 @@ export function parseChart(
|
|
|
87
95
|
palette?: PaletteColors
|
|
88
96
|
): ParsedChart {
|
|
89
97
|
const lines = content.split('\n');
|
|
98
|
+
const parsedEras: ChartEra[] = [];
|
|
90
99
|
const result: ParsedChart = {
|
|
91
100
|
type: 'bar',
|
|
92
101
|
data: [],
|
|
102
|
+
eras: parsedEras,
|
|
93
103
|
diagnostics: [],
|
|
94
104
|
error: null,
|
|
95
105
|
};
|
|
@@ -117,6 +127,18 @@ export function parseChart(
|
|
|
117
127
|
// Skip comments
|
|
118
128
|
if (trimmed.startsWith('//')) continue;
|
|
119
129
|
|
|
130
|
+
// Era line: must be matched before colon-split (era '77 -> '81: Carter has colons inside)
|
|
131
|
+
const eraMatch = trimmed.match(/^era\s+(.+?)\s*->\s*(.+?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/);
|
|
132
|
+
if (eraMatch) {
|
|
133
|
+
parsedEras.push({
|
|
134
|
+
start: eraMatch[1].trim(),
|
|
135
|
+
end: eraMatch[2].trim(),
|
|
136
|
+
label: eraMatch[3].trim(),
|
|
137
|
+
color: eraMatch[4] ? resolveColor(eraMatch[4].trim(), palette) : null,
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
120
142
|
// Parse key: value pairs
|
|
121
143
|
const colonIndex = trimmed.indexOf(':');
|
|
122
144
|
if (colonIndex === -1) continue;
|
|
@@ -214,6 +236,11 @@ export function parseChart(
|
|
|
214
236
|
}
|
|
215
237
|
}
|
|
216
238
|
|
|
239
|
+
// Eras are only valid for line, multi-line (aliased to 'line'), and area chart types
|
|
240
|
+
if (result.type !== 'line' && result.type !== 'area') {
|
|
241
|
+
result.eras = undefined;
|
|
242
|
+
}
|
|
243
|
+
|
|
217
244
|
// Validation
|
|
218
245
|
const setChartError = (line: number, message: string) => {
|
|
219
246
|
const diag = makeDgmoError(line, message);
|
package/src/cli.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { execSync } from 'node:child_process';
|
|
|
3
3
|
import { resolve, basename, extname } from 'node:path';
|
|
4
4
|
import { Resvg } from '@resvg/resvg-js';
|
|
5
5
|
import { render } from './render';
|
|
6
|
-
import { parseDgmo,
|
|
6
|
+
import { parseDgmo, getAllChartTypes } from './dgmo-router';
|
|
7
7
|
import { parseDgmoChartType } from './dgmo-router';
|
|
8
8
|
import { formatDgmoError } from './diagnostics';
|
|
9
9
|
import { getPalette } from './palettes/registry';
|
|
@@ -276,7 +276,7 @@ async function main(): Promise<void> {
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
if (opts.chartTypes) {
|
|
279
|
-
const types =
|
|
279
|
+
const types = getAllChartTypes();
|
|
280
280
|
if (opts.json) {
|
|
281
281
|
const chartTypes = types.map((id) => ({
|
|
282
282
|
id,
|
package/src/d3.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { injectBranding } from './branding';
|
|
|
10
10
|
// Types
|
|
11
11
|
// ============================================================
|
|
12
12
|
|
|
13
|
-
export type
|
|
13
|
+
export type VisualizationType =
|
|
14
14
|
| 'slope'
|
|
15
15
|
| 'wordcloud'
|
|
16
16
|
| 'arc'
|
|
@@ -136,8 +136,8 @@ export interface D3ExportDimensions {
|
|
|
136
136
|
height?: number;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export interface
|
|
140
|
-
type:
|
|
139
|
+
export interface ParsedVisualization {
|
|
140
|
+
type: VisualizationType | null;
|
|
141
141
|
title: string | null;
|
|
142
142
|
titleLineNumber: number | null;
|
|
143
143
|
orientation: 'horizontal' | 'vertical';
|
|
@@ -328,8 +328,8 @@ export function addDurationToDate(
|
|
|
328
328
|
/**
|
|
329
329
|
* Parses D3 chart text format into structured data.
|
|
330
330
|
*/
|
|
331
|
-
export function
|
|
332
|
-
const result:
|
|
331
|
+
export function parseVisualization(content: string, palette?: PaletteColors): ParsedVisualization {
|
|
332
|
+
const result: ParsedVisualization = {
|
|
333
333
|
type: null,
|
|
334
334
|
title: null,
|
|
335
335
|
titleLineNumber: null,
|
|
@@ -367,7 +367,7 @@ export function parseD3(content: string, palette?: PaletteColors): ParsedD3 {
|
|
|
367
367
|
error: null,
|
|
368
368
|
};
|
|
369
369
|
|
|
370
|
-
const fail = (line: number, message: string):
|
|
370
|
+
const fail = (line: number, message: string): ParsedVisualization => {
|
|
371
371
|
const diag = makeDgmoError(line, message);
|
|
372
372
|
result.diagnostics.push(diag);
|
|
373
373
|
result.error = formatDgmoError(diag);
|
|
@@ -1301,7 +1301,7 @@ const SLOPE_CHAR_WIDTH = 8; // approximate px per character at 14px
|
|
|
1301
1301
|
*/
|
|
1302
1302
|
export function renderSlopeChart(
|
|
1303
1303
|
container: HTMLDivElement,
|
|
1304
|
-
parsed:
|
|
1304
|
+
parsed: ParsedVisualization,
|
|
1305
1305
|
palette: PaletteColors,
|
|
1306
1306
|
isDark: boolean,
|
|
1307
1307
|
onClickItem?: (lineNumber: number) => void,
|
|
@@ -1690,7 +1690,7 @@ const ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
|
|
|
1690
1690
|
*/
|
|
1691
1691
|
export function renderArcDiagram(
|
|
1692
1692
|
container: HTMLDivElement,
|
|
1693
|
-
parsed:
|
|
1693
|
+
parsed: ParsedVisualization,
|
|
1694
1694
|
palette: PaletteColors,
|
|
1695
1695
|
_isDark: boolean,
|
|
1696
1696
|
onClickItem?: (lineNumber: number) => void,
|
|
@@ -2809,7 +2809,7 @@ function buildEraTooltipHtml(era: TimelineEra): string {
|
|
|
2809
2809
|
*/
|
|
2810
2810
|
export function renderTimeline(
|
|
2811
2811
|
container: HTMLDivElement,
|
|
2812
|
-
parsed:
|
|
2812
|
+
parsed: ParsedVisualization,
|
|
2813
2813
|
palette: PaletteColors,
|
|
2814
2814
|
isDark: boolean,
|
|
2815
2815
|
onClickItem?: (lineNumber: number) => void,
|
|
@@ -4446,7 +4446,7 @@ function getRotateFn(mode: WordCloudRotate): () => number {
|
|
|
4446
4446
|
*/
|
|
4447
4447
|
export function renderWordCloud(
|
|
4448
4448
|
container: HTMLDivElement,
|
|
4449
|
-
parsed:
|
|
4449
|
+
parsed: ParsedVisualization,
|
|
4450
4450
|
palette: PaletteColors,
|
|
4451
4451
|
_isDark: boolean,
|
|
4452
4452
|
onClickItem?: (lineNumber: number) => void,
|
|
@@ -4526,7 +4526,7 @@ export function renderWordCloud(
|
|
|
4526
4526
|
|
|
4527
4527
|
function renderWordCloudAsync(
|
|
4528
4528
|
container: HTMLDivElement,
|
|
4529
|
-
parsed:
|
|
4529
|
+
parsed: ParsedVisualization,
|
|
4530
4530
|
palette: PaletteColors,
|
|
4531
4531
|
_isDark: boolean,
|
|
4532
4532
|
exportDims?: D3ExportDimensions
|
|
@@ -4790,7 +4790,7 @@ function regionCentroid(circles: Circle[], inside: boolean[]): Point {
|
|
|
4790
4790
|
|
|
4791
4791
|
export function renderVenn(
|
|
4792
4792
|
container: HTMLDivElement,
|
|
4793
|
-
parsed:
|
|
4793
|
+
parsed: ParsedVisualization,
|
|
4794
4794
|
palette: PaletteColors,
|
|
4795
4795
|
isDark: boolean,
|
|
4796
4796
|
onClickItem?: (lineNumber: number) => void,
|
|
@@ -4890,8 +4890,6 @@ export function renderVenn(
|
|
|
4890
4890
|
.attr('fill-opacity', 0.35)
|
|
4891
4891
|
.attr('stroke', setColors[i])
|
|
4892
4892
|
.attr('stroke-width', 2)
|
|
4893
|
-
.attr('class', 'venn-fill-circle')
|
|
4894
|
-
.attr('data-line-number', String(vennSets[i].lineNumber))
|
|
4895
4893
|
.style('pointer-events', 'none') as d3Selection.Selection<
|
|
4896
4894
|
SVGCircleElement,
|
|
4897
4895
|
unknown,
|
|
@@ -4959,12 +4957,9 @@ export function renderVenn(
|
|
|
4959
4957
|
.attr('fill', 'white')
|
|
4960
4958
|
.attr('fill-opacity', 0)
|
|
4961
4959
|
.attr('class', 'venn-region-overlay')
|
|
4960
|
+
.attr('data-line-number', regionLineNumber != null ? String(regionLineNumber) : '0')
|
|
4962
4961
|
.attr('clip-path', `url(#${clipId})`);
|
|
4963
4962
|
|
|
4964
|
-
if (regionLineNumber != null) {
|
|
4965
|
-
el.attr('data-line-number', String(regionLineNumber));
|
|
4966
|
-
}
|
|
4967
|
-
|
|
4968
4963
|
if (excluded.length > 0) {
|
|
4969
4964
|
// Mask subtracts excluded circles so only the exact region shape highlights
|
|
4970
4965
|
const maskId = `vvm-${key}`;
|
|
@@ -4986,7 +4981,7 @@ export function renderVenn(
|
|
|
4986
4981
|
|
|
4987
4982
|
const showRegionOverlay = (idxs: number[]) => {
|
|
4988
4983
|
const key = [...idxs].sort((a, b) => a - b).join('-');
|
|
4989
|
-
overlayEls.forEach((el, k) => el.attr('fill-opacity', k === key ? 0
|
|
4984
|
+
overlayEls.forEach((el, k) => el.attr('fill-opacity', k === key ? 0 : 0.55));
|
|
4990
4985
|
};
|
|
4991
4986
|
const hideAllOverlays = () => {
|
|
4992
4987
|
overlayEls.forEach(el => el.attr('fill-opacity', 0));
|
|
@@ -5217,7 +5212,7 @@ type QuadrantPosition =
|
|
|
5217
5212
|
*/
|
|
5218
5213
|
export function renderQuadrant(
|
|
5219
5214
|
container: HTMLDivElement,
|
|
5220
|
-
parsed:
|
|
5215
|
+
parsed: ParsedVisualization,
|
|
5221
5216
|
palette: PaletteColors,
|
|
5222
5217
|
isDark: boolean,
|
|
5223
5218
|
onClickItem?: (lineNumber: number) => void,
|
|
@@ -5776,7 +5771,7 @@ function finalizeSvgExport(
|
|
|
5776
5771
|
* Renders a D3 chart to an SVG string for export.
|
|
5777
5772
|
* Creates a detached DOM element, renders into it, extracts the SVG, then cleans up.
|
|
5778
5773
|
*/
|
|
5779
|
-
export async function
|
|
5774
|
+
export async function renderForExport(
|
|
5780
5775
|
content: string,
|
|
5781
5776
|
theme: 'light' | 'dark' | 'transparent',
|
|
5782
5777
|
palette?: PaletteColors,
|
|
@@ -5788,7 +5783,7 @@ export async function renderD3ForExport(
|
|
|
5788
5783
|
},
|
|
5789
5784
|
options?: { branding?: boolean; c4Level?: 'context' | 'containers' | 'components' | 'deployment'; c4System?: string; c4Container?: string; scenario?: string }
|
|
5790
5785
|
): Promise<string> {
|
|
5791
|
-
// Flowchart and org chart use their own parser pipelines — intercept before
|
|
5786
|
+
// Flowchart and org chart use their own parser pipelines — intercept before parseVisualization()
|
|
5792
5787
|
const { parseDgmoChartType } = await import('./dgmo-router');
|
|
5793
5788
|
const detectedType = parseDgmoChartType(content);
|
|
5794
5789
|
|
|
@@ -6053,8 +6048,8 @@ export async function renderD3ForExport(
|
|
|
6053
6048
|
return finalizeSvgExport(container, theme, effectivePalette, options);
|
|
6054
6049
|
}
|
|
6055
6050
|
|
|
6056
|
-
const parsed =
|
|
6057
|
-
// Allow sequence diagrams through even if
|
|
6051
|
+
const parsed = parseVisualization(content, palette);
|
|
6052
|
+
// Allow sequence diagrams through even if parseVisualization errors —
|
|
6058
6053
|
// sequence is parsed by its own dedicated parser (parseSequenceDgmo)
|
|
6059
6054
|
// and may not have a "chart:" line (auto-detected from arrow syntax).
|
|
6060
6055
|
if (parsed.error && parsed.type !== 'sequence') {
|
package/src/dgmo-router.ts
CHANGED
|
@@ -8,8 +8,8 @@ import { looksLikeState, parseState } from './graph/state-parser';
|
|
|
8
8
|
import { looksLikeClassDiagram, parseClassDiagram } from './class/parser';
|
|
9
9
|
import { looksLikeERDiagram, parseERDiagram } from './er/parser';
|
|
10
10
|
import { parseChart } from './chart';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { parseExtendedChart } from './echarts';
|
|
12
|
+
import { parseVisualization } from './d3';
|
|
13
13
|
import { parseOrg, looksLikeOrg } from './org/parser';
|
|
14
14
|
import { parseKanban } from './kanban/parser';
|
|
15
15
|
import { parseC4 } from './c4/parser';
|
|
@@ -19,18 +19,15 @@ import { parseInfra } from './infra/parser';
|
|
|
19
19
|
import type { DgmoError } from './diagnostics';
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Framework identifiers used by the .dgmo router.
|
|
23
|
-
*
|
|
22
|
+
* Framework identifiers used by the .dgmo router internally.
|
|
23
|
+
* Not part of the public API — use RenderCategory instead.
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
type DgmoFramework = 'echart' | 'd3' | 'mermaid';
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Maps every supported chart type string to its backing framework.
|
|
29
|
-
*
|
|
30
|
-
* ECharts: standard chart types (bar, line, pie, etc.), scatter, flow/relationship diagrams, math, heatmap
|
|
31
|
-
* D3: slope, wordcloud, arc diagram, timeline
|
|
28
|
+
* Maps every supported chart type string to its backing framework (internal).
|
|
32
29
|
*/
|
|
33
|
-
|
|
30
|
+
const DGMO_CHART_TYPE_MAP: Record<string, DgmoFramework> = {
|
|
34
31
|
// Standard charts (via ECharts)
|
|
35
32
|
bar: 'echart',
|
|
36
33
|
line: 'echart',
|
|
@@ -71,9 +68,10 @@ export const DGMO_CHART_TYPE_MAP: Record<string, DgmoFramework> = {
|
|
|
71
68
|
};
|
|
72
69
|
|
|
73
70
|
/**
|
|
74
|
-
* Returns the framework for a given chart type, or `null` if unknown.
|
|
71
|
+
* Returns the internal framework for a given chart type, or `null` if unknown.
|
|
72
|
+
* Internal only — use getRenderCategory() for public dispatch.
|
|
75
73
|
*/
|
|
76
|
-
|
|
74
|
+
function getDgmoFramework(chartType: string): DgmoFramework | null {
|
|
77
75
|
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
78
76
|
}
|
|
79
77
|
|
|
@@ -107,13 +105,69 @@ export function parseDgmoChartType(content: string): string | null {
|
|
|
107
105
|
return null;
|
|
108
106
|
}
|
|
109
107
|
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
// ============================================================
|
|
109
|
+
// Public render-category API
|
|
110
|
+
// ============================================================
|
|
111
|
+
|
|
112
|
+
/** User-visible rendering category for dispatch and routing. */
|
|
113
|
+
export type RenderCategory = 'data-chart' | 'visualization' | 'diagram';
|
|
114
|
+
|
|
115
|
+
const DATA_CHART_TYPES = new Set([
|
|
116
|
+
'bar', 'line', 'pie', 'doughnut', 'area', 'polar-area', 'radar',
|
|
117
|
+
'bar-stacked', 'multi-line', 'scatter', 'sankey', 'chord', 'function',
|
|
118
|
+
'heatmap', 'funnel',
|
|
119
|
+
]);
|
|
120
|
+
const VISUALIZATION_TYPES = new Set([
|
|
121
|
+
'slope', 'wordcloud', 'arc', 'timeline', 'venn', 'quadrant',
|
|
122
|
+
]);
|
|
123
|
+
const DIAGRAM_TYPES = new Set([
|
|
124
|
+
'sequence', 'flowchart', 'class', 'er', 'org', 'kanban', 'c4',
|
|
125
|
+
'initiative-status', 'state', 'sitemap', 'infra',
|
|
126
|
+
]);
|
|
127
|
+
const EXTENDED_CHART_TYPES = new Set([
|
|
128
|
+
'scatter', 'sankey', 'chord', 'function', 'heatmap', 'funnel',
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Returns the render category for a given chart type, or `null` if unknown.
|
|
133
|
+
* Use this instead of the internal framework map for dispatch in consumers.
|
|
134
|
+
*/
|
|
135
|
+
export function getRenderCategory(chartType: string): RenderCategory | null {
|
|
136
|
+
const type = chartType.toLowerCase();
|
|
137
|
+
if (DATA_CHART_TYPES.has(type)) return 'data-chart';
|
|
138
|
+
if (VISUALIZATION_TYPES.has(type)) return 'visualization';
|
|
139
|
+
if (DIAGRAM_TYPES.has(type)) return 'diagram';
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Returns true if the chart type is an extended chart type
|
|
145
|
+
* handled by parseExtendedChart (scatter, sankey, chord, function, heatmap, funnel).
|
|
146
|
+
* Returns false for standard chart types and all other types.
|
|
147
|
+
*/
|
|
148
|
+
export function isExtendedChartType(chartType: string): boolean {
|
|
149
|
+
return EXTENDED_CHART_TYPES.has(chartType.toLowerCase());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Standard chart types parsed by parseChart (then rendered via ECharts). Internal use. */
|
|
153
|
+
const STANDARD_CHART_TYPES = new Set([
|
|
112
154
|
'bar', 'line', 'multi-line', 'area', 'pie', 'doughnut',
|
|
113
155
|
'radar', 'polar-area', 'bar-stacked',
|
|
114
156
|
]);
|
|
115
157
|
|
|
116
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Returns all supported chart type identifiers.
|
|
160
|
+
* Useful for CLI enumeration and autocomplete.
|
|
161
|
+
*/
|
|
162
|
+
export function getAllChartTypes(): string[] {
|
|
163
|
+
return [
|
|
164
|
+
...DATA_CHART_TYPES,
|
|
165
|
+
...VISUALIZATION_TYPES,
|
|
166
|
+
...DIAGRAM_TYPES,
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ECharts-native types parsed by parseExtendedChart
|
|
117
171
|
const ECHART_TYPES = new Set([
|
|
118
172
|
'scatter', 'sankey', 'chord', 'function', 'heatmap', 'funnel',
|
|
119
173
|
]);
|
|
@@ -141,8 +195,8 @@ export function parseDgmo(content: string): { diagnostics: DgmoError[] } {
|
|
|
141
195
|
const chartType = parseDgmoChartType(content);
|
|
142
196
|
|
|
143
197
|
if (!chartType) {
|
|
144
|
-
// No chart type detected — try
|
|
145
|
-
return { diagnostics:
|
|
198
|
+
// No chart type detected — try visualization parser as fallback (it handles missing chart: line)
|
|
199
|
+
return { diagnostics: parseVisualization(content).diagnostics };
|
|
146
200
|
}
|
|
147
201
|
|
|
148
202
|
const directParser = PARSE_DISPATCH.get(chartType);
|
|
@@ -152,9 +206,9 @@ export function parseDgmo(content: string): { diagnostics: DgmoError[] } {
|
|
|
152
206
|
return { diagnostics: parseChart(content).diagnostics };
|
|
153
207
|
}
|
|
154
208
|
if (ECHART_TYPES.has(chartType)) {
|
|
155
|
-
return { diagnostics:
|
|
209
|
+
return { diagnostics: parseExtendedChart(content).diagnostics };
|
|
156
210
|
}
|
|
157
211
|
|
|
158
|
-
//
|
|
159
|
-
return { diagnostics:
|
|
212
|
+
// Visualization types (slope, wordcloud, arc, timeline, venn, quadrant)
|
|
213
|
+
return { diagnostics: parseVisualization(content).diagnostics };
|
|
160
214
|
}
|