@diagrammo/dgmo 0.8.21 → 0.8.22
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/AGENTS.md +2 -1
- package/README.md +1 -0
- package/dist/cli.cjs +143 -93
- package/dist/editor.cjs +17 -3
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +17 -3
- package/dist/editor.js.map +1 -1
- package/dist/highlight.cjs +12 -2
- package/dist/highlight.cjs.map +1 -1
- package/dist/highlight.js +12 -2
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +19997 -14886
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +331 -8
- package/dist/index.d.ts +331 -8
- package/dist/index.js +19984 -14889
- package/dist/index.js.map +1 -1
- package/docs/guide/chart-sitemap.md +18 -1
- package/docs/guide/chart-tech-radar.md +219 -0
- package/docs/guide/registry.json +1 -0
- package/docs/language-reference.md +116 -6
- package/gallery/fixtures/boxes-and-lines.dgmo +10 -3
- package/gallery/fixtures/c4-full.dgmo +2 -2
- package/gallery/fixtures/cycle/ooda-loop.dgmo +25 -0
- package/gallery/fixtures/cycle/pdca-circle-nodes.dgmo +12 -0
- package/gallery/fixtures/cycle/pdca-minimal.dgmo +6 -0
- package/gallery/fixtures/cycle/sprint-cycle-span.dgmo +17 -0
- package/gallery/fixtures/gantt-full.dgmo +2 -2
- package/gallery/fixtures/gantt.dgmo +2 -2
- package/gallery/fixtures/infra-full.dgmo +2 -2
- package/gallery/fixtures/infra.dgmo +1 -1
- package/gallery/fixtures/sequence-tags-protocols.dgmo +2 -2
- package/gallery/fixtures/sequence-tags.dgmo +2 -2
- package/gallery/fixtures/tech-radar-dense.dgmo +77 -0
- package/gallery/fixtures/tech-radar.dgmo +36 -0
- package/gallery/fixtures/timeline.dgmo +1 -1
- package/package.json +1 -1
- package/src/boxes-and-lines/layout.ts +309 -33
- package/src/boxes-and-lines/parser.ts +86 -10
- package/src/boxes-and-lines/renderer.ts +250 -91
- package/src/boxes-and-lines/types.ts +1 -1
- package/src/c4/layout.ts +8 -8
- package/src/c4/parser.ts +35 -2
- package/src/c4/renderer.ts +19 -3
- package/src/c4/types.ts +1 -0
- package/src/chart.ts +14 -7
- package/src/completion.ts +227 -0
- package/src/cycle/layout.ts +732 -0
- package/src/cycle/parser.ts +352 -0
- package/src/cycle/renderer.ts +539 -0
- package/src/cycle/types.ts +77 -0
- package/src/d3.ts +87 -8
- package/src/dgmo-router.ts +9 -0
- package/src/echarts.ts +7 -4
- package/src/editor/dgmo.grammar +5 -1
- package/src/editor/dgmo.grammar.js +1 -1
- package/src/editor/keywords.ts +14 -0
- package/src/gantt/parser.ts +2 -8
- package/src/graph/flowchart-parser.ts +15 -21
- package/src/graph/state-parser.ts +5 -10
- package/src/index.ts +50 -0
- package/src/infra/layout.ts +218 -74
- package/src/infra/parser.ts +30 -6
- package/src/infra/renderer.ts +14 -8
- package/src/infra/types.ts +10 -3
- package/src/journey-map/layout.ts +386 -0
- package/src/journey-map/parser.ts +540 -0
- package/src/journey-map/renderer.ts +1456 -0
- package/src/journey-map/types.ts +47 -0
- package/src/kanban/parser.ts +3 -10
- package/src/kanban/renderer.ts +31 -15
- package/src/mindmap/parser.ts +12 -18
- package/src/mindmap/renderer.ts +14 -13
- package/src/mindmap/text-wrap.ts +22 -12
- package/src/mindmap/types.ts +2 -2
- package/src/org/parser.ts +2 -6
- package/src/sequence/renderer.ts +144 -38
- package/src/sharing.ts +1 -0
- package/src/sitemap/layout.ts +21 -6
- package/src/sitemap/parser.ts +26 -17
- package/src/sitemap/renderer.ts +34 -0
- package/src/sitemap/types.ts +1 -0
- package/src/tech-radar/index.ts +14 -0
- package/src/tech-radar/interactive.ts +1058 -0
- package/src/tech-radar/layout.ts +190 -0
- package/src/tech-radar/parser.ts +385 -0
- package/src/tech-radar/renderer.ts +1159 -0
- package/src/tech-radar/shared.ts +187 -0
- package/src/tech-radar/types.ts +81 -0
- package/src/utils/description-helpers.ts +33 -0
- package/src/utils/legend-layout.ts +3 -1
- package/src/utils/parsing.ts +46 -7
- package/src/utils/tag-groups.ts +46 -60
package/src/d3.ts
CHANGED
|
@@ -19,7 +19,9 @@ export type VisualizationType =
|
|
|
19
19
|
| 'timeline'
|
|
20
20
|
| 'venn'
|
|
21
21
|
| 'quadrant'
|
|
22
|
-
| 'sequence'
|
|
22
|
+
| 'sequence'
|
|
23
|
+
| 'tech-radar'
|
|
24
|
+
| 'cycle';
|
|
23
25
|
|
|
24
26
|
interface D3DataItem {
|
|
25
27
|
label: string;
|
|
@@ -184,6 +186,7 @@ import { makeDgmoError, formatDgmoError, suggest } from './diagnostics';
|
|
|
184
186
|
import {
|
|
185
187
|
collectIndentedValues,
|
|
186
188
|
extractColor,
|
|
189
|
+
normalizeNumericToken,
|
|
187
190
|
parseFirstLine,
|
|
188
191
|
parsePipeMetadata,
|
|
189
192
|
MULTIPLE_PIPE_ERROR,
|
|
@@ -632,7 +635,7 @@ export function parseVisualization(
|
|
|
632
635
|
// Arc link line: source -> target(color) weight
|
|
633
636
|
if (result.type === 'arc') {
|
|
634
637
|
const linkMatch = line.match(
|
|
635
|
-
/^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?:\s+(\d+(
|
|
638
|
+
/^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?:\s+(-?[\d,_]+(?:\.[\d]+)?))?$/
|
|
636
639
|
);
|
|
637
640
|
if (linkMatch) {
|
|
638
641
|
const source = linkMatch[1].trim();
|
|
@@ -648,7 +651,9 @@ export function parseVisualization(
|
|
|
648
651
|
result.links.push({
|
|
649
652
|
source,
|
|
650
653
|
target,
|
|
651
|
-
value: linkMatch[4]
|
|
654
|
+
value: linkMatch[4]
|
|
655
|
+
? parseFloat(normalizeNumericToken(linkMatch[4]) ?? linkMatch[4])
|
|
656
|
+
: 1,
|
|
652
657
|
color: linkColor,
|
|
653
658
|
lineNumber,
|
|
654
659
|
});
|
|
@@ -1028,7 +1033,7 @@ export function parseVisualization(
|
|
|
1028
1033
|
|
|
1029
1034
|
// Data points: Label x, y OR Label x y
|
|
1030
1035
|
const pointMatch = line.match(
|
|
1031
|
-
/^(.+?)\s+([0-9]
|
|
1036
|
+
/^(.+?)\s+(-?[0-9][0-9,_]*(?:\.[0-9]+)?)\s*[,\s]\s*(-?[0-9][0-9,_]*(?:\.[0-9]+)?)\s*$/
|
|
1032
1037
|
);
|
|
1033
1038
|
if (pointMatch) {
|
|
1034
1039
|
const label = pointMatch[1].trim();
|
|
@@ -1042,8 +1047,12 @@ export function parseVisualization(
|
|
|
1042
1047
|
) {
|
|
1043
1048
|
result.quadrantPoints.push({
|
|
1044
1049
|
label,
|
|
1045
|
-
x: parseFloat(
|
|
1046
|
-
|
|
1050
|
+
x: parseFloat(
|
|
1051
|
+
normalizeNumericToken(pointMatch[2]) ?? pointMatch[2]
|
|
1052
|
+
),
|
|
1053
|
+
y: parseFloat(
|
|
1054
|
+
normalizeNumericToken(pointMatch[3]) ?? pointMatch[3]
|
|
1055
|
+
),
|
|
1047
1056
|
lineNumber,
|
|
1048
1057
|
});
|
|
1049
1058
|
}
|
|
@@ -1201,7 +1210,8 @@ export function parseVisualization(
|
|
|
1201
1210
|
// Scan from right, capped at P values
|
|
1202
1211
|
let rightIdx = tokens.length - 1;
|
|
1203
1212
|
while (rightIdx >= 0 && values.length < P) {
|
|
1204
|
-
const raw =
|
|
1213
|
+
const raw =
|
|
1214
|
+
normalizeNumericToken(tokens[rightIdx]) ?? tokens[rightIdx];
|
|
1205
1215
|
const num = parseFloat(raw);
|
|
1206
1216
|
if (!isNaN(num) && /^-?\d/.test(raw)) {
|
|
1207
1217
|
values.unshift(num);
|
|
@@ -1385,8 +1395,11 @@ export function parseVisualization(
|
|
|
1385
1395
|
} else if (colonIndex === -1) {
|
|
1386
1396
|
// Try "word weight" or "multi-word-label weight" space-separated format
|
|
1387
1397
|
const lastSpace = line.lastIndexOf(' ');
|
|
1398
|
+
const rawWeight = lastSpace >= 0 ? line.substring(lastSpace + 1) : '';
|
|
1388
1399
|
const maybeWeight =
|
|
1389
|
-
lastSpace >= 0
|
|
1400
|
+
lastSpace >= 0
|
|
1401
|
+
? parseFloat(normalizeNumericToken(rawWeight) ?? rawWeight)
|
|
1402
|
+
: NaN;
|
|
1390
1403
|
if (lastSpace >= 0 && !isNaN(maybeWeight) && maybeWeight > 0) {
|
|
1391
1404
|
result.words.push({
|
|
1392
1405
|
text: line.substring(0, lastSpace).trim(),
|
|
@@ -7041,6 +7054,72 @@ export async function renderForExport(
|
|
|
7041
7054
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
7042
7055
|
}
|
|
7043
7056
|
|
|
7057
|
+
if (detectedType === 'tech-radar') {
|
|
7058
|
+
const { parseTechRadar } = await import('./tech-radar/parser');
|
|
7059
|
+
const { renderTechRadarForExport } = await import('./tech-radar/renderer');
|
|
7060
|
+
|
|
7061
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
7062
|
+
const radarParsed = parseTechRadar(content);
|
|
7063
|
+
if (radarParsed.error || radarParsed.quadrants.length === 0) return '';
|
|
7064
|
+
|
|
7065
|
+
const RADAR_EXPORT_W = 1600;
|
|
7066
|
+
const RADAR_EXPORT_H = 1200;
|
|
7067
|
+
const container = createExportContainer(RADAR_EXPORT_W, RADAR_EXPORT_H);
|
|
7068
|
+
renderTechRadarForExport(
|
|
7069
|
+
container,
|
|
7070
|
+
radarParsed,
|
|
7071
|
+
effectivePalette,
|
|
7072
|
+
theme === 'dark',
|
|
7073
|
+
{ width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
|
|
7074
|
+
viewState
|
|
7075
|
+
);
|
|
7076
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
7077
|
+
}
|
|
7078
|
+
|
|
7079
|
+
if (detectedType === 'journey-map') {
|
|
7080
|
+
const { parseJourneyMap } = await import('./journey-map/parser');
|
|
7081
|
+
const { renderJourneyMap } = await import('./journey-map/renderer');
|
|
7082
|
+
const { layoutJourneyMap } = await import('./journey-map/layout');
|
|
7083
|
+
|
|
7084
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
7085
|
+
const jmParsed = parseJourneyMap(content, effectivePalette);
|
|
7086
|
+
if (
|
|
7087
|
+
jmParsed.error ||
|
|
7088
|
+
(jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
|
|
7089
|
+
)
|
|
7090
|
+
return '';
|
|
7091
|
+
|
|
7092
|
+
const jmLayout = layoutJourneyMap(jmParsed, effectivePalette);
|
|
7093
|
+
const container = createExportContainer(
|
|
7094
|
+
jmLayout.totalWidth,
|
|
7095
|
+
jmLayout.totalHeight
|
|
7096
|
+
);
|
|
7097
|
+
renderJourneyMap(container, jmParsed, effectivePalette, theme === 'dark', {
|
|
7098
|
+
exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
|
|
7099
|
+
});
|
|
7100
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
7101
|
+
}
|
|
7102
|
+
|
|
7103
|
+
if (detectedType === 'cycle') {
|
|
7104
|
+
const { parseCycle } = await import('./cycle/parser');
|
|
7105
|
+
const { renderCycleForExport } = await import('./cycle/renderer');
|
|
7106
|
+
|
|
7107
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
7108
|
+
const cycleParsed = parseCycle(content);
|
|
7109
|
+
if (cycleParsed.error || cycleParsed.nodes.length === 0) return '';
|
|
7110
|
+
|
|
7111
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
7112
|
+
renderCycleForExport(
|
|
7113
|
+
container,
|
|
7114
|
+
cycleParsed,
|
|
7115
|
+
effectivePalette,
|
|
7116
|
+
theme === 'dark',
|
|
7117
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
|
|
7118
|
+
viewState
|
|
7119
|
+
);
|
|
7120
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
7121
|
+
}
|
|
7122
|
+
|
|
7044
7123
|
const parsed = parseVisualization(content, palette);
|
|
7045
7124
|
// Allow sequence diagrams through even if parseVisualization errors —
|
|
7046
7125
|
// sequence is parsed by its own dedicated parser (parseSequenceDgmo)
|
package/src/dgmo-router.ts
CHANGED
|
@@ -19,6 +19,9 @@ import { parseGantt } from './gantt/parser';
|
|
|
19
19
|
import { parseBoxesAndLines } from './boxes-and-lines/parser';
|
|
20
20
|
import { parseMindmap } from './mindmap/parser';
|
|
21
21
|
import { parseWireframe } from './wireframe/parser';
|
|
22
|
+
import { parseTechRadar } from './tech-radar/parser';
|
|
23
|
+
import { parseCycle } from './cycle/parser';
|
|
24
|
+
import { parseJourneyMap } from './journey-map/parser';
|
|
22
25
|
import { parseFirstLine } from './utils/parsing';
|
|
23
26
|
import { makeDgmoError, suggest } from './diagnostics';
|
|
24
27
|
import type { DgmoError } from './diagnostics';
|
|
@@ -137,6 +140,8 @@ const VISUALIZATION_TYPES = new Set([
|
|
|
137
140
|
'timeline',
|
|
138
141
|
'venn',
|
|
139
142
|
'quadrant',
|
|
143
|
+
'tech-radar',
|
|
144
|
+
'cycle',
|
|
140
145
|
]);
|
|
141
146
|
const DIAGRAM_TYPES = new Set([
|
|
142
147
|
'sequence',
|
|
@@ -153,6 +158,7 @@ const DIAGRAM_TYPES = new Set([
|
|
|
153
158
|
'boxes-and-lines',
|
|
154
159
|
'mindmap',
|
|
155
160
|
'wireframe',
|
|
161
|
+
'journey-map',
|
|
156
162
|
]);
|
|
157
163
|
const EXTENDED_CHART_TYPES = new Set([
|
|
158
164
|
'scatter',
|
|
@@ -234,6 +240,9 @@ const PARSE_DISPATCH = new Map<
|
|
|
234
240
|
['boxes-and-lines', (c) => parseBoxesAndLines(c)],
|
|
235
241
|
['mindmap', (c) => parseMindmap(c)],
|
|
236
242
|
['wireframe', (c) => parseWireframe(c)],
|
|
243
|
+
['tech-radar', (c) => parseTechRadar(c)],
|
|
244
|
+
['cycle', (c) => parseCycle(c)],
|
|
245
|
+
['journey-map', (c) => parseJourneyMap(c)],
|
|
237
246
|
]);
|
|
238
247
|
|
|
239
248
|
/**
|
package/src/echarts.ts
CHANGED
|
@@ -150,6 +150,7 @@ import {
|
|
|
150
150
|
collectIndentedValues,
|
|
151
151
|
extractColor,
|
|
152
152
|
measureIndent,
|
|
153
|
+
normalizeNumericToken,
|
|
153
154
|
parseFirstLine,
|
|
154
155
|
parseSeriesNames,
|
|
155
156
|
} from './utils/parsing';
|
|
@@ -359,10 +360,11 @@ export function parseExtendedChart(
|
|
|
359
360
|
|
|
360
361
|
// Sankey/chord link syntax: Source -> Target Value (directed) or Source -- Target Value (undirected)
|
|
361
362
|
const arrowMatch = trimmed.match(
|
|
362
|
-
/^(.+?)\s*(->|--)\s*(.+?)\s+(\d+(
|
|
363
|
+
/^(.+?)\s*(->|--)\s*(.+?)\s+(-?[\d,_]+(?:\.[\d]+)?)\s*(?:\(([^)]+)\))?\s*$/
|
|
363
364
|
);
|
|
364
365
|
if (arrowMatch) {
|
|
365
|
-
const [, rawSource, arrow, rawTarget,
|
|
366
|
+
const [, rawSource, arrow, rawTarget, rawVal, rawLinkColor] = arrowMatch;
|
|
367
|
+
const val = normalizeNumericToken(rawVal) ?? rawVal;
|
|
366
368
|
const { label: source, color: sourceColor } = extractColor(
|
|
367
369
|
rawSource.trim(),
|
|
368
370
|
palette
|
|
@@ -409,7 +411,7 @@ export function parseExtendedChart(
|
|
|
409
411
|
// Parse "TargetName value (linkColor)" or "TargetName(nodeColor) value (linkColor)"
|
|
410
412
|
// Strip trailing (color) annotation before parseDataRowValues — it can't handle it
|
|
411
413
|
const valColorMatch = trimmed.match(
|
|
412
|
-
/(\d+(
|
|
414
|
+
/(-?[\d,_]+(?:\.[\d]+)?)\s*\(([^)]+)\)\s*$/
|
|
413
415
|
);
|
|
414
416
|
const strippedLine = valColorMatch
|
|
415
417
|
? trimmed.replace(/\s*\([^)]+\)\s*$/, '')
|
|
@@ -449,9 +451,10 @@ export function parseExtendedChart(
|
|
|
449
451
|
|
|
450
452
|
// Bare label at indent 0 (or any indent without a value) = new source node
|
|
451
453
|
const spaceIdx = trimmed.indexOf(' ');
|
|
454
|
+
const lastTok = trimmed.substring(trimmed.lastIndexOf(' ') + 1);
|
|
452
455
|
const hasNumericSuffix =
|
|
453
456
|
spaceIdx >= 0 &&
|
|
454
|
-
!isNaN(parseFloat(
|
|
457
|
+
!isNaN(parseFloat(normalizeNumericToken(lastTok) ?? lastTok));
|
|
455
458
|
if (!hasNumericSuffix) {
|
|
456
459
|
while (sankeyStack.length && sankeyStack.at(-1)!.indent >= indent) {
|
|
457
460
|
sankeyStack.pop();
|
package/src/editor/dgmo.grammar
CHANGED
|
@@ -30,7 +30,11 @@ contentPart {
|
|
|
30
30
|
Duration { $[0-9]+ ("." $[0-9]+)? ("min" | "bd" | "h" | "d" | "w" | "m" | "q" | "y" | "s") "?"? }
|
|
31
31
|
DateLiteral { $[0-9] $[0-9] $[0-9] $[0-9] "-" $[0-9] $[0-9] ("-" $[0-9] $[0-9])? }
|
|
32
32
|
Percentage { $[0-9]+ ("." $[0-9]+)? "%" }
|
|
33
|
-
Number {
|
|
33
|
+
Number {
|
|
34
|
+
$[0-9] $[0-9]? $[0-9]? ("," $[0-9] $[0-9] $[0-9])+ ("." $[0-9]+)? |
|
|
35
|
+
$[0-9]+ ("_" $[0-9]+)+ ("." $[0-9]+)? |
|
|
36
|
+
$[0-9]+ ("." $[0-9]+)?
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
SectionMarker { "==" }
|
|
36
40
|
Url { "http" "s"? "://" ![ \t\n|,)\]>]+ }
|
|
@@ -10,7 +10,7 @@ export const parser = LRParser.deserialize({
|
|
|
10
10
|
maxTerm: 40,
|
|
11
11
|
skippedNodes: [0],
|
|
12
12
|
repeatNodeCount: 2,
|
|
13
|
-
tokenData: "
|
|
13
|
+
tokenData: "<O~RxOX#oXY#tYZ$PZp#opq#tqt#oux#oxy$Uyz$tz{${{|%S|}%Z}!O%b!O!P#o!P!Q%q!Q![&b![!]/T!]!^#o!^!_/[!_!`/c!`!a/p!a!b/w!b!c#o!c!}0O!}#O3U#O#P#o#P#Q3Z#Q#R#o#R#S0O#S#T#o#T#[0O#[#]3b#]#o0O#o#p#o#p#q;b#q#r#o#r#s;i#s;'S#o;'S;=`;x<%lO#o~#tOq~~#yQv~XY#tpq#t~$UOw~~$]Qc~q~}!O$c#T#o$c~$fRyz$o}!O$c#T#o$c~$tOg~~${Od~q~~%SOn~q~~%ZOk~q~~%bOj~q~~%iPl~q~!`!a%l~%qOX~~%vPq~!P!Q%y~&OSV~OY%yZ;'S%y;'S;=`&[<%lO%y~&_P;=`<%l%y~&i]^~q~uv'b|}'g!O!P(d!Q![*T#R#S.o#U#V)_#W#X)e#[#])e#a#b)r#e#f)e#g#h)e#k#l)e#m#n)e~'gO]~~'jP!Q!['m~'pP!Q!['s~'vP!Q!['y~(OQ^~|}'g!O!P(U~(XP!Q![([~(aP^~!Q![([~(gP!Q![(j~(oY^~uv'b!Q![(j#U#V)_#W#X)e#[#])e#a#b)r#e#f)e#g#h)e#k#l)e#m#n)e~)bP#W#X)e~)jPZ~!a!b)m~)rOZ~~)wQZ~!a!b)m#]#^)}~*QP#b#c)e~*Y]^~uv'b|}'g!O!P(d!Q![+R#R#S.o#U#V)_#W#X)e#[#])e#a#b)r#e#f)e#g#h)e#k#l)e#m#n)e~+W]^~uv'b|}'g!O!P(d!Q![,P#R#S.o#U#V)_#W#X)e#[#])e#a#b)r#e#f)e#g#h)e#k#l)e#m#n)e~,U]^~uv'b}!O,}!O!P(d!Q![-t#R#S.o#U#V)_#W#X)e#[#])e#a#b)r#e#f)e#g#h)e#k#l)e#m#n)e~-QP!Q![-T~-WP!Q![-Z~-`P[~}!O-c~-fP!Q![-i~-lP!Q![-o~-tO[~~-y[^~uv'b!O!P(d!Q![-t#R#S.o#U#V)_#W#X)e#[#])e#a#b)r#e#f)e#g#h)e#k#l)e#m#n)e~.rP!Q![.u~.zR^~!O!P(U!Q![.u#R#S.o~/[Oi~q~~/cOe~q~~/hPq~!_!`/k~/pO_~~/wOf~q~~0OOo~q~~0V_p~q~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#o1U~1Z_p~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#o1U~2]]qr1Ust1Uvw1Uwx1U{|1U!O!P1U!P!Q1U!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#o1U~3ZOa~~3bOb~q~~3iap~q~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#h1U#h#i4n#i#o1U~4sap~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#h1U#h#i5x#i#o1U~5}ap~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#d1U#d#e7S#e#o1U~7Xbp~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U![!]8a!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#g1U#g#h:Z#h#o1U~8dP!P!Q8g~8jP!P!Q8m~8pYOX9`Zp9`qy9`z|9`}!`9`!a#P9`#Q#p9`#q;'S9`;'S;=`:T<%lO9`~9eY`~OX9`Zp9`qy9`z|9`}!`9`!a#P9`#Q#p9`#q;'S9`;'S;=`:T<%lO9`~:WP;=`<%l9`~:``p~qr1Ust1Uvw1Uwx1U{|1U}!O2Y!O!P1U!P!Q1U!Q![1U![!]8a!_!`1U!a!b1U!b!c1U!c!}1U#R#S1U#T#o1U~;iOh~q~~;pPm~q~!`!a;s~;xOY~~;{P;=`<%l#o",
|
|
14
14
|
tokenizers: [0],
|
|
15
15
|
topRules: {"Document":[0,6]},
|
|
16
16
|
specialized: [{term: 32, get: (value, stack) => (specializeKeyword(value, stack) << 1), external: specializeKeyword}],
|
package/src/editor/keywords.ts
CHANGED
|
@@ -14,6 +14,9 @@ export const CHART_TYPES = new Set([
|
|
|
14
14
|
'gantt',
|
|
15
15
|
'boxes-and-lines',
|
|
16
16
|
'wireframe',
|
|
17
|
+
'tech-radar',
|
|
18
|
+
'mindmap',
|
|
19
|
+
'journey-map',
|
|
17
20
|
// Data chart types
|
|
18
21
|
'bar',
|
|
19
22
|
'line',
|
|
@@ -65,6 +68,10 @@ export const METADATA_KEYS = new Set([
|
|
|
65
68
|
'top-left',
|
|
66
69
|
'bottom-right',
|
|
67
70
|
'bottom-left',
|
|
71
|
+
// Tech-radar pipe metadata
|
|
72
|
+
'quadrant',
|
|
73
|
+
'ring',
|
|
74
|
+
'trend',
|
|
68
75
|
]);
|
|
69
76
|
|
|
70
77
|
/** Tag declaration keyword. */
|
|
@@ -82,6 +89,8 @@ export const DIRECTIVE_KEYWORDS = new Set([
|
|
|
82
89
|
'critical-path',
|
|
83
90
|
'no-dependencies',
|
|
84
91
|
'sort',
|
|
92
|
+
// Tech-radar
|
|
93
|
+
'rings',
|
|
85
94
|
// Tags
|
|
86
95
|
'tags',
|
|
87
96
|
'import',
|
|
@@ -194,6 +203,11 @@ export const STATUS_KEYWORDS = new Set([
|
|
|
194
203
|
'in-progress',
|
|
195
204
|
'backlog',
|
|
196
205
|
'ready',
|
|
206
|
+
// Tech-radar trend values
|
|
207
|
+
'new',
|
|
208
|
+
'up',
|
|
209
|
+
'down',
|
|
210
|
+
'stable',
|
|
197
211
|
]);
|
|
198
212
|
|
|
199
213
|
/** Modifier keywords — adjust declarations. */
|
package/src/gantt/parser.ts
CHANGED
|
@@ -744,7 +744,7 @@ export function parseGantt(
|
|
|
744
744
|
|
|
745
745
|
// First segment could be empty (just `[Group]`) or have metadata
|
|
746
746
|
let metadata: Record<string, string> = {};
|
|
747
|
-
|
|
747
|
+
const color: string | null = null;
|
|
748
748
|
|
|
749
749
|
const pipeWarn = () => warn(lineNumber, MULTIPLE_PIPE_ERROR);
|
|
750
750
|
if (segments.length > 0 && segments[0].trim()) {
|
|
@@ -758,14 +758,8 @@ export function parseGantt(
|
|
|
758
758
|
);
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
-
// Extract color from group name if present
|
|
762
|
-
const nameExtracted = extractColor(groupMatch[1], palette);
|
|
763
|
-
if (nameExtracted.color) {
|
|
764
|
-
color = nameExtracted.color;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
761
|
const group: GanttGroup = {
|
|
768
|
-
name:
|
|
762
|
+
name: groupMatch[1],
|
|
769
763
|
color,
|
|
770
764
|
metadata,
|
|
771
765
|
lineNumber,
|
|
@@ -5,7 +5,6 @@ import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
|
5
5
|
import { parseInArrowLabel, matchColorParens } from '../utils/arrows';
|
|
6
6
|
import {
|
|
7
7
|
measureIndent,
|
|
8
|
-
extractColor,
|
|
9
8
|
inferArrowColor,
|
|
10
9
|
parseFirstLine,
|
|
11
10
|
OPTION_NOCOLON_RE,
|
|
@@ -32,55 +31,50 @@ interface NodeRef {
|
|
|
32
31
|
* Try to parse a node reference from a text fragment.
|
|
33
32
|
* Order matters: subroutine & document before process.
|
|
34
33
|
*/
|
|
35
|
-
function parseNodeRef(text: string
|
|
34
|
+
function parseNodeRef(text: string): NodeRef | null {
|
|
36
35
|
const t = text.trim();
|
|
37
36
|
if (!t) return null;
|
|
38
37
|
|
|
39
38
|
// Subroutine: [[Label]]
|
|
40
39
|
let m = t.match(/^\[\[([^\]]+)\]\]$/);
|
|
41
40
|
if (m) {
|
|
42
|
-
const
|
|
43
|
-
return {
|
|
44
|
-
id: nodeId('subroutine', label),
|
|
45
|
-
label,
|
|
46
|
-
shape: 'subroutine',
|
|
47
|
-
color,
|
|
48
|
-
};
|
|
41
|
+
const label = m[1].trim();
|
|
42
|
+
return { id: nodeId('subroutine', label), label, shape: 'subroutine' };
|
|
49
43
|
}
|
|
50
44
|
|
|
51
45
|
// Document: [Label~]
|
|
52
46
|
m = t.match(/^\[([^\]]+)~\]$/);
|
|
53
47
|
if (m) {
|
|
54
|
-
const
|
|
55
|
-
return { id: nodeId('document', label), label, shape: 'document'
|
|
48
|
+
const label = m[1].trim();
|
|
49
|
+
return { id: nodeId('document', label), label, shape: 'document' };
|
|
56
50
|
}
|
|
57
51
|
|
|
58
52
|
// Process: [Label]
|
|
59
53
|
m = t.match(/^\[([^\]]+)\]$/);
|
|
60
54
|
if (m) {
|
|
61
|
-
const
|
|
62
|
-
return { id: nodeId('process', label), label, shape: 'process'
|
|
55
|
+
const label = m[1].trim();
|
|
56
|
+
return { id: nodeId('process', label), label, shape: 'process' };
|
|
63
57
|
}
|
|
64
58
|
|
|
65
59
|
// Terminal: (Label) — use .+ (greedy) so (Label(color)) matches outermost parens
|
|
66
60
|
m = t.match(/^\((.+)\)$/);
|
|
67
61
|
if (m) {
|
|
68
|
-
const
|
|
69
|
-
return { id: nodeId('terminal', label), label, shape: 'terminal'
|
|
62
|
+
const label = m[1].trim();
|
|
63
|
+
return { id: nodeId('terminal', label), label, shape: 'terminal' };
|
|
70
64
|
}
|
|
71
65
|
|
|
72
66
|
// Decision: <Label>
|
|
73
67
|
m = t.match(/^<([^>]+)>$/);
|
|
74
68
|
if (m) {
|
|
75
|
-
const
|
|
76
|
-
return { id: nodeId('decision', label), label, shape: 'decision'
|
|
69
|
+
const label = m[1].trim();
|
|
70
|
+
return { id: nodeId('decision', label), label, shape: 'decision' };
|
|
77
71
|
}
|
|
78
72
|
|
|
79
73
|
// I/O: /Label/
|
|
80
74
|
m = t.match(/^\/([^/]+)\/$/);
|
|
81
75
|
if (m) {
|
|
82
|
-
const
|
|
83
|
-
return { id: nodeId('io', label), label, shape: 'io'
|
|
76
|
+
const label = m[1].trim();
|
|
77
|
+
return { id: nodeId('io', label), label, shape: 'io' };
|
|
84
78
|
}
|
|
85
79
|
|
|
86
80
|
return null;
|
|
@@ -370,7 +364,7 @@ export function parseFlowchart(
|
|
|
370
364
|
|
|
371
365
|
if (segments.length === 1) {
|
|
372
366
|
// Single node reference, no arrows
|
|
373
|
-
const ref = parseNodeRef(segments[0]
|
|
367
|
+
const ref = parseNodeRef(segments[0]);
|
|
374
368
|
if (ref) {
|
|
375
369
|
const node = getOrCreateNode(ref, lineNumber);
|
|
376
370
|
indentStack.push({ nodeId: node.id, indent });
|
|
@@ -398,7 +392,7 @@ export function parseFlowchart(
|
|
|
398
392
|
}
|
|
399
393
|
|
|
400
394
|
// This is a node text segment
|
|
401
|
-
const ref = parseNodeRef(seg
|
|
395
|
+
const ref = parseNodeRef(seg);
|
|
402
396
|
if (!ref) continue;
|
|
403
397
|
|
|
404
398
|
const node = getOrCreateNode(ref, lineNumber);
|
|
@@ -5,7 +5,6 @@ import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
|
5
5
|
import { parseInArrowLabel, matchColorParens } from '../utils/arrows';
|
|
6
6
|
import {
|
|
7
7
|
measureIndent,
|
|
8
|
-
extractColor,
|
|
9
8
|
parseFirstLine,
|
|
10
9
|
OPTION_NOCOLON_RE,
|
|
11
10
|
ALL_CHART_TYPES,
|
|
@@ -173,10 +172,7 @@ interface NodeRef {
|
|
|
173
172
|
color?: string;
|
|
174
173
|
}
|
|
175
174
|
|
|
176
|
-
function parseStateNodeRef(
|
|
177
|
-
text: string,
|
|
178
|
-
palette?: PaletteColors
|
|
179
|
-
): NodeRef | null {
|
|
175
|
+
function parseStateNodeRef(text: string): NodeRef | null {
|
|
180
176
|
const t = text.trim();
|
|
181
177
|
if (!t) return null;
|
|
182
178
|
|
|
@@ -189,14 +185,13 @@ function parseStateNodeRef(
|
|
|
189
185
|
};
|
|
190
186
|
}
|
|
191
187
|
|
|
192
|
-
// State: bare text
|
|
193
|
-
const
|
|
188
|
+
// State: bare text
|
|
189
|
+
const label = t;
|
|
194
190
|
if (!label) return null;
|
|
195
191
|
return {
|
|
196
192
|
id: `state:${label.toLowerCase().trim()}`,
|
|
197
193
|
label,
|
|
198
194
|
shape: 'state',
|
|
199
|
-
color,
|
|
200
195
|
};
|
|
201
196
|
}
|
|
202
197
|
|
|
@@ -380,7 +375,7 @@ export function parseState(
|
|
|
380
375
|
|
|
381
376
|
if (segments.length === 1) {
|
|
382
377
|
// Single state reference, no arrows — this is the canonical definition
|
|
383
|
-
const ref = parseStateNodeRef(segments[0]
|
|
378
|
+
const ref = parseStateNodeRef(segments[0]);
|
|
384
379
|
if (ref) {
|
|
385
380
|
const node = getOrCreateNode(ref, lineNumber);
|
|
386
381
|
// Standalone heading is the "definition" — update lineNumber so
|
|
@@ -409,7 +404,7 @@ export function parseState(
|
|
|
409
404
|
continue;
|
|
410
405
|
}
|
|
411
406
|
|
|
412
|
-
const ref = parseStateNodeRef(seg
|
|
407
|
+
const ref = parseStateNodeRef(seg);
|
|
413
408
|
if (!ref) continue;
|
|
414
409
|
|
|
415
410
|
const node = getOrCreateNode(ref, lineNumber);
|
package/src/index.ts
CHANGED
|
@@ -358,6 +358,55 @@ export { layoutWireframe } from './wireframe/layout';
|
|
|
358
358
|
export type { WireframeLayout, WireframeLayoutNode } from './wireframe/layout';
|
|
359
359
|
export { renderWireframe } from './wireframe/renderer';
|
|
360
360
|
|
|
361
|
+
export { parseTechRadar } from './tech-radar/parser';
|
|
362
|
+
export { computeRadarLayout, getRadarGeometry } from './tech-radar/layout';
|
|
363
|
+
export {
|
|
364
|
+
renderTechRadar,
|
|
365
|
+
renderTechRadarForExport,
|
|
366
|
+
} from './tech-radar/renderer';
|
|
367
|
+
export {
|
|
368
|
+
renderQuadrantFocus,
|
|
369
|
+
renderQuadrantFocusForExport,
|
|
370
|
+
} from './tech-radar/interactive';
|
|
371
|
+
export type {
|
|
372
|
+
ParsedTechRadar,
|
|
373
|
+
TechRadarRing,
|
|
374
|
+
TechRadarQuadrant,
|
|
375
|
+
TechRadarBlip,
|
|
376
|
+
TechRadarLayoutPoint,
|
|
377
|
+
QuadrantPosition,
|
|
378
|
+
BlipTrend,
|
|
379
|
+
} from './tech-radar/types';
|
|
380
|
+
|
|
381
|
+
export { parseCycle } from './cycle/parser';
|
|
382
|
+
export { computeCycleLayout } from './cycle/layout';
|
|
383
|
+
export { renderCycle, renderCycleForExport } from './cycle/renderer';
|
|
384
|
+
export type { CycleRenderOptions } from './cycle/renderer';
|
|
385
|
+
export type {
|
|
386
|
+
ParsedCycle,
|
|
387
|
+
CycleNode,
|
|
388
|
+
CycleEdge,
|
|
389
|
+
CycleLayoutNode,
|
|
390
|
+
CycleLayoutEdge,
|
|
391
|
+
CycleLayoutResult,
|
|
392
|
+
} from './cycle/types';
|
|
393
|
+
|
|
394
|
+
export { parseJourneyMap } from './journey-map/parser';
|
|
395
|
+
export { layoutJourneyMap } from './journey-map/layout';
|
|
396
|
+
export type { JourneyMapLayout } from './journey-map/layout';
|
|
397
|
+
export {
|
|
398
|
+
renderJourneyMap,
|
|
399
|
+
renderJourneyMapForExport,
|
|
400
|
+
} from './journey-map/renderer';
|
|
401
|
+
export type {
|
|
402
|
+
ParsedJourneyMap,
|
|
403
|
+
JourneyMapPhase,
|
|
404
|
+
JourneyMapStep,
|
|
405
|
+
JourneyMapPersona,
|
|
406
|
+
JourneyMapAnnotation,
|
|
407
|
+
} from './journey-map/types';
|
|
408
|
+
export type { JourneyMapInteractiveOptions } from './journey-map/renderer';
|
|
409
|
+
|
|
361
410
|
export { resolveOrgImports } from './org/resolver';
|
|
362
411
|
export type {
|
|
363
412
|
ReadFileFn,
|
|
@@ -436,6 +485,7 @@ export {
|
|
|
436
485
|
applyGroupOrdering,
|
|
437
486
|
groupMessagesBySection,
|
|
438
487
|
buildNoteMessageMap,
|
|
488
|
+
collectNoteLineNumbers,
|
|
439
489
|
} from './sequence/renderer';
|
|
440
490
|
export type {
|
|
441
491
|
RenderStep,
|