@diagrammo/dgmo 0.8.22 → 0.8.23
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/cli.cjs +111 -109
- package/dist/editor.cjs +3 -0
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +3 -0
- package/dist/editor.js.map +1 -1
- package/dist/highlight.cjs +3 -0
- package/dist/highlight.cjs.map +1 -1
- package/dist/highlight.js +3 -0
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +1010 -215
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -11
- package/dist/index.d.ts +97 -11
- package/dist/index.js +1001 -213
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +380 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +179 -0
- package/dist/internal.d.ts +179 -0
- package/dist/internal.js +337 -0
- package/dist/internal.js.map +1 -0
- package/docs/guide/chart-cycle.md +156 -0
- package/docs/guide/chart-journey-map.md +179 -0
- package/docs/guide/chart-pyramid.md +111 -0
- package/docs/guide/registry.json +5 -0
- package/docs/language-reference.md +62 -1
- package/gallery/fixtures/pyramid/dikw.dgmo +17 -0
- package/gallery/fixtures/pyramid/inverted-funnel.dgmo +16 -0
- package/gallery/fixtures/pyramid/minimal.dgmo +5 -0
- package/package.json +11 -1
- package/src/cli.ts +5 -35
- package/src/completion.ts +9 -44
- package/src/cycle/layout.ts +19 -28
- package/src/cycle/renderer.ts +59 -32
- package/src/cycle/types.ts +21 -0
- package/src/d3.ts +21 -1
- package/src/dgmo-router.ts +73 -3
- package/src/echarts.ts +1 -1
- package/src/editor/keywords.ts +3 -0
- package/src/index.ts +13 -2
- package/src/infra/parser.ts +2 -2
- package/src/internal.ts +16 -0
- package/src/journey-map/renderer.ts +112 -47
- package/src/org/collapse.ts +81 -0
- package/src/org/renderer.ts +212 -4
- package/src/pyramid/parser.ts +172 -0
- package/src/pyramid/renderer.ts +684 -0
- package/src/pyramid/types.ts +28 -0
- package/src/render.ts +2 -8
- package/src/sequence/parser.ts +62 -20
- package/src/sequence/renderer.ts +2 -2
- package/src/tech-radar/interactive.ts +54 -0
- package/src/utils/parsing.ts +1 -0
package/src/completion.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { extractSymbols as extractFlowchartSymbols } from './graph/flowchart-par
|
|
|
16
16
|
import { extractSymbols as extractInfraSymbols } from './infra/parser';
|
|
17
17
|
import { extractSymbols as extractClassSymbols } from './class/parser';
|
|
18
18
|
import { parseFirstLine, ALL_CHART_TYPES } from './utils/parsing';
|
|
19
|
+
import { CHART_TYPE_DESCRIPTIONS } from './dgmo-router';
|
|
19
20
|
|
|
20
21
|
// ============================================================
|
|
21
22
|
// Symbol extraction
|
|
@@ -381,56 +382,20 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
381
382
|
persona: { description: 'Define the journey persona' },
|
|
382
383
|
}),
|
|
383
384
|
],
|
|
385
|
+
[
|
|
386
|
+
'pyramid',
|
|
387
|
+
withGlobals({
|
|
388
|
+
inverted: { description: 'Flip apex to the bottom (funnel orientation)' },
|
|
389
|
+
color: { description: 'Override layer color (pipe metadata)' },
|
|
390
|
+
description: { description: 'Layer description (pipe or indented body)' },
|
|
391
|
+
}),
|
|
392
|
+
],
|
|
384
393
|
]);
|
|
385
394
|
|
|
386
395
|
// ============================================================
|
|
387
396
|
// Chart types array (for chart type completion popup)
|
|
388
397
|
// ============================================================
|
|
389
398
|
|
|
390
|
-
const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
391
|
-
// Data charts
|
|
392
|
-
bar: 'Bar chart',
|
|
393
|
-
line: 'Line chart',
|
|
394
|
-
pie: 'Pie chart',
|
|
395
|
-
doughnut: 'Doughnut chart',
|
|
396
|
-
area: 'Area chart',
|
|
397
|
-
'polar-area': 'Polar area chart',
|
|
398
|
-
radar: 'Radar chart',
|
|
399
|
-
'bar-stacked': 'Stacked bar chart',
|
|
400
|
-
// Extended charts
|
|
401
|
-
scatter: 'Scatter plot',
|
|
402
|
-
heatmap: 'Heatmap',
|
|
403
|
-
sankey: 'Sankey flow diagram',
|
|
404
|
-
chord: 'Chord diagram',
|
|
405
|
-
funnel: 'Funnel chart',
|
|
406
|
-
function: 'Mathematical function plot',
|
|
407
|
-
// Visualizations
|
|
408
|
-
slope: 'Slope chart',
|
|
409
|
-
wordcloud: 'Word cloud',
|
|
410
|
-
arc: 'Arc diagram',
|
|
411
|
-
timeline: 'Timeline',
|
|
412
|
-
venn: 'Venn diagram',
|
|
413
|
-
quadrant: 'Quadrant chart',
|
|
414
|
-
// Diagrams
|
|
415
|
-
sequence: 'Sequence diagram',
|
|
416
|
-
flowchart: 'Flowchart',
|
|
417
|
-
class: 'Class diagram',
|
|
418
|
-
er: 'Entity-relationship diagram',
|
|
419
|
-
org: 'Organization chart',
|
|
420
|
-
kanban: 'Kanban board',
|
|
421
|
-
c4: 'C4 architecture diagram',
|
|
422
|
-
state: 'State diagram',
|
|
423
|
-
sitemap: 'Sitemap diagram',
|
|
424
|
-
infra: 'Infrastructure diagram',
|
|
425
|
-
gantt: 'Gantt chart',
|
|
426
|
-
'boxes-and-lines': 'Boxes and lines diagram',
|
|
427
|
-
mindmap: 'Mindmap diagram',
|
|
428
|
-
wireframe: 'UI wireframe diagram',
|
|
429
|
-
'tech-radar': 'Technology adoption radar (ThoughtWorks style)',
|
|
430
|
-
cycle: 'Cycle diagram (circular process flow)',
|
|
431
|
-
'journey-map': 'User journey map with emotion curve',
|
|
432
|
-
};
|
|
433
|
-
|
|
434
399
|
/** All chart types with descriptions, for chart type autocomplete. Excludes `multi-line` alias. */
|
|
435
400
|
export const CHART_TYPES: ReadonlyArray<{ name: string; description: string }> =
|
|
436
401
|
[...ALL_CHART_TYPES]
|
package/src/cycle/layout.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Cycle Diagram — Layout Engine
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_EDGE_WIDTH,
|
|
7
|
+
MIN_EDGE_WIDTH,
|
|
8
|
+
arrowHeadLength,
|
|
9
|
+
type ParsedCycle,
|
|
10
|
+
type CycleLayoutNode,
|
|
11
|
+
type CycleLayoutEdge,
|
|
12
|
+
type CycleLayoutResult,
|
|
10
13
|
} from './types';
|
|
11
14
|
|
|
12
15
|
/** Minimum arc angle in radians (~15°) to keep arcs readable. */
|
|
@@ -519,11 +522,6 @@ function circleRectExitAngle(
|
|
|
519
522
|
return (insideAngle + outsideAngle) / 2;
|
|
520
523
|
}
|
|
521
524
|
|
|
522
|
-
/** Default edge stroke width (must match renderer). */
|
|
523
|
-
const DEFAULT_EDGE_WIDTH = 3;
|
|
524
|
-
/** Arrowhead marker width in stroke-width units (must match renderer). */
|
|
525
|
-
const ARROWHEAD_MARKER_W = 8;
|
|
526
|
-
|
|
527
525
|
/** Compute edge paths for all edges in the parsed diagram. */
|
|
528
526
|
function computeEdgePaths(
|
|
529
527
|
layoutNodes: CycleLayoutNode[],
|
|
@@ -536,9 +534,13 @@ function computeEdgePaths(
|
|
|
536
534
|
return parsed.edges.map((edge) => {
|
|
537
535
|
const src = layoutNodes[edge.sourceIndex];
|
|
538
536
|
const tgt = layoutNodes[edge.targetIndex];
|
|
539
|
-
const strokeWidth =
|
|
540
|
-
|
|
541
|
-
|
|
537
|
+
const strokeWidth = Math.max(
|
|
538
|
+
edge.width ?? DEFAULT_EDGE_WIDTH,
|
|
539
|
+
MIN_EDGE_WIDTH
|
|
540
|
+
);
|
|
541
|
+
// Arrowhead effective reach: full length minus the 10% overlap that
|
|
542
|
+
// slides the marker back to cover the stroke/arrowhead junction line.
|
|
543
|
+
const arrowLen = arrowHeadLength(strokeWidth) * 0.9;
|
|
542
544
|
const { path, labelX, labelY, labelAngle } = buildEdgeArc(
|
|
543
545
|
src,
|
|
544
546
|
tgt,
|
|
@@ -578,7 +580,7 @@ function fitToCanvas(
|
|
|
578
580
|
height: number,
|
|
579
581
|
_isClockwise: boolean
|
|
580
582
|
): { radius: number } | null {
|
|
581
|
-
const PADDING =
|
|
583
|
+
const PADDING = 30;
|
|
582
584
|
let contentMinX = Infinity,
|
|
583
585
|
contentMaxX = -Infinity;
|
|
584
586
|
let contentMinY = Infinity,
|
|
@@ -672,20 +674,9 @@ function buildEdgeArc(
|
|
|
672
674
|
): { path: string; labelX: number; labelY: number; labelAngle: number } {
|
|
673
675
|
const dir = isClockwise ? 1 : -1;
|
|
674
676
|
|
|
675
|
-
//
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
: circleRectExitAngle(
|
|
679
|
-
src.x,
|
|
680
|
-
src.y,
|
|
681
|
-
src.width / 2,
|
|
682
|
-
src.height / 2,
|
|
683
|
-
cx,
|
|
684
|
-
cy,
|
|
685
|
-
radius,
|
|
686
|
-
src.angle,
|
|
687
|
-
dir
|
|
688
|
-
);
|
|
677
|
+
// Start arc from the source node's center angle — the node renders on top
|
|
678
|
+
// of the edge, so the overlap is hidden and there's no visible gap.
|
|
679
|
+
const startAngle = src.angle;
|
|
689
680
|
|
|
690
681
|
// Find where the cycle circle exits the target node
|
|
691
682
|
const nodeEndAngle = tgt.isCircle
|
package/src/cycle/renderer.ts
CHANGED
|
@@ -23,13 +23,15 @@ import { renderInlineText } from '../utils/inline-markdown';
|
|
|
23
23
|
import type { PaletteColors } from '../palettes';
|
|
24
24
|
import type { D3ExportDimensions } from '../utils/d3-types';
|
|
25
25
|
import type { CompactViewState } from '../sharing';
|
|
26
|
-
import
|
|
26
|
+
import {
|
|
27
|
+
DEFAULT_EDGE_WIDTH,
|
|
28
|
+
MIN_EDGE_WIDTH,
|
|
29
|
+
arrowHeadLength,
|
|
30
|
+
type ParsedCycle,
|
|
31
|
+
} from './types';
|
|
27
32
|
import { computeCycleLayout } from './layout';
|
|
28
33
|
|
|
29
34
|
// ── Constants ────────────────────────────────────────────────
|
|
30
|
-
const DEFAULT_EDGE_WIDTH = 3;
|
|
31
|
-
const ARROWHEAD_W = 8;
|
|
32
|
-
const ARROWHEAD_H = 8;
|
|
33
35
|
const NODE_FONT_SIZE = 13;
|
|
34
36
|
const DESC_FONT_SIZE = 11;
|
|
35
37
|
const EDGE_LABEL_FONT_SIZE = 11;
|
|
@@ -183,22 +185,28 @@ export function renderCycle(
|
|
|
183
185
|
// Resolve default node color: first palette color (uniform)
|
|
184
186
|
const defaultNodeColor = palette.primary;
|
|
185
187
|
|
|
186
|
-
// ── Arrowhead markers ──
|
|
187
|
-
const
|
|
188
|
-
for (
|
|
189
|
-
const edge = parsed.edges[i];
|
|
188
|
+
// ── Arrowhead markers (per color+width, markerUnits=strokeWidth) ──
|
|
189
|
+
const markerKeys = new Set<string>();
|
|
190
|
+
for (const edge of parsed.edges) {
|
|
190
191
|
const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
|
|
191
|
-
|
|
192
|
+
const sw = Math.max(edge.width ?? DEFAULT_EDGE_WIDTH, MIN_EDGE_WIDTH);
|
|
193
|
+
const key = `${color}|${sw}`;
|
|
194
|
+
if (!markerKeys.has(key)) {
|
|
195
|
+
markerKeys.add(key);
|
|
196
|
+
ensureArrowMarker(defs, color, sw);
|
|
197
|
+
}
|
|
192
198
|
}
|
|
193
|
-
ensureArrowMarkers(defs, arrowColors);
|
|
194
199
|
|
|
195
200
|
// ── Render edges (below nodes) ──
|
|
196
201
|
for (let i = 0; i < layout.edges.length; i++) {
|
|
197
202
|
const le = layout.edges[i];
|
|
198
203
|
const edge = parsed.edges[i];
|
|
199
204
|
const color = resolveEdgeColor(edge, parsed, palette, defaultNodeColor);
|
|
200
|
-
const strokeWidth =
|
|
201
|
-
|
|
205
|
+
const strokeWidth = Math.max(
|
|
206
|
+
edge.width ?? DEFAULT_EDGE_WIDTH,
|
|
207
|
+
MIN_EDGE_WIDTH
|
|
208
|
+
);
|
|
209
|
+
const markerId = arrowMarkerId(color, strokeWidth);
|
|
202
210
|
|
|
203
211
|
const edgeG = g.append('g').attr('class', 'cycle-edge');
|
|
204
212
|
|
|
@@ -514,26 +522,45 @@ function resolveEdgeColor(
|
|
|
514
522
|
return defaultNodeColor;
|
|
515
523
|
}
|
|
516
524
|
|
|
517
|
-
|
|
525
|
+
/** Stable marker ID for a (color, strokeWidth) pair. */
|
|
526
|
+
function arrowMarkerId(color: string, strokeWidth: number): string {
|
|
527
|
+
return `cycle-arrow-${color.replace('#', '')}-w${strokeWidth}`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Create an arrowhead marker using markerUnits="strokeWidth" (SVG default)
|
|
532
|
+
* with per-edge dimensions. The marker base automatically equals the stroke
|
|
533
|
+
* width — no gaps or lollipop effects. Marker dimensions are computed so
|
|
534
|
+
* the rendered arrowhead length follows a sublinear formula:
|
|
535
|
+
*
|
|
536
|
+
* rendered length = markerWidth × strokeWidth = arrowHeadLength(sw)
|
|
537
|
+
* → markerWidth = arrowHeadLength(sw) / sw
|
|
538
|
+
*
|
|
539
|
+
* The height is fixed at 1 strokeWidth unit so the base = stroke width.
|
|
540
|
+
*/
|
|
541
|
+
function ensureArrowMarker(
|
|
518
542
|
defs: d3Selection.Selection<SVGDefsElement, unknown, null, undefined>,
|
|
519
|
-
|
|
543
|
+
color: string,
|
|
544
|
+
strokeWidth: number
|
|
520
545
|
): void {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
546
|
+
const id = arrowMarkerId(color, strokeWidth);
|
|
547
|
+
// Marker dimensions in strokeWidth units.
|
|
548
|
+
// Rendered size = mw × sw (length) and mh × sw (height).
|
|
549
|
+
const mw = arrowHeadLength(strokeWidth) / strokeWidth;
|
|
550
|
+
// Height proportional to length (½ ratio) but at least 1.5× stroke width
|
|
551
|
+
// so the arrowhead is always visibly wider than the stroke.
|
|
552
|
+
const mh = Math.max(1.5, mw * 0.5);
|
|
553
|
+
|
|
554
|
+
defs
|
|
555
|
+
.append('marker')
|
|
556
|
+
.attr('id', id)
|
|
557
|
+
.attr('viewBox', `0 0 ${mw} ${mh}`)
|
|
558
|
+
.attr('refX', mw * 0.1)
|
|
559
|
+
.attr('refY', mh / 2)
|
|
560
|
+
.attr('markerWidth', mw)
|
|
561
|
+
.attr('markerHeight', mh)
|
|
562
|
+
.attr('orient', 'auto')
|
|
563
|
+
.append('polygon')
|
|
564
|
+
.attr('points', `0,0 ${mw},${mh / 2} 0,${mh}`)
|
|
565
|
+
.attr('fill', color);
|
|
539
566
|
}
|
package/src/cycle/types.ts
CHANGED
|
@@ -64,6 +64,27 @@ export interface CycleLayoutEdge {
|
|
|
64
64
|
label?: string;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// ============================================================
|
|
68
|
+
// Shared arrow-sizing helpers (used by both layout + renderer)
|
|
69
|
+
// ============================================================
|
|
70
|
+
|
|
71
|
+
/** Default edge stroke width. */
|
|
72
|
+
export const DEFAULT_EDGE_WIDTH = 3;
|
|
73
|
+
/** Minimum rendered stroke width — thinner strokes produce unusable arrowheads. */
|
|
74
|
+
export const MIN_EDGE_WIDTH = 2;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Compute the desired arrowhead length in user-space pixels using sublinear
|
|
78
|
+
* scaling. The renderer uses markerUnits="strokeWidth" with computed marker
|
|
79
|
+
* dimensions so the arrowhead base always matches the stroke width (no gaps,
|
|
80
|
+
* no lollipop effect) while the rendered length follows this formula.
|
|
81
|
+
*/
|
|
82
|
+
const BASE_ARROW_SIZE = 8;
|
|
83
|
+
const ARROW_SCALE = 6;
|
|
84
|
+
export function arrowHeadLength(strokeWidth: number): number {
|
|
85
|
+
return BASE_ARROW_SIZE + ARROW_SCALE * Math.sqrt(strokeWidth);
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
export interface CycleLayoutResult {
|
|
68
89
|
nodes: CycleLayoutNode[];
|
|
69
90
|
edges: CycleLayoutEdge[];
|
package/src/d3.ts
CHANGED
|
@@ -21,7 +21,8 @@ export type VisualizationType =
|
|
|
21
21
|
| 'quadrant'
|
|
22
22
|
| 'sequence'
|
|
23
23
|
| 'tech-radar'
|
|
24
|
-
| 'cycle'
|
|
24
|
+
| 'cycle'
|
|
25
|
+
| 'pyramid';
|
|
25
26
|
|
|
26
27
|
interface D3DataItem {
|
|
27
28
|
label: string;
|
|
@@ -7120,6 +7121,25 @@ export async function renderForExport(
|
|
|
7120
7121
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
7121
7122
|
}
|
|
7122
7123
|
|
|
7124
|
+
if (detectedType === 'pyramid') {
|
|
7125
|
+
const { parsePyramid } = await import('./pyramid/parser');
|
|
7126
|
+
const { renderPyramidForExport } = await import('./pyramid/renderer');
|
|
7127
|
+
|
|
7128
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
7129
|
+
const pyramidParsed = parsePyramid(content);
|
|
7130
|
+
if (pyramidParsed.error || pyramidParsed.layers.length === 0) return '';
|
|
7131
|
+
|
|
7132
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
7133
|
+
renderPyramidForExport(
|
|
7134
|
+
container,
|
|
7135
|
+
pyramidParsed,
|
|
7136
|
+
effectivePalette,
|
|
7137
|
+
theme === 'dark',
|
|
7138
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
7139
|
+
);
|
|
7140
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
7141
|
+
}
|
|
7142
|
+
|
|
7123
7143
|
const parsed = parseVisualization(content, palette);
|
|
7124
7144
|
// Allow sequence diagrams through even if parseVisualization errors —
|
|
7125
7145
|
// sequence is parsed by its own dedicated parser (parseSequenceDgmo)
|
package/src/dgmo-router.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { parseWireframe } from './wireframe/parser';
|
|
|
22
22
|
import { parseTechRadar } from './tech-radar/parser';
|
|
23
23
|
import { parseCycle } from './cycle/parser';
|
|
24
24
|
import { parseJourneyMap } from './journey-map/parser';
|
|
25
|
+
import { parsePyramid } from './pyramid/parser';
|
|
25
26
|
import { parseFirstLine } from './utils/parsing';
|
|
26
27
|
import { makeDgmoError, suggest } from './diagnostics';
|
|
27
28
|
import type { DgmoError } from './diagnostics';
|
|
@@ -142,6 +143,7 @@ const VISUALIZATION_TYPES = new Set([
|
|
|
142
143
|
'quadrant',
|
|
143
144
|
'tech-radar',
|
|
144
145
|
'cycle',
|
|
146
|
+
'pyramid',
|
|
145
147
|
]);
|
|
146
148
|
const DIAGRAM_TYPES = new Set([
|
|
147
149
|
'sequence',
|
|
@@ -211,6 +213,63 @@ export function getAllChartTypes(): string[] {
|
|
|
211
213
|
return [...DATA_CHART_TYPES, ...VISUALIZATION_TYPES, ...DIAGRAM_TYPES];
|
|
212
214
|
}
|
|
213
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Canonical descriptions for every supported chart type. Shared by the CLI
|
|
218
|
+
* `--chart-types` flag, the editor autocomplete popup, and the MCP
|
|
219
|
+
* `list_chart_types` tool so all three surfaces stay in sync.
|
|
220
|
+
*/
|
|
221
|
+
export const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
222
|
+
bar: 'Bar chart — categorical comparisons',
|
|
223
|
+
line: 'Line chart — trends over time; supports era bands (era start -> end Label (color)) for annotating named periods',
|
|
224
|
+
'multi-line':
|
|
225
|
+
'Multi-line chart — multiple series trends over time; supports era bands',
|
|
226
|
+
area: 'Area chart — filled line chart; supports era bands',
|
|
227
|
+
pie: 'Pie chart — part-to-whole proportions',
|
|
228
|
+
doughnut: 'Doughnut chart — ring-style pie chart',
|
|
229
|
+
radar: 'Radar chart — multi-dimensional metrics',
|
|
230
|
+
'polar-area': 'Polar area chart — radial bar chart',
|
|
231
|
+
'bar-stacked': 'Stacked bar chart — multi-series categorical',
|
|
232
|
+
scatter: 'Scatter plot — 2D data points or bubble chart',
|
|
233
|
+
sankey: 'Sankey diagram — flow/allocation visualization',
|
|
234
|
+
chord: 'Chord diagram — circular flow relationships',
|
|
235
|
+
function: 'Function plot — mathematical expressions',
|
|
236
|
+
heatmap: 'Heatmap — matrix intensity visualization',
|
|
237
|
+
funnel: 'Funnel chart — conversion pipeline',
|
|
238
|
+
slope: 'Slope chart — change between two periods',
|
|
239
|
+
wordcloud: 'Word cloud — term frequency visualization',
|
|
240
|
+
arc: 'Arc diagram — network relationships',
|
|
241
|
+
timeline: 'Timeline — events, eras, and date ranges',
|
|
242
|
+
venn: 'Venn diagram — set overlaps',
|
|
243
|
+
quadrant: 'Quadrant chart — 2x2 positioning matrix',
|
|
244
|
+
'tech-radar':
|
|
245
|
+
'Tech radar — technology adoption quadrants (adopt/trial/assess/hold)',
|
|
246
|
+
cycle:
|
|
247
|
+
'Cycle diagram — cyclical process visualization (PDCA, OODA, DevOps loops)',
|
|
248
|
+
sequence: 'Sequence diagram — message/interaction flows',
|
|
249
|
+
flowchart: 'Flowchart — decision trees and process flows',
|
|
250
|
+
class: 'Class diagram — UML class hierarchies',
|
|
251
|
+
er: 'ER diagram — database schemas and relationships',
|
|
252
|
+
org: 'Org chart — hierarchical tree structures',
|
|
253
|
+
kanban: 'Kanban board — task/workflow columns',
|
|
254
|
+
c4: 'C4 diagram — system architecture (context, container, component, deployment)',
|
|
255
|
+
state: 'State diagram — state machine / lifecycle transitions',
|
|
256
|
+
sitemap:
|
|
257
|
+
'Sitemap — navigable UI structure with pages, groups, and cross-link arrows',
|
|
258
|
+
infra:
|
|
259
|
+
'Infrastructure diagram — traffic flow with RPS computation, capacity modeling, and latency analysis',
|
|
260
|
+
gantt:
|
|
261
|
+
'Gantt chart — project scheduling with task dependencies and milestones',
|
|
262
|
+
'boxes-and-lines':
|
|
263
|
+
'Boxes and lines — general-purpose node-edge diagrams with nested groups, tags, and shape inference',
|
|
264
|
+
mindmap: 'Mindmap — radial hierarchy of ideas branching from a central topic',
|
|
265
|
+
wireframe:
|
|
266
|
+
'Wireframe — low-fidelity UI layout with panels, controls, and annotations',
|
|
267
|
+
'journey-map':
|
|
268
|
+
'Journey map — user experience flow with emotion scores, phases, and annotations',
|
|
269
|
+
pyramid:
|
|
270
|
+
'Pyramid — hierarchical layered pyramid (Maslow, DIKW, learning pyramid); inverted for funnel-of-learning style',
|
|
271
|
+
};
|
|
272
|
+
|
|
214
273
|
// ECharts-native types parsed by parseExtendedChart
|
|
215
274
|
const ECHART_TYPES = new Set([
|
|
216
275
|
'scatter',
|
|
@@ -243,6 +302,7 @@ const PARSE_DISPATCH = new Map<
|
|
|
243
302
|
['tech-radar', (c) => parseTechRadar(c)],
|
|
244
303
|
['cycle', (c) => parseCycle(c)],
|
|
245
304
|
['journey-map', (c) => parseJourneyMap(c)],
|
|
305
|
+
['pyramid', (c) => parsePyramid(c)],
|
|
246
306
|
]);
|
|
247
307
|
|
|
248
308
|
/**
|
|
@@ -260,7 +320,10 @@ const ALL_KNOWN_TYPES = new Set([
|
|
|
260
320
|
* Parse DGMO content and return diagnostics without rendering.
|
|
261
321
|
* Useful for the CLI and editor to surface all errors before attempting render.
|
|
262
322
|
*/
|
|
263
|
-
export function parseDgmo(content: string): {
|
|
323
|
+
export function parseDgmo(content: string): {
|
|
324
|
+
diagnostics: DgmoError[];
|
|
325
|
+
chartType: string | null;
|
|
326
|
+
} {
|
|
264
327
|
const chartType = parseDgmoChartType(content);
|
|
265
328
|
|
|
266
329
|
if (!chartType) {
|
|
@@ -268,11 +331,14 @@ export function parseDgmo(content: string): { diagnostics: DgmoError[] } {
|
|
|
268
331
|
const colonDiag = detectColonChartType(content);
|
|
269
332
|
if (colonDiag) {
|
|
270
333
|
const fallback = parseVisualization(content).diagnostics;
|
|
271
|
-
return { diagnostics: [colonDiag, ...fallback] };
|
|
334
|
+
return { diagnostics: [colonDiag, ...fallback], chartType: null };
|
|
272
335
|
}
|
|
273
336
|
|
|
274
337
|
// No chart type detected — try visualization parser as fallback
|
|
275
|
-
return {
|
|
338
|
+
return {
|
|
339
|
+
diagnostics: parseVisualization(content).diagnostics,
|
|
340
|
+
chartType: null,
|
|
341
|
+
};
|
|
276
342
|
}
|
|
277
343
|
|
|
278
344
|
const directParser = PARSE_DISPATCH.get(chartType);
|
|
@@ -280,6 +346,7 @@ export function parseDgmo(content: string): { diagnostics: DgmoError[] } {
|
|
|
280
346
|
const result = directParser(content);
|
|
281
347
|
return {
|
|
282
348
|
diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
|
|
349
|
+
chartType,
|
|
283
350
|
};
|
|
284
351
|
}
|
|
285
352
|
|
|
@@ -287,12 +354,14 @@ export function parseDgmo(content: string): { diagnostics: DgmoError[] } {
|
|
|
287
354
|
const result = parseChart(content);
|
|
288
355
|
return {
|
|
289
356
|
diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
|
|
357
|
+
chartType,
|
|
290
358
|
};
|
|
291
359
|
}
|
|
292
360
|
if (ECHART_TYPES.has(chartType)) {
|
|
293
361
|
const result = parseExtendedChart(content);
|
|
294
362
|
return {
|
|
295
363
|
diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
|
|
364
|
+
chartType,
|
|
296
365
|
};
|
|
297
366
|
}
|
|
298
367
|
|
|
@@ -300,6 +369,7 @@ export function parseDgmo(content: string): { diagnostics: DgmoError[] } {
|
|
|
300
369
|
const result = parseVisualization(content);
|
|
301
370
|
return {
|
|
302
371
|
diagnostics: [...result.diagnostics, ...detectEmptyContent(content)],
|
|
372
|
+
chartType,
|
|
303
373
|
};
|
|
304
374
|
}
|
|
305
375
|
|
package/src/echarts.ts
CHANGED
package/src/editor/keywords.ts
CHANGED
|
@@ -17,6 +17,7 @@ export const CHART_TYPES = new Set([
|
|
|
17
17
|
'tech-radar',
|
|
18
18
|
'mindmap',
|
|
19
19
|
'journey-map',
|
|
20
|
+
'pyramid',
|
|
20
21
|
// Data chart types
|
|
21
22
|
'bar',
|
|
22
23
|
'line',
|
|
@@ -158,6 +159,8 @@ export const DIRECTIVE_KEYWORDS = new Set([
|
|
|
158
159
|
// Layout
|
|
159
160
|
'direction-tb',
|
|
160
161
|
'direction-lr',
|
|
162
|
+
// Pyramid
|
|
163
|
+
'inverted',
|
|
161
164
|
// Data chart metadata
|
|
162
165
|
'title',
|
|
163
166
|
'series',
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,8 @@ export {
|
|
|
32
32
|
parseDgmo,
|
|
33
33
|
getRenderCategory,
|
|
34
34
|
isExtendedChartType,
|
|
35
|
+
getAllChartTypes,
|
|
36
|
+
CHART_TYPE_DESCRIPTIONS,
|
|
35
37
|
} from './dgmo-router';
|
|
36
38
|
export type { RenderCategory } from './dgmo-router';
|
|
37
39
|
|
|
@@ -63,6 +65,7 @@ export type {
|
|
|
63
65
|
|
|
64
66
|
export {
|
|
65
67
|
parseSequenceDgmo,
|
|
68
|
+
parseSequenceDgmo as parseSequenceDiagram,
|
|
66
69
|
looksLikeSequence,
|
|
67
70
|
isSequenceBlock,
|
|
68
71
|
isSequenceNote,
|
|
@@ -331,8 +334,12 @@ export type {
|
|
|
331
334
|
ResolvedGroup,
|
|
332
335
|
} from './gantt/types';
|
|
333
336
|
|
|
334
|
-
export { collapseOrgTree } from './org/collapse';
|
|
335
|
-
export type {
|
|
337
|
+
export { collapseOrgTree, focusOrgTree } from './org/collapse';
|
|
338
|
+
export type {
|
|
339
|
+
CollapsedOrgResult,
|
|
340
|
+
FocusOrgResult,
|
|
341
|
+
AncestorInfo,
|
|
342
|
+
} from './org/collapse';
|
|
336
343
|
|
|
337
344
|
export { parseMindmap } from './mindmap/parser';
|
|
338
345
|
export type {
|
|
@@ -407,6 +414,10 @@ export type {
|
|
|
407
414
|
} from './journey-map/types';
|
|
408
415
|
export type { JourneyMapInteractiveOptions } from './journey-map/renderer';
|
|
409
416
|
|
|
417
|
+
export { parsePyramid } from './pyramid/parser';
|
|
418
|
+
export { renderPyramid, renderPyramidForExport } from './pyramid/renderer';
|
|
419
|
+
export type { ParsedPyramid, PyramidLayer } from './pyramid/types';
|
|
420
|
+
|
|
410
421
|
export { resolveOrgImports } from './org/resolver';
|
|
411
422
|
export type {
|
|
412
423
|
ReadFileFn,
|
package/src/infra/parser.ts
CHANGED
|
@@ -113,8 +113,8 @@ function extractPipeMetadata(rest: string): {
|
|
|
113
113
|
const tags: Record<string, string> = {};
|
|
114
114
|
let clean = rest;
|
|
115
115
|
let match: RegExpExecArray | null;
|
|
116
|
-
|
|
117
|
-
while ((match =
|
|
116
|
+
PIPE_META_RE.lastIndex = 0;
|
|
117
|
+
while ((match = PIPE_META_RE.exec(rest)) !== null) {
|
|
118
118
|
tags[match[1].trim()] = match[2].trim();
|
|
119
119
|
clean = clean.replace(match[0], '');
|
|
120
120
|
}
|
package/src/internal.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// @diagrammo/dgmo/internal — internal helpers for app consumers.
|
|
3
|
+
// Not part of the public API; may change between versions.
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
export { parseDataRowValues } from './chart';
|
|
7
|
+
export {
|
|
8
|
+
computeCardArchive,
|
|
9
|
+
computeCardMove,
|
|
10
|
+
isArchiveColumn,
|
|
11
|
+
} from './kanban/mutations';
|
|
12
|
+
export {
|
|
13
|
+
groupMessagesBySection,
|
|
14
|
+
buildNoteMessageMap,
|
|
15
|
+
collectNoteLineNumbers,
|
|
16
|
+
} from './sequence/renderer';
|