@diagrammo/dgmo 0.15.1 → 0.16.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 +9 -9
- package/dist/advanced.cjs +479 -454
- package/dist/advanced.d.cts +34 -35
- package/dist/advanced.d.ts +34 -35
- package/dist/advanced.js +479 -453
- package/dist/auto.cjs +374 -352
- package/dist/auto.js +103 -103
- package/dist/auto.mjs +374 -352
- package/dist/cli.cjs +140 -140
- package/dist/editor.cjs +8 -9
- package/dist/editor.js +8 -9
- package/dist/highlight.cjs +8 -9
- package/dist/highlight.js +8 -9
- package/dist/index.cjs +365 -342
- package/dist/index.js +365 -342
- package/dist/internal.cjs +479 -454
- package/dist/internal.d.cts +34 -35
- package/dist/internal.d.ts +34 -35
- package/dist/internal.js +479 -453
- package/dist/pert.d.cts +2 -2
- package/dist/pert.d.ts +2 -2
- package/docs/language-reference.md +83 -66
- package/gallery/fixtures/area.dgmo +3 -3
- package/gallery/fixtures/bar-stacked.dgmo +5 -5
- package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
- package/gallery/fixtures/c4-full.dgmo +8 -8
- package/gallery/fixtures/class-full.dgmo +2 -2
- package/gallery/fixtures/doughnut.dgmo +6 -6
- package/gallery/fixtures/flowchart-colors.dgmo +3 -3
- package/gallery/fixtures/function.dgmo +3 -3
- package/gallery/fixtures/gantt-full.dgmo +9 -9
- package/gallery/fixtures/gantt.dgmo +7 -7
- package/gallery/fixtures/infra-full.dgmo +6 -6
- package/gallery/fixtures/infra.dgmo +2 -2
- package/gallery/fixtures/kanban.dgmo +9 -9
- package/gallery/fixtures/line.dgmo +2 -2
- package/gallery/fixtures/multi-line.dgmo +3 -3
- package/gallery/fixtures/org-full.dgmo +6 -6
- package/gallery/fixtures/quadrant.dgmo +2 -2
- package/gallery/fixtures/sankey.dgmo +9 -9
- package/gallery/fixtures/scatter.dgmo +3 -3
- package/gallery/fixtures/sequence-tags-protocols.dgmo +8 -8
- package/gallery/fixtures/sequence-tags.dgmo +7 -7
- package/gallery/fixtures/sitemap-full.dgmo +7 -7
- package/gallery/fixtures/slope.dgmo +5 -5
- package/gallery/fixtures/spr-eras.dgmo +9 -9
- package/gallery/fixtures/timeline.dgmo +3 -3
- package/gallery/fixtures/venn.dgmo +3 -3
- package/package.json +1 -1
- package/src/advanced.ts +0 -1
- package/src/boxes-and-lines/renderer.ts +5 -1
- package/src/c4/parser.ts +1 -1
- package/src/c4/renderer.ts +15 -8
- package/src/chart.ts +18 -9
- package/src/class/parser.ts +7 -6
- package/src/class/renderer.ts +17 -6
- package/src/cli.ts +6 -6
- package/src/completion.ts +13 -3
- package/src/cycle/parser.ts +14 -0
- package/src/cycle/renderer.ts +6 -3
- package/src/d3.ts +86 -46
- package/src/echarts.ts +26 -9
- package/src/editor/dgmo.grammar +1 -3
- package/src/editor/dgmo.grammar.js +8 -8
- package/src/editor/dgmo.grammar.terms.js +11 -12
- package/src/editor/highlight-api.ts +0 -1
- package/src/editor/highlight.ts +0 -1
- package/src/er/parser.ts +18 -11
- package/src/er/renderer.ts +19 -7
- package/src/gantt/parser.ts +1 -1
- package/src/gantt/renderer.ts +7 -4
- package/src/graph/flowchart-parser.ts +18 -84
- package/src/graph/flowchart-renderer.ts +3 -8
- package/src/graph/layout.ts +0 -2
- package/src/graph/state-parser.ts +17 -62
- package/src/graph/state-renderer.ts +3 -8
- package/src/infra/parser.ts +21 -11
- package/src/infra/renderer.ts +7 -4
- package/src/journey-map/parser.ts +10 -3
- package/src/journey-map/renderer.ts +3 -1
- package/src/kanban/parser.ts +10 -6
- package/src/kanban/renderer.ts +3 -1
- package/src/mindmap/parser.ts +2 -2
- package/src/mindmap/renderer.ts +2 -1
- package/src/org/parser.ts +2 -2
- package/src/org/renderer.ts +4 -3
- package/src/pert/parser.ts +7 -7
- package/src/pert/renderer.ts +7 -2
- package/src/pert/types.ts +1 -1
- package/src/pyramid/parser.ts +12 -0
- package/src/raci/parser.ts +40 -10
- package/src/raci/renderer.ts +2 -1
- package/src/raci/types.ts +4 -3
- package/src/ring/parser.ts +12 -0
- package/src/sequence/parser.ts +15 -9
- package/src/sequence/renderer.ts +1 -1
- package/src/sitemap/layout.ts +0 -2
- package/src/sitemap/parser.ts +11 -37
- package/src/sitemap/renderer.ts +13 -13
- package/src/sitemap/types.ts +0 -1
- package/src/tech-radar/renderer.ts +5 -3
- package/src/tech-radar/types.ts +2 -0
- package/src/utils/arrows.ts +3 -28
- package/src/utils/legend-d3.ts +12 -6
- package/src/utils/legend-layout.ts +1 -1
- package/src/utils/legend-types.ts +1 -1
- package/src/utils/parsing.ts +64 -35
- package/src/utils/tag-groups.ts +98 -18
- package/src/wireframe/parser.ts +2 -2
package/src/infra/renderer.ts
CHANGED
|
@@ -2024,7 +2024,8 @@ function renderLegend(
|
|
|
2024
2024
|
palette: PaletteColors,
|
|
2025
2025
|
isDark: boolean,
|
|
2026
2026
|
activeGroup: string | null,
|
|
2027
|
-
playback?: InfraPlaybackState
|
|
2027
|
+
playback?: InfraPlaybackState,
|
|
2028
|
+
exportMode = false
|
|
2028
2029
|
) {
|
|
2029
2030
|
if (legendGroups.length === 0 && !playback) return;
|
|
2030
2031
|
|
|
@@ -2049,7 +2050,7 @@ function renderLegend(
|
|
|
2049
2050
|
const legendConfig: LegendConfig = {
|
|
2050
2051
|
groups: allGroups,
|
|
2051
2052
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
2052
|
-
mode: '
|
|
2053
|
+
mode: exportMode ? 'export' : 'preview',
|
|
2053
2054
|
showEmptyGroups: true,
|
|
2054
2055
|
};
|
|
2055
2056
|
const legendState: LegendState = { activeGroup };
|
|
@@ -2394,7 +2395,8 @@ export function renderInfra(
|
|
|
2394
2395
|
palette,
|
|
2395
2396
|
isDark,
|
|
2396
2397
|
activeGroup ?? null,
|
|
2397
|
-
playback ?? undefined
|
|
2398
|
+
playback ?? undefined,
|
|
2399
|
+
exportMode
|
|
2398
2400
|
);
|
|
2399
2401
|
// Re-enable pointer events on interactive legend elements
|
|
2400
2402
|
legendSvg
|
|
@@ -2410,7 +2412,8 @@ export function renderInfra(
|
|
|
2410
2412
|
palette,
|
|
2411
2413
|
isDark,
|
|
2412
2414
|
activeGroup ?? null,
|
|
2413
|
-
playback ?? undefined
|
|
2415
|
+
playback ?? undefined,
|
|
2416
|
+
exportMode
|
|
2414
2417
|
);
|
|
2415
2418
|
}
|
|
2416
2419
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PaletteColors } from '../palettes';
|
|
2
|
+
import { resolveColorWithDiagnostic } from '../colors';
|
|
2
3
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
3
4
|
import {
|
|
4
5
|
matchTagBlockHeading,
|
|
@@ -138,8 +139,14 @@ export function parseJourneyMap(
|
|
|
138
139
|
const key = part.substring(0, colonIdx).trim().toLowerCase();
|
|
139
140
|
const value = part.substring(colonIdx + 1).trim();
|
|
140
141
|
if (key === 'color') {
|
|
141
|
-
|
|
142
|
-
personaColor =
|
|
142
|
+
// Resolve the color name directly (no synthetic parens wrap).
|
|
143
|
+
personaColor =
|
|
144
|
+
resolveColorWithDiagnostic(
|
|
145
|
+
value,
|
|
146
|
+
lineNumber,
|
|
147
|
+
result.diagnostics,
|
|
148
|
+
palette
|
|
149
|
+
) ?? undefined;
|
|
143
150
|
}
|
|
144
151
|
}
|
|
145
152
|
}
|
|
@@ -209,7 +216,7 @@ export function parseJourneyMap(
|
|
|
209
216
|
if (!color) {
|
|
210
217
|
warn(
|
|
211
218
|
lineNumber,
|
|
212
|
-
`Expected 'Value
|
|
219
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
213
220
|
);
|
|
214
221
|
continue;
|
|
215
222
|
}
|
|
@@ -36,6 +36,7 @@ export interface JourneyMapInteractiveOptions {
|
|
|
36
36
|
collapsedPhases?: Set<string>;
|
|
37
37
|
/** Called when a phase is toggled */
|
|
38
38
|
onPhaseToggle?: (phaseName: string) => void;
|
|
39
|
+
exportMode?: boolean;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// ============================================================
|
|
@@ -313,7 +314,7 @@ export function renderJourneyMap(
|
|
|
313
314
|
titleRelation: 'inline-with-title',
|
|
314
315
|
},
|
|
315
316
|
titleWidth: 0,
|
|
316
|
-
mode:
|
|
317
|
+
mode: options?.exportMode ? 'export' : 'preview',
|
|
317
318
|
};
|
|
318
319
|
|
|
319
320
|
const legendState: LegendState = { activeGroup: effectiveActiveGroup };
|
|
@@ -1559,6 +1560,7 @@ export function renderJourneyMapForExport(
|
|
|
1559
1560
|
const container = document.createElement('div');
|
|
1560
1561
|
renderJourneyMap(container, parsed, palette, isDark, {
|
|
1561
1562
|
exportDims: { width: layout.totalWidth, height: layout.totalHeight },
|
|
1563
|
+
exportMode: true,
|
|
1562
1564
|
});
|
|
1563
1565
|
|
|
1564
1566
|
const svgEl = container.querySelector('svg');
|
package/src/kanban/parser.ts
CHANGED
|
@@ -26,10 +26,11 @@ import type {
|
|
|
26
26
|
// Regex patterns
|
|
27
27
|
// ============================================================
|
|
28
28
|
|
|
29
|
-
// [Column Name], [Column Name]
|
|
29
|
+
// [Column Name], [Column Name] color, [Column Name] as <alias>, [Column Name] | wip: 3, etc.
|
|
30
|
+
// Universal §1.5 trailing-token: color is a bare token after `]`.
|
|
30
31
|
// Captures: [1]=label [2]=color [3]=alias (TD-18) [4]=pipe meta
|
|
31
32
|
const COLUMN_RE =
|
|
32
|
-
/^\[(.+?)\](?:\s
|
|
33
|
+
/^\[(.+?)\](?:\s+(\S+))?(?:\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11}))?\s*(?:\|\s*(.+))?$/;
|
|
33
34
|
// Legacy delimiter
|
|
34
35
|
const LEGACY_COLUMN_RE = /^==\s+(.+?)\s*(?:\[wip:\s*(\d+)\])?\s*==$/;
|
|
35
36
|
|
|
@@ -180,7 +181,7 @@ export function parseKanban(
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
// Tag group entries (indented Value
|
|
184
|
+
// Tag group entries (indented Value color under tag heading)
|
|
184
185
|
// First entry is the default unless another is marked `default`
|
|
185
186
|
if (currentTagGroup && !contentStarted) {
|
|
186
187
|
const indent = measureIndent(line);
|
|
@@ -190,7 +191,7 @@ export function parseKanban(
|
|
|
190
191
|
if (!color) {
|
|
191
192
|
warn(
|
|
192
193
|
lineNumber,
|
|
193
|
-
`Expected 'Value
|
|
194
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
194
195
|
);
|
|
195
196
|
continue;
|
|
196
197
|
}
|
|
@@ -247,9 +248,12 @@ export function parseKanban(
|
|
|
247
248
|
|
|
248
249
|
columnCounter++;
|
|
249
250
|
const colName = columnMatch[1].trim();
|
|
250
|
-
|
|
251
|
+
// Trailing token after `]` must be a recognized color word (§1.5).
|
|
252
|
+
// If it isn't, the line is malformed — emit the standard diagnostic.
|
|
253
|
+
const rawTrailing = columnMatch[2]?.trim();
|
|
254
|
+
const colColor = rawTrailing
|
|
251
255
|
? resolveColorWithDiagnostic(
|
|
252
|
-
|
|
256
|
+
rawTrailing,
|
|
253
257
|
lineNumber,
|
|
254
258
|
result.diagnostics,
|
|
255
259
|
palette
|
package/src/kanban/renderer.ts
CHANGED
|
@@ -37,6 +37,7 @@ interface KanbanInteractiveOptions {
|
|
|
37
37
|
collapsedLanes?: Set<string>;
|
|
38
38
|
collapsedColumns?: Set<string>;
|
|
39
39
|
compactMeta?: boolean;
|
|
40
|
+
exportMode?: boolean;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// ============================================================
|
|
@@ -330,7 +331,7 @@ export function renderKanban(
|
|
|
330
331
|
const legendConfig: LegendConfig = {
|
|
331
332
|
groups: parsed.tagGroups,
|
|
332
333
|
position: { placement: 'top-center', titleRelation: 'inline-with-title' },
|
|
333
|
-
mode:
|
|
334
|
+
mode: options?.exportMode ? 'export' : 'preview',
|
|
334
335
|
};
|
|
335
336
|
const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
|
|
336
337
|
const legendG = svg
|
|
@@ -682,6 +683,7 @@ export function renderKanbanForExport(
|
|
|
682
683
|
const container = document.createElement('div');
|
|
683
684
|
renderKanban(container, parsed, palette, isDark, {
|
|
684
685
|
exportDims: { width: layout.totalWidth, height: layout.totalHeight },
|
|
686
|
+
exportMode: true,
|
|
685
687
|
});
|
|
686
688
|
|
|
687
689
|
const svgEl = container.querySelector('svg');
|
package/src/mindmap/parser.ts
CHANGED
|
@@ -179,7 +179,7 @@ export function parseMindmap(
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
// Tag group entries (indented Value
|
|
182
|
+
// Tag group entries (indented Value color under tag heading)
|
|
183
183
|
if (currentTagGroup && !contentStarted) {
|
|
184
184
|
const indent = measureIndent(line);
|
|
185
185
|
if (indent > 0) {
|
|
@@ -188,7 +188,7 @@ export function parseMindmap(
|
|
|
188
188
|
if (!color) {
|
|
189
189
|
pushError(
|
|
190
190
|
lineNumber,
|
|
191
|
-
`Expected 'Value
|
|
191
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
192
192
|
);
|
|
193
193
|
continue;
|
|
194
194
|
}
|
package/src/mindmap/renderer.ts
CHANGED
|
@@ -95,6 +95,7 @@ export function renderMindmap(
|
|
|
95
95
|
onToggleDescriptions?: (active: boolean) => void;
|
|
96
96
|
controlsExpanded?: boolean;
|
|
97
97
|
onToggleControlsExpand?: () => void;
|
|
98
|
+
exportMode?: boolean;
|
|
98
99
|
}
|
|
99
100
|
): void {
|
|
100
101
|
const isExport = !!exportDims;
|
|
@@ -234,7 +235,7 @@ export function renderMindmap(
|
|
|
234
235
|
};
|
|
235
236
|
}),
|
|
236
237
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
237
|
-
mode: '
|
|
238
|
+
mode: options?.exportMode ? 'export' : 'preview',
|
|
238
239
|
controlsGroup: controlsToggles,
|
|
239
240
|
};
|
|
240
241
|
const legendState: LegendState = {
|
package/src/org/parser.ts
CHANGED
|
@@ -235,7 +235,7 @@ export function parseOrg(content: string, palette?: PaletteColors): ParsedOrg {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
// Tag group entries (indented Value
|
|
238
|
+
// Tag group entries (indented Value color under tag heading)
|
|
239
239
|
// First entry is the default unless another is marked `default`
|
|
240
240
|
if (currentTagGroup && !contentStarted) {
|
|
241
241
|
const indent = measureIndent(line);
|
|
@@ -245,7 +245,7 @@ export function parseOrg(content: string, palette?: PaletteColors): ParsedOrg {
|
|
|
245
245
|
if (!color) {
|
|
246
246
|
pushError(
|
|
247
247
|
lineNumber,
|
|
248
|
-
`Expected 'Value
|
|
248
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
249
249
|
);
|
|
250
250
|
continue;
|
|
251
251
|
}
|
package/src/org/renderer.ts
CHANGED
|
@@ -110,7 +110,8 @@ export function renderOrg(
|
|
|
110
110
|
exportDims?: { width?: number; height?: number },
|
|
111
111
|
activeTagGroup?: string | null,
|
|
112
112
|
hiddenAttributes?: Set<string>,
|
|
113
|
-
ancestorPath?: AncestorInfo[]
|
|
113
|
+
ancestorPath?: AncestorInfo[],
|
|
114
|
+
exportMode?: boolean
|
|
114
115
|
): void {
|
|
115
116
|
// Clear existing content
|
|
116
117
|
d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
|
|
@@ -759,7 +760,7 @@ export function renderOrg(
|
|
|
759
760
|
},
|
|
760
761
|
],
|
|
761
762
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
762
|
-
mode: '
|
|
763
|
+
mode: exportMode ? 'export' : 'preview',
|
|
763
764
|
};
|
|
764
765
|
const singleState: LegendState = { activeGroup: lg.name };
|
|
765
766
|
const groupG = legendParentBase
|
|
@@ -783,7 +784,7 @@ export function renderOrg(
|
|
|
783
784
|
const legendConfig: LegendConfig = {
|
|
784
785
|
groups,
|
|
785
786
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
786
|
-
mode: '
|
|
787
|
+
mode: exportMode ? 'export' : 'preview',
|
|
787
788
|
capsulePillAddonWidth: eyeAddonWidth,
|
|
788
789
|
};
|
|
789
790
|
const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
|
package/src/pert/parser.ts
CHANGED
|
@@ -448,7 +448,7 @@ export interface ParsePertOptions {
|
|
|
448
448
|
now?: Date;
|
|
449
449
|
/**
|
|
450
450
|
* Active palette — used when resolving color names on `tag` entries
|
|
451
|
-
* (e.g. `High
|
|
451
|
+
* (e.g. `High red` → palette.colors.red). Optional; when omitted the
|
|
452
452
|
* universal default color map is used.
|
|
453
453
|
*/
|
|
454
454
|
palette?: PaletteColors;
|
|
@@ -503,7 +503,7 @@ export function parsePert(
|
|
|
503
503
|
|
|
504
504
|
/**
|
|
505
505
|
* Tag groups declared at the top of the diagram. A `tag …` heading
|
|
506
|
-
* opens a block; entries (indented `Value
|
|
506
|
+
* opens a block; entries (indented `Value color` lines) accumulate
|
|
507
507
|
* until the first non-tag content line closes it.
|
|
508
508
|
*/
|
|
509
509
|
const tagGroups: TagGroup[] = [];
|
|
@@ -576,7 +576,7 @@ export function parsePert(
|
|
|
576
576
|
// layer is responsible for routing.
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
-
// ── Tag-block phase. `tag Priority as p\n High
|
|
579
|
+
// ── Tag-block phase. `tag Priority as p\n High red\n Low green`
|
|
580
580
|
// lives BEFORE diagram content; once any group / activity / arrow
|
|
581
581
|
// is seen, `contentStarted` flips and further `tag …` headings
|
|
582
582
|
// emit an error.
|
|
@@ -599,7 +599,7 @@ export function parsePert(
|
|
|
599
599
|
);
|
|
600
600
|
}
|
|
601
601
|
tagGroups.push(currentTagGroup);
|
|
602
|
-
// Inline values (e.g. `tag Priority as p Low
|
|
602
|
+
// Inline values (e.g. `tag Priority as p Low green, High red`).
|
|
603
603
|
if (tagBlockMatch.inlineValues) {
|
|
604
604
|
for (const raw of tagBlockMatch.inlineValues) {
|
|
605
605
|
const { text, isDefault } = stripDefaultModifier(raw);
|
|
@@ -612,7 +612,7 @@ export function parsePert(
|
|
|
612
612
|
if (!color) {
|
|
613
613
|
warn(
|
|
614
614
|
lineNumber,
|
|
615
|
-
`Expected 'Value
|
|
615
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
616
616
|
);
|
|
617
617
|
continue;
|
|
618
618
|
}
|
|
@@ -624,7 +624,7 @@ export function parsePert(
|
|
|
624
624
|
}
|
|
625
625
|
continue;
|
|
626
626
|
}
|
|
627
|
-
// Indented `Value
|
|
627
|
+
// Indented `Value color` entry under an open tag block.
|
|
628
628
|
if (currentTagGroup && indent > 0) {
|
|
629
629
|
const { text, isDefault } = stripDefaultModifier(trimmed);
|
|
630
630
|
const { label, color } = extractColor(
|
|
@@ -636,7 +636,7 @@ export function parsePert(
|
|
|
636
636
|
if (!color) {
|
|
637
637
|
warn(
|
|
638
638
|
lineNumber,
|
|
639
|
-
`Expected 'Value
|
|
639
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
640
640
|
);
|
|
641
641
|
continue;
|
|
642
642
|
}
|
package/src/pert/renderer.ts
CHANGED
|
@@ -388,6 +388,8 @@ export interface PertRenderOptions {
|
|
|
388
388
|
* through to the parsed `active-tag` directive.
|
|
389
389
|
*/
|
|
390
390
|
activeTagOverride?: string | null;
|
|
391
|
+
/** True when rendering for export — strips collapsed pills and cog from legend. */
|
|
392
|
+
exportMode?: boolean;
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
export function renderPert(
|
|
@@ -568,6 +570,7 @@ export function renderPert(
|
|
|
568
570
|
y: tagLegendY,
|
|
569
571
|
width: exportWidth,
|
|
570
572
|
activeGroup: tagLegendActive,
|
|
573
|
+
exportMode: options.exportMode,
|
|
571
574
|
});
|
|
572
575
|
}
|
|
573
576
|
|
|
@@ -681,6 +684,7 @@ export function renderPertForExport(
|
|
|
681
684
|
title: hasTitle ? parsed.title : null,
|
|
682
685
|
subtitle: resolved.projectSubtitle,
|
|
683
686
|
exportDims: { width: exportWidth, height: exportHeight },
|
|
687
|
+
exportMode: true,
|
|
684
688
|
});
|
|
685
689
|
const svgEl = container.querySelector('svg');
|
|
686
690
|
if (!svgEl) return '';
|
|
@@ -2990,6 +2994,7 @@ interface TagLegendArgs {
|
|
|
2990
2994
|
y: number;
|
|
2991
2995
|
width: number;
|
|
2992
2996
|
activeGroup: string | null;
|
|
2997
|
+
exportMode?: boolean;
|
|
2993
2998
|
}
|
|
2994
2999
|
|
|
2995
3000
|
/**
|
|
@@ -3008,7 +3013,7 @@ function renderTagLegendRow(
|
|
|
3008
3013
|
): void {
|
|
3009
3014
|
if (resolved.tagGroups.length === 0) return;
|
|
3010
3015
|
|
|
3011
|
-
const { x, y, width, activeGroup } = args;
|
|
3016
|
+
const { x, y, width, activeGroup, exportMode } = args;
|
|
3012
3017
|
const groups = resolved.tagGroups.map((g) => ({
|
|
3013
3018
|
name: g.name,
|
|
3014
3019
|
entries: g.entries.map((e) => ({ value: e.value, color: e.color })),
|
|
@@ -3024,7 +3029,7 @@ function renderTagLegendRow(
|
|
|
3024
3029
|
{
|
|
3025
3030
|
groups,
|
|
3026
3031
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
3027
|
-
mode: '
|
|
3032
|
+
mode: exportMode ? 'export' : 'preview',
|
|
3028
3033
|
},
|
|
3029
3034
|
{ activeGroup },
|
|
3030
3035
|
palette,
|
package/src/pert/types.ts
CHANGED
|
@@ -215,7 +215,7 @@ export interface ParsedPert {
|
|
|
215
215
|
groups: PertGroup[];
|
|
216
216
|
/**
|
|
217
217
|
* Tag groups declared at the top of the diagram (`tag Priority as p
|
|
218
|
-
* High
|
|
218
|
+
* High red, Low green`). Drive node fill via `resolveTagColor()`.
|
|
219
219
|
* Empty when no `tag` blocks are declared.
|
|
220
220
|
*/
|
|
221
221
|
tagGroups: TagGroup[];
|
package/src/pyramid/parser.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
measureIndent,
|
|
8
8
|
parseFirstLine,
|
|
9
9
|
parsePipeMetadata,
|
|
10
|
+
peelTrailingColorName,
|
|
10
11
|
tryParseSharedOption,
|
|
11
12
|
} from '../utils/parsing';
|
|
12
13
|
import type { ParsedPyramid, PyramidLayer } from './types';
|
|
@@ -146,6 +147,17 @@ export function parsePyramid(content: string): ParsedPyramid {
|
|
|
146
147
|
continue;
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
// Universal trailing-token shortcut: `Label color` is equivalent to
|
|
151
|
+
// `Label | color: <name>` when color is the only metadata key (§1.5).
|
|
152
|
+
if (!color) {
|
|
153
|
+
const { label: stripped, colorName: shortcutColor } =
|
|
154
|
+
peelTrailingColorName(label);
|
|
155
|
+
if (shortcutColor) {
|
|
156
|
+
color = shortcutColor;
|
|
157
|
+
label = stripped;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
149
161
|
currentLayer = {
|
|
150
162
|
label,
|
|
151
163
|
lineNumber: lineNum,
|
package/src/raci/parser.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
measureIndent,
|
|
25
25
|
parseFirstLine,
|
|
26
26
|
parsePipeMetadata,
|
|
27
|
+
peelTrailingColorName,
|
|
27
28
|
OPTION_NOCOLON_RE,
|
|
28
29
|
tryParseSharedOption,
|
|
29
30
|
} from '../utils/parsing';
|
|
@@ -82,10 +83,10 @@ const KNOWN_BOOLEANS = new Set<string>([
|
|
|
82
83
|
...Object.keys(VARIANT_LOCK_DIRECTIVES),
|
|
83
84
|
]);
|
|
84
85
|
|
|
85
|
-
// Allow optional trailing
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
const PHASE_RE = /^\[(.+?)\]
|
|
86
|
+
// Allow optional trailing color shortcut and/or pipe metadata after the
|
|
87
|
+
// bracket: `[Voyage] blue | desc: …` (per §1.5 universal trailing-token
|
|
88
|
+
// rule + the modern cycle/pyramid/ring/journey-map/b&l idiom).
|
|
89
|
+
const PHASE_RE = /^\[(.+?)\](?:\s+(\S+))?(?:\s*\|\s*(.+))?\s*$/;
|
|
89
90
|
const ROLE_ASSIGNMENT_RE = /^([^:]+):\s*(.*)$/;
|
|
90
91
|
|
|
91
92
|
/**
|
|
@@ -382,10 +383,10 @@ export function parseRaci(
|
|
|
382
383
|
// Strip a possible trailing comma (user habit tolerance,
|
|
383
384
|
// matches `collectIndentedValues`).
|
|
384
385
|
const stripped = nextTrim.replace(/,\s*$/, '');
|
|
385
|
-
// Optional pipe metadata: `Cap | color: blue` —
|
|
386
|
-
//
|
|
386
|
+
// Optional pipe metadata: `Cap | color: blue` — long form.
|
|
387
|
+
// Optional trailing-token shortcut: `Cap blue` — short form (§1.5).
|
|
387
388
|
const segments = stripped.split('|').map((s) => s.trim());
|
|
388
|
-
|
|
389
|
+
let roleLabel = segments[0] ?? '';
|
|
389
390
|
let roleColor: string | undefined;
|
|
390
391
|
if (segments.length > 1) {
|
|
391
392
|
const meta = parsePipeMetadata(segments);
|
|
@@ -398,6 +399,20 @@ export function parseRaci(
|
|
|
398
399
|
);
|
|
399
400
|
}
|
|
400
401
|
}
|
|
402
|
+
// Apply shortcut only when pipe metadata didn't already set color.
|
|
403
|
+
if (!roleColor) {
|
|
404
|
+
const { label: stripLabel, colorName: shortcutColor } =
|
|
405
|
+
peelTrailingColorName(roleLabel);
|
|
406
|
+
if (shortcutColor) {
|
|
407
|
+
roleColor = resolveColorWithDiagnostic(
|
|
408
|
+
shortcutColor,
|
|
409
|
+
j + 1,
|
|
410
|
+
result.diagnostics,
|
|
411
|
+
palette
|
|
412
|
+
);
|
|
413
|
+
roleLabel = stripLabel;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
401
416
|
if (roleLabel) getOrAddRole(roleLabel, j + 1, roleColor);
|
|
402
417
|
}
|
|
403
418
|
i = j - 1; // outer loop's i++ lands on the first non-block line
|
|
@@ -469,10 +484,13 @@ export function parseRaci(
|
|
|
469
484
|
errorAt(lineNumber, 'Phase label is empty.');
|
|
470
485
|
continue;
|
|
471
486
|
}
|
|
472
|
-
//
|
|
487
|
+
// PHASE_RE captures: 1=label, 2=optional trailing-token color, 3=pipe meta.
|
|
488
|
+
// Long pipe form (`[Voyage] | color: blue`) wins over the shortcut.
|
|
473
489
|
let phaseColor: string | undefined;
|
|
474
|
-
|
|
475
|
-
|
|
490
|
+
const trailingToken = phaseMatch[2];
|
|
491
|
+
const pipeMeta = phaseMatch[3];
|
|
492
|
+
if (pipeMeta) {
|
|
493
|
+
const meta = parsePipeMetadata(['', pipeMeta]);
|
|
476
494
|
if (meta['color']) {
|
|
477
495
|
phaseColor = resolveColorWithDiagnostic(
|
|
478
496
|
meta['color'],
|
|
@@ -482,6 +500,18 @@ export function parseRaci(
|
|
|
482
500
|
);
|
|
483
501
|
}
|
|
484
502
|
}
|
|
503
|
+
if (!phaseColor && trailingToken) {
|
|
504
|
+
// Trailing token must be a recognized color word, or it's a parse error.
|
|
505
|
+
const { colorName } = peelTrailingColorName(`x ${trailingToken}`);
|
|
506
|
+
if (colorName) {
|
|
507
|
+
phaseColor = resolveColorWithDiagnostic(
|
|
508
|
+
colorName,
|
|
509
|
+
lineNumber,
|
|
510
|
+
result.diagnostics,
|
|
511
|
+
palette
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
485
515
|
currentPhase = {
|
|
486
516
|
id: normalizeName(display),
|
|
487
517
|
displayName: display,
|
package/src/raci/renderer.ts
CHANGED
|
@@ -602,7 +602,8 @@ export function renderRaci(
|
|
|
602
602
|
parsed.roles.forEach((roleId, i) => {
|
|
603
603
|
const cx = roleX(i) + COLUMN_INSET;
|
|
604
604
|
const cw = roleColW - 2 * COLUMN_INSET;
|
|
605
|
-
// Per-role color from `Cap
|
|
605
|
+
// Per-role color from `Cap blue` trailing-token (or `Cap | color: blue`)
|
|
606
|
+
// syntax. When the user provides
|
|
606
607
|
// one, it wins; otherwise rotate through marker-safe accents so
|
|
607
608
|
// each column has a subtle visual identity instead of every column
|
|
608
609
|
// reading as the same neutral gray.
|
package/src/raci/types.ts
CHANGED
|
@@ -67,9 +67,10 @@ export interface ParsedRaci {
|
|
|
67
67
|
/** Display name for each role (parallel to `roles`). */
|
|
68
68
|
roleDisplayNames: string[];
|
|
69
69
|
/**
|
|
70
|
-
* Optional per-role palette color from `Cap
|
|
71
|
-
* roles block
|
|
72
|
-
*
|
|
70
|
+
* Optional per-role palette color from the `Cap blue` trailing-token
|
|
71
|
+
* suffix in the roles block (or the long pipe form `Cap | color: blue`).
|
|
72
|
+
* Parallel to `roles`; entries default to `undefined` (renderer falls
|
|
73
|
+
* back to the neutral column tint).
|
|
73
74
|
*/
|
|
74
75
|
roleColors: Array<string | undefined>;
|
|
75
76
|
phases: RaciPhase[];
|
package/src/ring/parser.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
measureIndent,
|
|
9
9
|
parseFirstLine,
|
|
10
10
|
parsePipeMetadata,
|
|
11
|
+
peelTrailingColorName,
|
|
11
12
|
tryParseSharedOption,
|
|
12
13
|
PIPE_KEY_VALUE_PREFIX_RE,
|
|
13
14
|
PIPE_LIKELY_STRUCTURED_TAIL_RE,
|
|
@@ -175,6 +176,17 @@ export function parseRing(content: string): ParsedRing {
|
|
|
175
176
|
continue;
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
// Universal trailing-token shortcut: `Label color` equivalent to
|
|
180
|
+
// `Label | color: <name>` when color is not already set (§1.5).
|
|
181
|
+
if (!color) {
|
|
182
|
+
const { label: stripped, colorName: shortcutColor } =
|
|
183
|
+
peelTrailingColorName(label);
|
|
184
|
+
if (shortcutColor) {
|
|
185
|
+
color = shortcutColor;
|
|
186
|
+
label = stripped;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
178
190
|
currentLayer = {
|
|
179
191
|
label,
|
|
180
192
|
lineNumber: lineNum,
|
package/src/sequence/parser.ts
CHANGED
|
@@ -215,7 +215,10 @@ const IS_A_PATTERN = /^([^:]+?)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
|
215
215
|
const POSITION_ONLY_PATTERN = /^([^:]+?)\s+position\s+(-?\d+)$/i;
|
|
216
216
|
|
|
217
217
|
// Colored participant declaration — e.g. "Tapin2(green)", "API(blue)"
|
|
218
|
-
|
|
218
|
+
// Scoped to recognized 11-name palette colors only (§1.5) so legitimate
|
|
219
|
+
// `funcCall(arg)` lines don't trigger the legacy-color diagnostic.
|
|
220
|
+
const COLORED_PARTICIPANT_PATTERN =
|
|
221
|
+
/^(\S+?)\((red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\)\s*$/;
|
|
219
222
|
|
|
220
223
|
// Group heading pattern — "[Backend]", "[Backend] | t: Product"
|
|
221
224
|
// Group 1: name (no ] or | inside brackets), Group 2: color in parens, Group 3: after-bracket text
|
|
@@ -678,7 +681,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
678
681
|
if (groupColor) {
|
|
679
682
|
pushWarning(
|
|
680
683
|
lineNumber,
|
|
681
|
-
`(${groupColor}) color syntax removed from sequence diagrams — use 'tag:' groups for coloring`
|
|
684
|
+
`'(${groupColor})' parens-color syntax removed from sequence diagrams — use 'tag:' groups for coloring`
|
|
682
685
|
);
|
|
683
686
|
}
|
|
684
687
|
contentStarted = true;
|
|
@@ -765,7 +768,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
765
768
|
continue;
|
|
766
769
|
}
|
|
767
770
|
|
|
768
|
-
// Tag group entries (indented Value
|
|
771
|
+
// Tag group entries (indented Value color under tag heading)
|
|
769
772
|
// First entry is the default unless another is marked `default`
|
|
770
773
|
if (currentTagGroup && !contentStarted && measureIndent(raw) > 0) {
|
|
771
774
|
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
@@ -778,7 +781,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
778
781
|
if (!color) {
|
|
779
782
|
pushError(
|
|
780
783
|
lineNumber,
|
|
781
|
-
`Expected 'Value
|
|
784
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
782
785
|
);
|
|
783
786
|
continue;
|
|
784
787
|
}
|
|
@@ -807,11 +810,14 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
807
810
|
blockStack.pop();
|
|
808
811
|
}
|
|
809
812
|
const labelRaw = sectionMatch[1].trim();
|
|
810
|
-
|
|
813
|
+
// Scoped to recognized 11-name palette colors only (§1.5).
|
|
814
|
+
const colorMatch = labelRaw.match(
|
|
815
|
+
/^(.+?)\((red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\)$/
|
|
816
|
+
);
|
|
811
817
|
if (colorMatch) {
|
|
812
818
|
pushWarning(
|
|
813
819
|
lineNumber,
|
|
814
|
-
`(${colorMatch[2]
|
|
820
|
+
`'(${colorMatch[2]})' parens-color syntax removed from sequence diagrams — use 'tag:' groups for coloring`
|
|
815
821
|
);
|
|
816
822
|
}
|
|
817
823
|
contentStarted = true;
|
|
@@ -1016,8 +1022,8 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1016
1022
|
continue;
|
|
1017
1023
|
}
|
|
1018
1024
|
|
|
1019
|
-
//
|
|
1020
|
-
//
|
|
1025
|
+
// Legacy `Name(color)` participant declaration at any level (§1.5 hard
|
|
1026
|
+
// break). Scoped to the 11-name palette so `funcCall(arg)` doesn't trip.
|
|
1021
1027
|
const { core: colorCore, meta: colorMeta } = splitPipe(trimmed, lineNumber);
|
|
1022
1028
|
const coloredMatch = colorCore.match(COLORED_PARTICIPANT_PATTERN);
|
|
1023
1029
|
if (coloredMatch && !ARROW_PATTERN.test(colorCore)) {
|
|
@@ -1025,7 +1031,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1025
1031
|
const color = coloredMatch[2].trim();
|
|
1026
1032
|
pushError(
|
|
1027
1033
|
lineNumber,
|
|
1028
|
-
`'${id}(${color})' syntax is no longer supported — use 'tag:' groups for coloring`
|
|
1034
|
+
`'${id}(${color})' parens-color syntax is no longer supported — use 'tag:' groups for coloring`
|
|
1029
1035
|
);
|
|
1030
1036
|
contentStarted = true;
|
|
1031
1037
|
const key = addParticipant(id, lineNumber, { metadata: colorMeta });
|
package/src/sequence/renderer.ts
CHANGED
|
@@ -2762,7 +2762,7 @@ export function renderSequenceDiagram(
|
|
|
2762
2762
|
const legendConfig: LegendConfig = {
|
|
2763
2763
|
groups: resolvedGroups,
|
|
2764
2764
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
2765
|
-
mode: '
|
|
2765
|
+
mode: 'preview',
|
|
2766
2766
|
};
|
|
2767
2767
|
const legendState: LegendState = {
|
|
2768
2768
|
activeGroup: activeTagGroup ?? null,
|
package/src/sitemap/layout.ts
CHANGED
|
@@ -41,7 +41,6 @@ export interface SitemapLayoutEdge {
|
|
|
41
41
|
targetId: string;
|
|
42
42
|
points: { x: number; y: number }[];
|
|
43
43
|
label?: string;
|
|
44
|
-
color?: string;
|
|
45
44
|
lineNumber: number;
|
|
46
45
|
/** True for edges deferred from dagre (container endpoints) — use linear curve */
|
|
47
46
|
deferred?: boolean;
|
|
@@ -652,7 +651,6 @@ export function layoutSitemap(
|
|
|
652
651
|
targetId: edge.targetId,
|
|
653
652
|
points,
|
|
654
653
|
label: edge.label,
|
|
655
|
-
color: edge.color,
|
|
656
654
|
lineNumber: edge.lineNumber,
|
|
657
655
|
deferred: deferredSet.has(i) || undefined,
|
|
658
656
|
});
|