@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.
Files changed (53) hide show
  1. package/dist/cli.cjs +111 -109
  2. package/dist/editor.cjs +3 -0
  3. package/dist/editor.cjs.map +1 -1
  4. package/dist/editor.js +3 -0
  5. package/dist/editor.js.map +1 -1
  6. package/dist/highlight.cjs +3 -0
  7. package/dist/highlight.cjs.map +1 -1
  8. package/dist/highlight.js +3 -0
  9. package/dist/highlight.js.map +1 -1
  10. package/dist/index.cjs +1010 -215
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +97 -11
  13. package/dist/index.d.ts +97 -11
  14. package/dist/index.js +1001 -213
  15. package/dist/index.js.map +1 -1
  16. package/dist/internal.cjs +380 -0
  17. package/dist/internal.cjs.map +1 -0
  18. package/dist/internal.d.cts +179 -0
  19. package/dist/internal.d.ts +179 -0
  20. package/dist/internal.js +337 -0
  21. package/dist/internal.js.map +1 -0
  22. package/docs/guide/chart-cycle.md +156 -0
  23. package/docs/guide/chart-journey-map.md +179 -0
  24. package/docs/guide/chart-pyramid.md +111 -0
  25. package/docs/guide/registry.json +5 -0
  26. package/docs/language-reference.md +62 -1
  27. package/gallery/fixtures/pyramid/dikw.dgmo +17 -0
  28. package/gallery/fixtures/pyramid/inverted-funnel.dgmo +16 -0
  29. package/gallery/fixtures/pyramid/minimal.dgmo +5 -0
  30. package/package.json +11 -1
  31. package/src/cli.ts +5 -35
  32. package/src/completion.ts +9 -44
  33. package/src/cycle/layout.ts +19 -28
  34. package/src/cycle/renderer.ts +59 -32
  35. package/src/cycle/types.ts +21 -0
  36. package/src/d3.ts +21 -1
  37. package/src/dgmo-router.ts +73 -3
  38. package/src/echarts.ts +1 -1
  39. package/src/editor/keywords.ts +3 -0
  40. package/src/index.ts +13 -2
  41. package/src/infra/parser.ts +2 -2
  42. package/src/internal.ts +16 -0
  43. package/src/journey-map/renderer.ts +112 -47
  44. package/src/org/collapse.ts +81 -0
  45. package/src/org/renderer.ts +212 -4
  46. package/src/pyramid/parser.ts +172 -0
  47. package/src/pyramid/renderer.ts +684 -0
  48. package/src/pyramid/types.ts +28 -0
  49. package/src/render.ts +2 -8
  50. package/src/sequence/parser.ts +62 -20
  51. package/src/sequence/renderer.ts +2 -2
  52. package/src/tech-radar/interactive.ts +54 -0
  53. 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
+ }