@diagrammo/dgmo 0.8.5 → 0.8.6
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/.claude/commands/dgmo.md +33 -0
- package/.cursorrules +20 -2
- package/.github/copilot-instructions.md +20 -2
- package/.windsurfrules +20 -2
- package/AGENTS.md +23 -3
- package/dist/cli.cjs +189 -190
- package/dist/editor.cjs +3 -18
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +3 -18
- package/dist/editor.js.map +1 -1
- package/dist/highlight.cjs +4 -21
- package/dist/highlight.cjs.map +1 -1
- package/dist/highlight.js +4 -21
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +2785 -2996
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -56
- package/dist/index.d.ts +56 -56
- package/dist/index.js +2780 -2989
- package/dist/index.js.map +1 -1
- package/docs/ai-integration.md +1 -1
- package/docs/language-reference.md +97 -25
- package/gallery/fixtures/boxes-and-lines.dgmo +64 -0
- package/package.json +1 -1
- package/src/boxes-and-lines/collapse.ts +78 -0
- package/src/boxes-and-lines/layout.ts +319 -0
- package/src/boxes-and-lines/parser.ts +694 -0
- package/src/boxes-and-lines/renderer.ts +848 -0
- package/src/boxes-and-lines/types.ts +40 -0
- package/src/c4/parser.ts +10 -5
- package/src/c4/renderer.ts +232 -56
- package/src/chart.ts +9 -4
- package/src/cli.ts +6 -5
- package/src/completion.ts +25 -33
- package/src/d3.ts +26 -27
- package/src/dgmo-router.ts +3 -7
- package/src/echarts.ts +38 -2
- package/src/editor/keywords.ts +4 -19
- package/src/er/parser.ts +10 -4
- package/src/gantt/parser.ts +7 -4
- package/src/gantt/renderer.ts +3 -5
- package/src/index.ts +17 -26
- package/src/infra/parser.ts +7 -5
- package/src/infra/renderer.ts +2 -2
- package/src/kanban/parser.ts +7 -5
- package/src/kanban/renderer.ts +43 -18
- package/src/org/parser.ts +7 -4
- package/src/org/renderer.ts +40 -29
- package/src/sequence/parser.ts +11 -5
- package/src/sequence/renderer.ts +114 -45
- package/src/sitemap/parser.ts +8 -4
- package/src/sitemap/renderer.ts +137 -57
- package/src/utils/legend-svg.ts +44 -20
- package/src/utils/parsing.ts +1 -1
- package/src/utils/tag-groups.ts +21 -1
- package/gallery/fixtures/initiative-status-full.dgmo +0 -46
- package/gallery/fixtures/initiative-status-phases.dgmo +0 -29
- package/gallery/fixtures/initiative-status.dgmo +0 -9
- package/src/initiative-status/collapse.ts +0 -76
- package/src/initiative-status/filter.ts +0 -63
- package/src/initiative-status/layout.ts +0 -650
- package/src/initiative-status/parser.ts +0 -629
- package/src/initiative-status/renderer.ts +0 -1199
- package/src/initiative-status/types.ts +0 -57
package/src/completion.ts
CHANGED
|
@@ -273,7 +273,6 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
273
273
|
}),
|
|
274
274
|
],
|
|
275
275
|
['c4', withGlobals()],
|
|
276
|
-
['initiative-status', withGlobals()],
|
|
277
276
|
[
|
|
278
277
|
'state',
|
|
279
278
|
withGlobals({
|
|
@@ -313,6 +312,14 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
313
312
|
dependencies: { description: 'Show dependencies' },
|
|
314
313
|
}),
|
|
315
314
|
],
|
|
315
|
+
[
|
|
316
|
+
'boxes-and-lines',
|
|
317
|
+
withGlobals({
|
|
318
|
+
direction: { description: 'Layout direction', values: ['LR', 'TB'] },
|
|
319
|
+
'active-tag': { description: 'Active tag group name' },
|
|
320
|
+
hide: { description: 'Hide tag:value pairs' },
|
|
321
|
+
}),
|
|
322
|
+
],
|
|
316
323
|
]);
|
|
317
324
|
|
|
318
325
|
// ============================================================
|
|
@@ -351,11 +358,11 @@ const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
|
351
358
|
org: 'Organization chart',
|
|
352
359
|
kanban: 'Kanban board',
|
|
353
360
|
c4: 'C4 architecture diagram',
|
|
354
|
-
'initiative-status': 'Initiative status diagram',
|
|
355
361
|
state: 'State diagram',
|
|
356
362
|
sitemap: 'Sitemap diagram',
|
|
357
363
|
infra: 'Infrastructure diagram',
|
|
358
364
|
gantt: 'Gantt chart',
|
|
365
|
+
'boxes-and-lines': 'Boxes and lines diagram',
|
|
359
366
|
};
|
|
360
367
|
|
|
361
368
|
/** All chart types with descriptions, for chart type autocomplete. Excludes `multi-line` alias. */
|
|
@@ -481,28 +488,12 @@ export const PIPE_METADATA = new Map<
|
|
|
481
488
|
},
|
|
482
489
|
],
|
|
483
490
|
[
|
|
484
|
-
'
|
|
491
|
+
'boxes-and-lines',
|
|
485
492
|
{
|
|
486
493
|
node: {
|
|
487
|
-
|
|
488
|
-
doing: { description: 'In progress' },
|
|
489
|
-
todo: { description: 'Not started' },
|
|
490
|
-
blocked: { description: 'Blocked' },
|
|
491
|
-
na: { description: 'Not applicable' },
|
|
492
|
-
wip: { description: 'Work in progress (alias for doing)' },
|
|
493
|
-
paused: { description: 'Paused (alias for blocked)' },
|
|
494
|
-
waiting: { description: 'Waiting (alias for blocked)' },
|
|
495
|
-
},
|
|
496
|
-
edge: {
|
|
497
|
-
done: { description: 'Completed' },
|
|
498
|
-
doing: { description: 'In progress' },
|
|
499
|
-
todo: { description: 'Not started' },
|
|
500
|
-
blocked: { description: 'Blocked' },
|
|
501
|
-
na: { description: 'Not applicable' },
|
|
502
|
-
wip: { description: 'Work in progress (alias for doing)' },
|
|
503
|
-
paused: { description: 'Paused (alias for blocked)' },
|
|
504
|
-
waiting: { description: 'Waiting (alias for blocked)' },
|
|
494
|
+
description: { description: 'Node description text' },
|
|
505
495
|
},
|
|
496
|
+
edge: {},
|
|
506
497
|
},
|
|
507
498
|
],
|
|
508
499
|
]);
|
|
@@ -935,12 +926,12 @@ function extractGanttSymbols(docText: string): DiagramSymbols {
|
|
|
935
926
|
}
|
|
936
927
|
|
|
937
928
|
// ============================================================
|
|
938
|
-
//
|
|
929
|
+
// Boxes-and-lines extractor
|
|
939
930
|
// ============================================================
|
|
940
931
|
|
|
941
|
-
const
|
|
932
|
+
const BL_ARROW_RE = /^(\S+)\s+(?:-.*)?(?:->|<->)\s+(\S+)/;
|
|
942
933
|
|
|
943
|
-
function
|
|
934
|
+
function extractBoxesAndLinesSymbols(docText: string): DiagramSymbols {
|
|
944
935
|
const lines = docText.split('\n');
|
|
945
936
|
const entities: string[] = [];
|
|
946
937
|
let pastFirstLine = false;
|
|
@@ -968,8 +959,11 @@ function extractInitiativeStatusSymbols(docText: string): DiagramSymbols {
|
|
|
968
959
|
inTagBlock = false;
|
|
969
960
|
}
|
|
970
961
|
|
|
971
|
-
//
|
|
972
|
-
|
|
962
|
+
// Skip groups
|
|
963
|
+
if (/^\[.+?\]/.test(trimmed)) continue;
|
|
964
|
+
|
|
965
|
+
// Edge lines
|
|
966
|
+
const arrowMatch = trimmed.match(BL_ARROW_RE);
|
|
973
967
|
if (arrowMatch) {
|
|
974
968
|
const src = arrowMatch[1].split('|')[0].trim();
|
|
975
969
|
const dst = arrowMatch[2].split('|')[0].trim();
|
|
@@ -978,14 +972,12 @@ function extractInitiativeStatusSymbols(docText: string): DiagramSymbols {
|
|
|
978
972
|
continue;
|
|
979
973
|
}
|
|
980
974
|
|
|
981
|
-
// Node lines
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
if (label && !entities.includes(label)) entities.push(label);
|
|
985
|
-
}
|
|
975
|
+
// Node lines
|
|
976
|
+
const label = trimmed.split('|')[0].split('[')[0].trim();
|
|
977
|
+
if (label && !entities.includes(label)) entities.push(label);
|
|
986
978
|
}
|
|
987
979
|
|
|
988
|
-
return { kind: '
|
|
980
|
+
return { kind: 'boxes-and-lines', entities, keywords: [] };
|
|
989
981
|
}
|
|
990
982
|
|
|
991
983
|
// ============================================================
|
|
@@ -1001,4 +993,4 @@ registerExtractor('state', extractStateSymbols);
|
|
|
1001
993
|
registerExtractor('sitemap', extractSitemapSymbols);
|
|
1002
994
|
registerExtractor('c4', extractC4Symbols);
|
|
1003
995
|
registerExtractor('gantt', extractGanttSymbols);
|
|
1004
|
-
registerExtractor('
|
|
996
|
+
registerExtractor('boxes-and-lines', extractBoxesAndLinesSymbols);
|
package/src/d3.ts
CHANGED
|
@@ -193,6 +193,7 @@ import {
|
|
|
193
193
|
matchTagBlockHeading,
|
|
194
194
|
validateTagValues,
|
|
195
195
|
resolveTagColor,
|
|
196
|
+
stripDefaultModifier,
|
|
196
197
|
} from './utils/tag-groups';
|
|
197
198
|
import type { TagGroup } from './utils/tag-groups';
|
|
198
199
|
import {
|
|
@@ -570,16 +571,16 @@ export function parseVisualization(
|
|
|
570
571
|
}
|
|
571
572
|
}
|
|
572
573
|
|
|
573
|
-
// Timeline tag group entries (indented under tag
|
|
574
|
+
// Timeline tag group entries (indented under tag heading)
|
|
574
575
|
if (currentTimelineTagGroup && indent > 0) {
|
|
575
|
-
const
|
|
576
|
-
const isDefault = /\bdefault\s*$/.test(trimmedEntry);
|
|
577
|
-
const entryText = isDefault
|
|
578
|
-
? trimmedEntry.replace(/\s+default\s*$/, '').trim()
|
|
579
|
-
: trimmedEntry;
|
|
576
|
+
const { text: entryText, isDefault } = stripDefaultModifier(line);
|
|
580
577
|
const { label, color } = extractColor(entryText, palette);
|
|
581
578
|
if (color) {
|
|
582
|
-
if (isDefault)
|
|
579
|
+
if (isDefault) {
|
|
580
|
+
currentTimelineTagGroup.defaultValue = label;
|
|
581
|
+
} else if (currentTimelineTagGroup.entries.length === 0) {
|
|
582
|
+
currentTimelineTagGroup.defaultValue = label;
|
|
583
|
+
}
|
|
583
584
|
currentTimelineTagGroup.entries.push({
|
|
584
585
|
value: label,
|
|
585
586
|
color,
|
|
@@ -1014,9 +1015,9 @@ export function parseVisualization(
|
|
|
1014
1015
|
continue;
|
|
1015
1016
|
}
|
|
1016
1017
|
|
|
1017
|
-
// Data points: Label x, y
|
|
1018
|
+
// Data points: Label x, y OR Label x y
|
|
1018
1019
|
const pointMatch = line.match(
|
|
1019
|
-
/^(.+?)\s+([0-9]*\.?[0-9]+)\s
|
|
1020
|
+
/^(.+?)\s+([0-9]*\.?[0-9]+)\s*[,\s]\s*([0-9]*\.?[0-9]+)\s*$/
|
|
1020
1021
|
);
|
|
1021
1022
|
if (pointMatch) {
|
|
1022
1023
|
const label = pointMatch[1].trim();
|
|
@@ -5047,8 +5048,8 @@ export function renderTimeline(
|
|
|
5047
5048
|
}
|
|
5048
5049
|
|
|
5049
5050
|
const pillXOff = isActive ? LG_CAPSULE_PAD : 0;
|
|
5050
|
-
const pillYOff =
|
|
5051
|
-
const pillH = LG_HEIGHT -
|
|
5051
|
+
const pillYOff = LG_CAPSULE_PAD;
|
|
5052
|
+
const pillH = LG_HEIGHT - LG_CAPSULE_PAD * 2;
|
|
5052
5053
|
|
|
5053
5054
|
// Pill background
|
|
5054
5055
|
gEl
|
|
@@ -6856,29 +6857,27 @@ export async function renderForExport(
|
|
|
6856
6857
|
return finalizeSvgExport(container, theme, effectivePalette, options);
|
|
6857
6858
|
}
|
|
6858
6859
|
|
|
6859
|
-
if (detectedType === '
|
|
6860
|
-
const {
|
|
6861
|
-
|
|
6862
|
-
const {
|
|
6863
|
-
await import('./
|
|
6864
|
-
const { renderInitiativeStatus } =
|
|
6865
|
-
await import('./initiative-status/renderer');
|
|
6860
|
+
if (detectedType === 'boxes-and-lines') {
|
|
6861
|
+
const { parseBoxesAndLines } = await import('./boxes-and-lines/parser');
|
|
6862
|
+
const { layoutBoxesAndLines } = await import('./boxes-and-lines/layout');
|
|
6863
|
+
const { renderBoxesAndLinesForExport } =
|
|
6864
|
+
await import('./boxes-and-lines/renderer');
|
|
6866
6865
|
|
|
6867
6866
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
6868
|
-
const
|
|
6869
|
-
if (
|
|
6867
|
+
const blParsed = parseBoxesAndLines(content);
|
|
6868
|
+
if (blParsed.error || blParsed.nodes.length === 0) return '';
|
|
6870
6869
|
|
|
6871
|
-
const
|
|
6870
|
+
const blLayout = layoutBoxesAndLines(blParsed);
|
|
6872
6871
|
const PADDING = 20;
|
|
6873
|
-
const titleOffset =
|
|
6874
|
-
const exportWidth =
|
|
6875
|
-
const exportHeight =
|
|
6872
|
+
const titleOffset = blParsed.title ? 40 : 0;
|
|
6873
|
+
const exportWidth = blLayout.width + PADDING * 2;
|
|
6874
|
+
const exportHeight = blLayout.height + PADDING * 2 + titleOffset;
|
|
6876
6875
|
const container = createExportContainer(exportWidth, exportHeight);
|
|
6877
6876
|
|
|
6878
|
-
|
|
6877
|
+
renderBoxesAndLinesForExport(
|
|
6879
6878
|
container,
|
|
6880
|
-
|
|
6881
|
-
|
|
6879
|
+
blParsed,
|
|
6880
|
+
blLayout,
|
|
6882
6881
|
effectivePalette,
|
|
6883
6882
|
theme === 'dark',
|
|
6884
6883
|
{ exportDims: { width: exportWidth, height: exportHeight } }
|
package/src/dgmo-router.ts
CHANGED
|
@@ -13,13 +13,10 @@ import { parseVisualization } from './d3';
|
|
|
13
13
|
import { parseOrg, looksLikeOrg } from './org/parser';
|
|
14
14
|
import { parseKanban } from './kanban/parser';
|
|
15
15
|
import { parseC4 } from './c4/parser';
|
|
16
|
-
import {
|
|
17
|
-
looksLikeInitiativeStatus,
|
|
18
|
-
parseInitiativeStatus,
|
|
19
|
-
} from './initiative-status/parser';
|
|
20
16
|
import { looksLikeSitemap, parseSitemap } from './sitemap/parser';
|
|
21
17
|
import { parseInfra } from './infra/parser';
|
|
22
18
|
import { parseGantt } from './gantt/parser';
|
|
19
|
+
import { parseBoxesAndLines } from './boxes-and-lines/parser';
|
|
23
20
|
import { parseFirstLine } from './utils/parsing';
|
|
24
21
|
import type { DgmoError } from './diagnostics';
|
|
25
22
|
|
|
@@ -97,7 +94,6 @@ export function parseDgmoChartType(content: string): string | null {
|
|
|
97
94
|
if (looksLikeFlowchart(content)) return 'flowchart';
|
|
98
95
|
if (looksLikeClassDiagram(content)) return 'class';
|
|
99
96
|
if (looksLikeERDiagram(content)) return 'er';
|
|
100
|
-
if (looksLikeInitiativeStatus(content)) return 'initiative-status';
|
|
101
97
|
if (looksLikeState(content)) return 'state';
|
|
102
98
|
if (looksLikeSitemap(content)) return 'sitemap';
|
|
103
99
|
if (looksLikeOrg(content)) return 'org';
|
|
@@ -147,11 +143,11 @@ const DIAGRAM_TYPES = new Set([
|
|
|
147
143
|
'org',
|
|
148
144
|
'kanban',
|
|
149
145
|
'c4',
|
|
150
|
-
'initiative-status',
|
|
151
146
|
'state',
|
|
152
147
|
'sitemap',
|
|
153
148
|
'infra',
|
|
154
149
|
'gantt',
|
|
150
|
+
'boxes-and-lines',
|
|
155
151
|
]);
|
|
156
152
|
const EXTENDED_CHART_TYPES = new Set([
|
|
157
153
|
'scatter',
|
|
@@ -226,11 +222,11 @@ const PARSE_DISPATCH = new Map<
|
|
|
226
222
|
['org', (c) => parseOrg(c)],
|
|
227
223
|
['kanban', (c) => parseKanban(c)],
|
|
228
224
|
['c4', (c) => parseC4(c)],
|
|
229
|
-
['initiative-status', (c) => parseInitiativeStatus(c)],
|
|
230
225
|
['state', (c) => parseState(c)],
|
|
231
226
|
['sitemap', (c) => parseSitemap(c)],
|
|
232
227
|
['infra', (c) => parseInfra(c)],
|
|
233
228
|
['gantt', (c) => parseGantt(c)],
|
|
229
|
+
['boxes-and-lines', (c) => parseBoxesAndLines(c)],
|
|
234
230
|
]);
|
|
235
231
|
|
|
236
232
|
/**
|
package/src/echarts.ts
CHANGED
|
@@ -2632,6 +2632,33 @@ function segmentLabelFormatter(parsed: ParsedChart): string {
|
|
|
2632
2632
|
|
|
2633
2633
|
// ── Pie / Doughnut ───────────────────────────────────────────
|
|
2634
2634
|
|
|
2635
|
+
/**
|
|
2636
|
+
* Compute pie label config: shrink radius and font when labels are long
|
|
2637
|
+
* so nothing gets truncated or wrapped.
|
|
2638
|
+
*/
|
|
2639
|
+
function pieLabelLayout(parsed: ParsedChart): {
|
|
2640
|
+
outerRadius: number;
|
|
2641
|
+
fontSize: number;
|
|
2642
|
+
} {
|
|
2643
|
+
const formatter = segmentLabelFormatter(parsed);
|
|
2644
|
+
const total = parsed.data.reduce((s, d) => s + d.value, 0);
|
|
2645
|
+
const maxLen = parsed.data.reduce((mx, d) => {
|
|
2646
|
+
const label = formatter
|
|
2647
|
+
.replace('{b}', d.label)
|
|
2648
|
+
.replace('{c}', String(d.value))
|
|
2649
|
+
.replace('{d}', total > 0 ? ((d.value / total) * 100).toFixed(2) : '0');
|
|
2650
|
+
return Math.max(mx, label.length);
|
|
2651
|
+
}, 0);
|
|
2652
|
+
|
|
2653
|
+
// Shrink radius and font for longer labels so they fit without truncation.
|
|
2654
|
+
// The chart renders in containers of varying width (800px in-app to 1200px CLI),
|
|
2655
|
+
// so we need enough margin for labels at the smallest reasonable container.
|
|
2656
|
+
if (maxLen > 30) return { outerRadius: 38, fontSize: 11 };
|
|
2657
|
+
if (maxLen > 24) return { outerRadius: 45, fontSize: 12 };
|
|
2658
|
+
if (maxLen > 18) return { outerRadius: 55, fontSize: 13 };
|
|
2659
|
+
return { outerRadius: 70, fontSize: 14 };
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2635
2662
|
function buildPieOption(
|
|
2636
2663
|
parsed: ParsedChart,
|
|
2637
2664
|
textColor: string,
|
|
@@ -2655,6 +2682,8 @@ function buildPieOption(
|
|
|
2655
2682
|
};
|
|
2656
2683
|
});
|
|
2657
2684
|
|
|
2685
|
+
const { outerRadius, fontSize } = pieLabelLayout(parsed);
|
|
2686
|
+
|
|
2658
2687
|
return {
|
|
2659
2688
|
...CHART_BASE,
|
|
2660
2689
|
...HIDE_AXES,
|
|
@@ -2666,13 +2695,16 @@ function buildPieOption(
|
|
|
2666
2695
|
series: [
|
|
2667
2696
|
{
|
|
2668
2697
|
type: 'pie',
|
|
2669
|
-
radius: isDoughnut
|
|
2698
|
+
radius: isDoughnut
|
|
2699
|
+
? [`${Math.round(outerRadius * 0.57)}%`, `${outerRadius}%`]
|
|
2700
|
+
: ['0%', `${outerRadius}%`],
|
|
2670
2701
|
data,
|
|
2671
2702
|
label: {
|
|
2672
2703
|
position: 'outside',
|
|
2673
2704
|
formatter: segmentLabelFormatter(parsed),
|
|
2674
2705
|
color: textColor,
|
|
2675
2706
|
fontFamily: FONT_FAMILY,
|
|
2707
|
+
fontSize,
|
|
2676
2708
|
},
|
|
2677
2709
|
labelLine: { show: true },
|
|
2678
2710
|
emphasis: EMPHASIS_SELF,
|
|
@@ -2790,13 +2822,17 @@ function buildPolarAreaOption(
|
|
|
2790
2822
|
{
|
|
2791
2823
|
type: 'pie',
|
|
2792
2824
|
roseType: 'radius',
|
|
2793
|
-
radius:
|
|
2825
|
+
radius: (() => {
|
|
2826
|
+
const { outerRadius } = pieLabelLayout(parsed);
|
|
2827
|
+
return [`${Math.round(outerRadius * 0.14)}%`, `${outerRadius}%`];
|
|
2828
|
+
})(),
|
|
2794
2829
|
data,
|
|
2795
2830
|
label: {
|
|
2796
2831
|
position: 'outside',
|
|
2797
2832
|
formatter: segmentLabelFormatter(parsed),
|
|
2798
2833
|
color: textColor,
|
|
2799
2834
|
fontFamily: FONT_FAMILY,
|
|
2835
|
+
fontSize: pieLabelLayout(parsed).fontSize,
|
|
2800
2836
|
},
|
|
2801
2837
|
labelLine: { show: true },
|
|
2802
2838
|
emphasis: EMPHASIS_SELF,
|
package/src/editor/keywords.ts
CHANGED
|
@@ -8,11 +8,11 @@ export const CHART_TYPES = new Set([
|
|
|
8
8
|
'org',
|
|
9
9
|
'kanban',
|
|
10
10
|
'c4',
|
|
11
|
-
'initiative-status',
|
|
12
11
|
'state',
|
|
13
12
|
'sitemap',
|
|
14
13
|
'infra',
|
|
15
14
|
'gantt',
|
|
15
|
+
'boxes-and-lines',
|
|
16
16
|
// Data chart types
|
|
17
17
|
'bar',
|
|
18
18
|
'line',
|
|
@@ -86,6 +86,8 @@ export const DIRECTIVE_KEYWORDS = new Set([
|
|
|
86
86
|
'import',
|
|
87
87
|
'active-tag',
|
|
88
88
|
'hide',
|
|
89
|
+
'mode',
|
|
90
|
+
'direction',
|
|
89
91
|
// ER
|
|
90
92
|
'notation',
|
|
91
93
|
// Class
|
|
@@ -146,8 +148,6 @@ export const DIRECTIVE_KEYWORDS = new Set([
|
|
|
146
148
|
// Layout
|
|
147
149
|
'direction-tb',
|
|
148
150
|
'direction-lr',
|
|
149
|
-
// Initiative-status
|
|
150
|
-
'contains',
|
|
151
151
|
// Data chart metadata
|
|
152
152
|
'title',
|
|
153
153
|
'series',
|
|
@@ -172,7 +172,7 @@ export const CONTROL_KEYWORDS = new Set([
|
|
|
172
172
|
'note',
|
|
173
173
|
]);
|
|
174
174
|
|
|
175
|
-
/** Status keywords —
|
|
175
|
+
/** Status keywords — kanban. */
|
|
176
176
|
export const STATUS_KEYWORDS = new Set([
|
|
177
177
|
'na',
|
|
178
178
|
'todo',
|
|
@@ -190,21 +190,6 @@ export const MODIFIER_KEYWORDS = new Set([
|
|
|
190
190
|
'aka',
|
|
191
191
|
'position',
|
|
192
192
|
'default',
|
|
193
|
-
// Sequence participant types
|
|
194
|
-
'actor',
|
|
195
|
-
'service',
|
|
196
|
-
'database',
|
|
197
|
-
'queue',
|
|
198
|
-
'cache',
|
|
199
|
-
'gateway',
|
|
200
|
-
'external',
|
|
201
|
-
'networking',
|
|
202
|
-
'frontend',
|
|
203
|
-
// C4 element types
|
|
204
|
-
'system',
|
|
205
|
-
'person',
|
|
206
|
-
'container',
|
|
207
|
-
'component',
|
|
208
193
|
// ER column modifiers
|
|
209
194
|
'pk',
|
|
210
195
|
'fk',
|
package/src/er/parser.ts
CHANGED
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
parseFirstLine,
|
|
9
9
|
OPTION_NOCOLON_RE,
|
|
10
10
|
} from '../utils/parsing';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
matchTagBlockHeading,
|
|
13
|
+
validateTagValues,
|
|
14
|
+
stripDefaultModifier,
|
|
15
|
+
} from '../utils/tag-groups';
|
|
12
16
|
import type { TagGroup } from '../utils/tag-groups';
|
|
13
17
|
import type {
|
|
14
18
|
ParsedERDiagram,
|
|
@@ -273,7 +277,8 @@ export function parseERDiagram(
|
|
|
273
277
|
|
|
274
278
|
// Tag group entries (indented under tag heading)
|
|
275
279
|
if (currentTagGroup && !contentStarted && indent > 0) {
|
|
276
|
-
const {
|
|
280
|
+
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
281
|
+
const { label, color } = extractColor(cleanEntry, palette);
|
|
277
282
|
if (!color) {
|
|
278
283
|
result.diagnostics.push(
|
|
279
284
|
makeDgmoError(
|
|
@@ -284,8 +289,9 @@ export function parseERDiagram(
|
|
|
284
289
|
);
|
|
285
290
|
continue;
|
|
286
291
|
}
|
|
287
|
-
|
|
288
|
-
|
|
292
|
+
if (isDefault) {
|
|
293
|
+
currentTagGroup.defaultValue = label;
|
|
294
|
+
} else if (currentTagGroup.entries.length === 0) {
|
|
289
295
|
currentTagGroup.defaultValue = label;
|
|
290
296
|
}
|
|
291
297
|
currentTagGroup.entries.push({ value: label, color, lineNumber });
|
package/src/gantt/parser.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { makeDgmoError, formatDgmoError } from '../diagnostics';
|
|
6
6
|
import type { DgmoError } from '../diagnostics';
|
|
7
7
|
import type { TagGroup } from '../utils/tag-groups';
|
|
8
|
-
import { matchTagBlockHeading } from '../utils/tag-groups';
|
|
8
|
+
import { matchTagBlockHeading, stripDefaultModifier } from '../utils/tag-groups';
|
|
9
9
|
import {
|
|
10
10
|
measureIndent,
|
|
11
11
|
extractColor,
|
|
@@ -357,9 +357,10 @@ export function parseGantt(
|
|
|
357
357
|
// fall through to process this line normally
|
|
358
358
|
} else {
|
|
359
359
|
// Parse tag entry: `Value(color)` or `Value`
|
|
360
|
-
// First entry is the default
|
|
360
|
+
// First entry is the default unless another is marked `default`
|
|
361
361
|
if (COMMENT_RE.test(line)) continue;
|
|
362
|
-
const
|
|
362
|
+
const { text: cleanEntry, isDefault } = stripDefaultModifier(line);
|
|
363
|
+
const extracted = extractColor(cleanEntry, palette);
|
|
363
364
|
const color =
|
|
364
365
|
extracted.color ||
|
|
365
366
|
seriesColors[currentTagGroup.entries.length % seriesColors.length] ||
|
|
@@ -370,7 +371,9 @@ export function parseGantt(
|
|
|
370
371
|
color,
|
|
371
372
|
lineNumber,
|
|
372
373
|
});
|
|
373
|
-
if (
|
|
374
|
+
if (isDefault) {
|
|
375
|
+
currentTagGroup.defaultValue = extracted.label;
|
|
376
|
+
} else if (isFirstEntry) {
|
|
374
377
|
currentTagGroup.defaultValue = extracted.label;
|
|
375
378
|
}
|
|
376
379
|
continue;
|
package/src/gantt/renderer.ts
CHANGED
|
@@ -298,7 +298,7 @@ export function renderGantt(
|
|
|
298
298
|
.append('g')
|
|
299
299
|
.attr('transform', `translate(${leftMargin}, ${marginTop})`);
|
|
300
300
|
|
|
301
|
-
// ── Title (y=30, consistent with timeline
|
|
301
|
+
// ── Title (y=30, consistent with timeline) ──
|
|
302
302
|
|
|
303
303
|
if (title) {
|
|
304
304
|
svg
|
|
@@ -1952,9 +1952,7 @@ function renderTagLegend(
|
|
|
1952
1952
|
measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) +
|
|
1953
1953
|
LEGEND_PILL_PAD +
|
|
1954
1954
|
iconReserve;
|
|
1955
|
-
const pillH =
|
|
1956
|
-
? LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2
|
|
1957
|
-
: LEGEND_HEIGHT;
|
|
1955
|
+
const pillH = LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2;
|
|
1958
1956
|
const groupW = groupWidths[i];
|
|
1959
1957
|
|
|
1960
1958
|
const gEl = legendRow
|
|
@@ -1979,7 +1977,7 @@ function renderTagLegend(
|
|
|
1979
1977
|
}
|
|
1980
1978
|
|
|
1981
1979
|
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
1982
|
-
const pillYOff =
|
|
1980
|
+
const pillYOff = LEGEND_CAPSULE_PAD;
|
|
1983
1981
|
|
|
1984
1982
|
// Pill background
|
|
1985
1983
|
gEl
|
package/src/index.ts
CHANGED
|
@@ -204,36 +204,27 @@ export {
|
|
|
204
204
|
renderC4DeploymentForExport,
|
|
205
205
|
} from './c4/renderer';
|
|
206
206
|
|
|
207
|
-
export {
|
|
208
|
-
parseInitiativeStatus,
|
|
209
|
-
looksLikeInitiativeStatus,
|
|
210
|
-
} from './initiative-status/parser';
|
|
207
|
+
export { parseBoxesAndLines } from './boxes-and-lines/parser';
|
|
211
208
|
export type {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
} from './
|
|
218
|
-
|
|
219
|
-
export { layoutInitiativeStatus } from './initiative-status/layout';
|
|
209
|
+
ParsedBoxesAndLines,
|
|
210
|
+
BLNode,
|
|
211
|
+
BLEdge,
|
|
212
|
+
BLGroup,
|
|
213
|
+
} from './boxes-and-lines/types';
|
|
214
|
+
export { layoutBoxesAndLines } from './boxes-and-lines/layout';
|
|
220
215
|
export type {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
} from './
|
|
226
|
-
|
|
216
|
+
BLLayoutResult,
|
|
217
|
+
BLLayoutNode,
|
|
218
|
+
BLLayoutEdge,
|
|
219
|
+
BLLayoutGroup,
|
|
220
|
+
} from './boxes-and-lines/layout';
|
|
227
221
|
export {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
} from './
|
|
231
|
-
export type { ISRenderOptions } from './initiative-status/renderer';
|
|
232
|
-
|
|
233
|
-
export { collapseInitiativeStatus } from './initiative-status/collapse';
|
|
234
|
-
export type { CollapseResult } from './initiative-status/collapse';
|
|
222
|
+
renderBoxesAndLines,
|
|
223
|
+
renderBoxesAndLinesForExport,
|
|
224
|
+
} from './boxes-and-lines/renderer';
|
|
235
225
|
|
|
236
|
-
export {
|
|
226
|
+
export { collapseBoxesAndLines } from './boxes-and-lines/collapse';
|
|
227
|
+
export type { BLCollapseResult } from './boxes-and-lines/collapse';
|
|
237
228
|
|
|
238
229
|
export { parseSitemap, looksLikeSitemap } from './sitemap/parser';
|
|
239
230
|
|
package/src/infra/parser.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
parseFirstLine,
|
|
13
13
|
OPTION_NOCOLON_RE,
|
|
14
14
|
} from '../utils/parsing';
|
|
15
|
-
import { matchTagBlockHeading } from '../utils/tag-groups';
|
|
15
|
+
import { matchTagBlockHeading, stripDefaultModifier } from '../utils/tag-groups';
|
|
16
16
|
import type {
|
|
17
17
|
ParsedInfra,
|
|
18
18
|
InfraNode,
|
|
@@ -338,17 +338,19 @@ export function parseInfra(content: string): ParsedInfra {
|
|
|
338
338
|
|
|
339
339
|
// ---- Indented lines ----
|
|
340
340
|
|
|
341
|
-
// Tag value inside tag group — first value is the default
|
|
341
|
+
// Tag value inside tag group — first value is the default unless another is marked `default`
|
|
342
342
|
if (currentTagGroup && indent > 0) {
|
|
343
|
-
const
|
|
343
|
+
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
344
|
+
const tvMatch = cleanEntry.match(TAG_VALUE_RE);
|
|
344
345
|
if (tvMatch) {
|
|
345
346
|
const valueName = tvMatch[1].trim();
|
|
346
347
|
currentTagGroup.values.push({
|
|
347
348
|
name: valueName,
|
|
348
349
|
color: tvMatch[2]?.trim(),
|
|
349
350
|
});
|
|
350
|
-
|
|
351
|
-
|
|
351
|
+
if (isDefault) {
|
|
352
|
+
currentTagGroup.defaultValue = valueName;
|
|
353
|
+
} else if (currentTagGroup.values.length === 1) {
|
|
352
354
|
currentTagGroup.defaultValue = valueName;
|
|
353
355
|
}
|
|
354
356
|
continue;
|
package/src/infra/renderer.ts
CHANGED
|
@@ -2069,8 +2069,8 @@ function renderLegend(
|
|
|
2069
2069
|
}
|
|
2070
2070
|
|
|
2071
2071
|
const pillXOff = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
2072
|
-
const pillYOff =
|
|
2073
|
-
const pillH = LEGEND_HEIGHT -
|
|
2072
|
+
const pillYOff = LEGEND_CAPSULE_PAD;
|
|
2073
|
+
const pillH = LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2;
|
|
2074
2074
|
|
|
2075
2075
|
// Pill background
|
|
2076
2076
|
gEl
|
package/src/kanban/parser.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PaletteColors } from '../palettes';
|
|
2
2
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
3
3
|
import { resolveColor } from '../colors';
|
|
4
|
-
import { matchTagBlockHeading } from '../utils/tag-groups';
|
|
4
|
+
import { matchTagBlockHeading, stripDefaultModifier } from '../utils/tag-groups';
|
|
5
5
|
import {
|
|
6
6
|
measureIndent,
|
|
7
7
|
extractColor,
|
|
@@ -166,11 +166,12 @@ export function parseKanban(
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// Tag group entries (indented Value(color) under tag heading)
|
|
169
|
-
// First entry is
|
|
169
|
+
// First entry is the default unless another is marked `default`
|
|
170
170
|
if (currentTagGroup && !contentStarted) {
|
|
171
171
|
const indent = measureIndent(line);
|
|
172
172
|
if (indent > 0) {
|
|
173
|
-
const {
|
|
173
|
+
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
174
|
+
const { label, color } = extractColor(cleanEntry, palette);
|
|
174
175
|
if (!color) {
|
|
175
176
|
warn(
|
|
176
177
|
lineNumber,
|
|
@@ -178,8 +179,9 @@ export function parseKanban(
|
|
|
178
179
|
);
|
|
179
180
|
continue;
|
|
180
181
|
}
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
if (isDefault) {
|
|
183
|
+
currentTagGroup.defaultValue = label;
|
|
184
|
+
} else if (currentTagGroup.entries.length === 0) {
|
|
183
185
|
currentTagGroup.defaultValue = label;
|
|
184
186
|
}
|
|
185
187
|
currentTagGroup.entries.push({
|