@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
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Pyramid Diagram — Parser
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
import { makeDgmoError, formatDgmoError } from '../diagnostics';
|
|
6
|
+
import {
|
|
7
|
+
measureIndent,
|
|
8
|
+
parseFirstLine,
|
|
9
|
+
parsePipeMetadata,
|
|
10
|
+
} from '../utils/parsing';
|
|
11
|
+
import type { ParsedPyramid, PyramidLayer } from './types';
|
|
12
|
+
|
|
13
|
+
/** Heuristic: pipe content is key:value form if it starts with `word:`. */
|
|
14
|
+
const KEY_VALUE_PREFIX_RE = /^\s*[A-Za-z][A-Za-z0-9_-]*\s*:/;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse a `.dgmo` pyramid diagram document.
|
|
18
|
+
*
|
|
19
|
+
* Top of file = apex of pyramid (reads top-down).
|
|
20
|
+
*
|
|
21
|
+
* Syntax:
|
|
22
|
+
* ```
|
|
23
|
+
* pyramid Maslow's Hierarchy of Needs
|
|
24
|
+
*
|
|
25
|
+
* inverted // optional — flips apex to bottom
|
|
26
|
+
*
|
|
27
|
+
* Self-Actualization // indented body = description
|
|
28
|
+
* Achieving one's full potential.
|
|
29
|
+
*
|
|
30
|
+
* Esteem | Respect, recognition // bare pipe shorthand = description
|
|
31
|
+
*
|
|
32
|
+
* Love & Belonging | color: blue // structured metadata
|
|
33
|
+
* Friendship, intimacy, family.
|
|
34
|
+
*
|
|
35
|
+
* Physiological | Food, water, rest
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function parsePyramid(content: string): ParsedPyramid {
|
|
39
|
+
const result: ParsedPyramid = {
|
|
40
|
+
type: 'pyramid',
|
|
41
|
+
title: '',
|
|
42
|
+
titleLineNumber: 0,
|
|
43
|
+
layers: [],
|
|
44
|
+
inverted: false,
|
|
45
|
+
options: {},
|
|
46
|
+
diagnostics: [],
|
|
47
|
+
error: null,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const lines = content.split('\n');
|
|
51
|
+
let headerParsed = false;
|
|
52
|
+
let currentLayer: PyramidLayer | null = null;
|
|
53
|
+
|
|
54
|
+
const fail = (line: number, message: string): ParsedPyramid => {
|
|
55
|
+
const diag = makeDgmoError(line, message);
|
|
56
|
+
result.diagnostics.push(diag);
|
|
57
|
+
result.error = formatDgmoError(diag);
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const warn = (
|
|
62
|
+
line: number,
|
|
63
|
+
message: string,
|
|
64
|
+
severity: 'warning' | 'error' = 'warning'
|
|
65
|
+
): void => {
|
|
66
|
+
result.diagnostics.push(makeDgmoError(line, message, severity));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const flushLayer = (): void => {
|
|
70
|
+
if (currentLayer) {
|
|
71
|
+
result.layers.push(currentLayer);
|
|
72
|
+
currentLayer = null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i < lines.length; i++) {
|
|
77
|
+
const lineNum = i + 1;
|
|
78
|
+
const raw = lines[i];
|
|
79
|
+
const trimmed = raw.trim();
|
|
80
|
+
|
|
81
|
+
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
82
|
+
|
|
83
|
+
const indent = measureIndent(raw);
|
|
84
|
+
|
|
85
|
+
// ── First line: chart type declaration ──
|
|
86
|
+
if (!headerParsed) {
|
|
87
|
+
const firstLineResult = parseFirstLine(trimmed);
|
|
88
|
+
if (firstLineResult && firstLineResult.chartType === 'pyramid') {
|
|
89
|
+
result.title = firstLineResult.title ?? '';
|
|
90
|
+
result.titleLineNumber = lineNum;
|
|
91
|
+
headerParsed = true;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
return fail(lineNum, 'Expected "pyramid [Title]" as the first line.');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Bare directive: inverted ──
|
|
98
|
+
if (indent === 0 && trimmed.toLowerCase() === 'inverted') {
|
|
99
|
+
result.inverted = true;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── Top-level: layer declaration ──
|
|
104
|
+
if (indent === 0) {
|
|
105
|
+
flushLayer();
|
|
106
|
+
|
|
107
|
+
const pipeIdx = trimmed.indexOf('|');
|
|
108
|
+
let label: string;
|
|
109
|
+
const description: string[] = [];
|
|
110
|
+
let color: string | undefined;
|
|
111
|
+
let restMeta: Record<string, string> = {};
|
|
112
|
+
|
|
113
|
+
if (pipeIdx < 0) {
|
|
114
|
+
label = trimmed;
|
|
115
|
+
} else {
|
|
116
|
+
label = trimmed.substring(0, pipeIdx).trim();
|
|
117
|
+
const after = trimmed.substring(pipeIdx + 1).trim();
|
|
118
|
+
|
|
119
|
+
if (!after) {
|
|
120
|
+
// Trailing pipe with nothing after — ignore.
|
|
121
|
+
} else if (KEY_VALUE_PREFIX_RE.test(after)) {
|
|
122
|
+
// Structured metadata: color: foo, other: bar
|
|
123
|
+
const metadata = parsePipeMetadata([label, after]);
|
|
124
|
+
color = metadata['color'];
|
|
125
|
+
const descFromPipe = metadata['description'];
|
|
126
|
+
if (descFromPipe) description.push(descFromPipe);
|
|
127
|
+
restMeta = { ...metadata };
|
|
128
|
+
delete restMeta['color'];
|
|
129
|
+
delete restMeta['description'];
|
|
130
|
+
} else {
|
|
131
|
+
// Bare shorthand: pipe content is the description.
|
|
132
|
+
description.push(after);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!label) {
|
|
137
|
+
warn(lineNum, 'Empty layer label.');
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
currentLayer = {
|
|
142
|
+
label,
|
|
143
|
+
lineNumber: lineNum,
|
|
144
|
+
color,
|
|
145
|
+
description,
|
|
146
|
+
metadata: restMeta,
|
|
147
|
+
};
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── Indented: description line under current layer ──
|
|
152
|
+
if (!currentLayer) {
|
|
153
|
+
warn(lineNum, `Unexpected indented line: "${trimmed}".`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const descLine = trimmed.startsWith('- ')
|
|
157
|
+
? `• ${trimmed.substring(2)}`
|
|
158
|
+
: trimmed;
|
|
159
|
+
currentLayer.description.push(descLine);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
flushLayer();
|
|
163
|
+
|
|
164
|
+
if (result.layers.length < 2) {
|
|
165
|
+
return fail(
|
|
166
|
+
result.titleLineNumber || 1,
|
|
167
|
+
'pyramid requires at least 2 layers.'
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
}
|