@diagrammo/dgmo 0.2.21 → 0.2.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +119 -113
- package/dist/index.cjs +6317 -2337
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +264 -1
- package/dist/index.d.ts +264 -1
- package/dist/index.js +6299 -2337
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/c4/layout.ts +2137 -0
- package/src/c4/parser.ts +809 -0
- package/src/c4/renderer.ts +1916 -0
- package/src/c4/types.ts +86 -0
- package/src/class/renderer.ts +2 -2
- package/src/cli.ts +54 -10
- package/src/d3.ts +148 -10
- package/src/dgmo-router.ts +13 -0
- package/src/echarts.ts +7 -8
- package/src/er/renderer.ts +2 -2
- package/src/graph/flowchart-renderer.ts +1 -1
- package/src/index.ts +54 -0
- package/src/initiative-status/layout.ts +217 -0
- package/src/initiative-status/parser.ts +246 -0
- package/src/initiative-status/renderer.ts +834 -0
- package/src/initiative-status/types.ts +43 -0
- package/src/kanban/renderer.ts +23 -3
- package/src/org/layout.ts +64 -26
- package/src/org/renderer.ts +47 -18
- package/src/org/resolver.ts +3 -1
- package/src/render.ts +9 -1
- package/src/sequence/participant-inference.ts +1 -0
- package/src/sequence/renderer.ts +12 -6
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Initiative Status Diagram — Types
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
import type { DgmoError } from '../diagnostics';
|
|
6
|
+
import type { ParticipantType } from '../sequence/parser';
|
|
7
|
+
|
|
8
|
+
export type InitiativeStatus = 'done' | 'wip' | 'todo' | 'na' | null;
|
|
9
|
+
|
|
10
|
+
export const VALID_STATUSES: readonly string[] = ['done', 'wip', 'todo', 'na'];
|
|
11
|
+
|
|
12
|
+
export interface ISNode {
|
|
13
|
+
label: string;
|
|
14
|
+
status: InitiativeStatus;
|
|
15
|
+
shape: ParticipantType;
|
|
16
|
+
lineNumber: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ISEdge {
|
|
20
|
+
source: string; // node label
|
|
21
|
+
target: string; // node label
|
|
22
|
+
label?: string; // e.g. "getUser"
|
|
23
|
+
status: InitiativeStatus;
|
|
24
|
+
lineNumber: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ISGroup {
|
|
28
|
+
label: string;
|
|
29
|
+
nodeLabels: string[];
|
|
30
|
+
lineNumber: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ParsedInitiativeStatus {
|
|
34
|
+
type: 'initiative-status';
|
|
35
|
+
title: string | null;
|
|
36
|
+
titleLineNumber: number | null;
|
|
37
|
+
nodes: ISNode[];
|
|
38
|
+
edges: ISEdge[];
|
|
39
|
+
groups: ISGroup[];
|
|
40
|
+
options: Record<string, string>;
|
|
41
|
+
diagnostics: DgmoError[];
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
package/src/kanban/renderer.ts
CHANGED
|
@@ -267,7 +267,9 @@ export function renderKanban(
|
|
|
267
267
|
? parsed.title.length * TITLE_FONT_SIZE * 0.6 + 16
|
|
268
268
|
: 0;
|
|
269
269
|
let legendX = DIAGRAM_PADDING + titleTextWidth;
|
|
270
|
-
const groupBg =
|
|
270
|
+
const groupBg = isDark
|
|
271
|
+
? mix(palette.surface, palette.bg, 50)
|
|
272
|
+
: mix(palette.surface, palette.bg, 30);
|
|
271
273
|
const capsulePad = 4;
|
|
272
274
|
|
|
273
275
|
for (const group of parsed.tagGroups) {
|
|
@@ -345,7 +347,12 @@ export function renderKanban(
|
|
|
345
347
|
if (isActive) {
|
|
346
348
|
let entryX = pillX + pillWidth + 4;
|
|
347
349
|
for (const entry of group.entries) {
|
|
348
|
-
svg
|
|
350
|
+
const entryG = svg
|
|
351
|
+
.append('g')
|
|
352
|
+
.attr('data-legend-entry', entry.value.toLowerCase())
|
|
353
|
+
.style('cursor', 'pointer');
|
|
354
|
+
|
|
355
|
+
entryG
|
|
349
356
|
.append('circle')
|
|
350
357
|
.attr('cx', entryX + LEGEND_DOT_R)
|
|
351
358
|
.attr('cy', legendY + LEGEND_HEIGHT / 2)
|
|
@@ -353,7 +360,7 @@ export function renderKanban(
|
|
|
353
360
|
.attr('fill', entry.color);
|
|
354
361
|
|
|
355
362
|
const entryTextX = entryX + LEGEND_DOT_R * 2 + 4;
|
|
356
|
-
|
|
363
|
+
entryG
|
|
357
364
|
.append('text')
|
|
358
365
|
.attr('x', entryTextX)
|
|
359
366
|
.attr('y', legendY + LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
|
|
@@ -464,6 +471,19 @@ export function renderKanban(
|
|
|
464
471
|
.attr('data-card-id', card.id)
|
|
465
472
|
.attr('data-line-number', card.lineNumber);
|
|
466
473
|
|
|
474
|
+
// Expose active tag group value for legend-entry hover dimming
|
|
475
|
+
if (activeTagGroup) {
|
|
476
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
477
|
+
const tagValue = card.tags[tagKey];
|
|
478
|
+
const group = parsed.tagGroups.find(
|
|
479
|
+
(tg) => tg.name.toLowerCase() === tagKey
|
|
480
|
+
);
|
|
481
|
+
const value = tagValue ?? group?.defaultValue;
|
|
482
|
+
if (value) {
|
|
483
|
+
cg.attr(`data-tag-${tagKey}`, value.toLowerCase());
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
467
487
|
const cx = colLayout.x + cardLayout.x;
|
|
468
488
|
const cy = colLayout.y + cardLayout.y;
|
|
469
489
|
|
package/src/org/layout.ts
CHANGED
|
@@ -56,6 +56,7 @@ export interface OrgLegendEntry {
|
|
|
56
56
|
|
|
57
57
|
export interface OrgLegendGroup {
|
|
58
58
|
name: string;
|
|
59
|
+
alias?: string;
|
|
59
60
|
entries: OrgLegendEntry[];
|
|
60
61
|
x: number;
|
|
61
62
|
y: number;
|
|
@@ -270,17 +271,30 @@ function centerHeavyChildren(node: TreeNode): void {
|
|
|
270
271
|
// Layout
|
|
271
272
|
// ============================================================
|
|
272
273
|
|
|
273
|
-
function computeLegendGroups(
|
|
274
|
+
function computeLegendGroups(
|
|
275
|
+
tagGroups: OrgTagGroup[],
|
|
276
|
+
_showEyeIcons: boolean,
|
|
277
|
+
usedValuesByGroup?: Map<string, Set<string>>
|
|
278
|
+
): OrgLegendGroup[] {
|
|
274
279
|
const groups: OrgLegendGroup[] = [];
|
|
275
280
|
|
|
276
281
|
for (const group of tagGroups) {
|
|
277
282
|
if (group.entries.length === 0) continue;
|
|
278
283
|
|
|
284
|
+
// Filter entries to only values actually used by nodes (if provided)
|
|
285
|
+
const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
|
|
286
|
+
const visibleEntries = usedValues
|
|
287
|
+
? group.entries.filter((e) => usedValues.has(e.value.toLowerCase()))
|
|
288
|
+
: group.entries;
|
|
289
|
+
if (visibleEntries.length === 0) continue;
|
|
290
|
+
|
|
291
|
+
// Pill label shows just the group name (alias is for DSL shorthand only)
|
|
279
292
|
const pillWidth = group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
293
|
+
const minPillWidth = pillWidth;
|
|
280
294
|
|
|
281
295
|
// Capsule: pad + pill + gap + entries + pad
|
|
282
296
|
let entriesWidth = 0;
|
|
283
|
-
for (const entry of
|
|
297
|
+
for (const entry of visibleEntries) {
|
|
284
298
|
entriesWidth +=
|
|
285
299
|
LEGEND_DOT_R * 2 +
|
|
286
300
|
LEGEND_ENTRY_DOT_GAP +
|
|
@@ -292,12 +306,16 @@ function computeLegendGroups(tagGroups: OrgTagGroup[], _showEyeIcons: boolean):
|
|
|
292
306
|
|
|
293
307
|
groups.push({
|
|
294
308
|
name: group.name,
|
|
295
|
-
|
|
309
|
+
alias: group.alias,
|
|
310
|
+
entries: visibleEntries.map((e) => ({
|
|
311
|
+
value: e.value,
|
|
312
|
+
color: e.color,
|
|
313
|
+
})),
|
|
296
314
|
x: 0,
|
|
297
315
|
y: 0,
|
|
298
316
|
width: capsuleWidth,
|
|
299
317
|
height: LEGEND_HEIGHT,
|
|
300
|
-
minifiedWidth:
|
|
318
|
+
minifiedWidth: minPillWidth,
|
|
301
319
|
minifiedHeight: LEGEND_HEIGHT,
|
|
302
320
|
});
|
|
303
321
|
}
|
|
@@ -348,20 +366,22 @@ export function layoutOrg(
|
|
|
348
366
|
return { nodes: [], edges: [], containers: [], legend: [], width: 0, height: 0 };
|
|
349
367
|
}
|
|
350
368
|
|
|
351
|
-
//
|
|
352
|
-
let
|
|
369
|
+
// Legend-only mode: stack groups vertically, all expanded
|
|
370
|
+
let cy = MARGIN;
|
|
371
|
+
let maxWidth = 0;
|
|
353
372
|
for (const g of legendGroups) {
|
|
354
|
-
g.x =
|
|
355
|
-
g.y =
|
|
356
|
-
|
|
373
|
+
g.x = MARGIN;
|
|
374
|
+
g.y = cy;
|
|
375
|
+
cy += LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
376
|
+
if (g.width > maxWidth) maxWidth = g.width;
|
|
357
377
|
}
|
|
358
378
|
return {
|
|
359
379
|
nodes: [],
|
|
360
380
|
edges: [],
|
|
361
381
|
containers: [],
|
|
362
382
|
legend: legendGroups,
|
|
363
|
-
width:
|
|
364
|
-
height:
|
|
383
|
+
width: maxWidth + MARGIN * 2,
|
|
384
|
+
height: cy - LEGEND_GROUP_GAP + MARGIN,
|
|
365
385
|
};
|
|
366
386
|
}
|
|
367
387
|
|
|
@@ -1084,13 +1104,28 @@ export function layoutOrg(
|
|
|
1084
1104
|
const totalWidth = finalMaxX - finalMinX + MARGIN * 2;
|
|
1085
1105
|
const totalHeight = finalMaxY - minY + MARGIN * 2;
|
|
1086
1106
|
|
|
1107
|
+
// Collect which tag group values are actually used by nodes
|
|
1108
|
+
const usedValuesByGroup = new Map<string, Set<string>>();
|
|
1109
|
+
for (const group of parsed.tagGroups) {
|
|
1110
|
+
const key = group.name.toLowerCase();
|
|
1111
|
+
const used = new Set<string>();
|
|
1112
|
+
const walk = (node: OrgNode) => {
|
|
1113
|
+
if (!node.isContainer && node.metadata[key]) {
|
|
1114
|
+
used.add(node.metadata[key].toLowerCase());
|
|
1115
|
+
}
|
|
1116
|
+
for (const child of node.children) walk(child);
|
|
1117
|
+
};
|
|
1118
|
+
for (const root of parsed.roots) walk(root);
|
|
1119
|
+
usedValuesByGroup.set(key, used);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1087
1122
|
// Compute legend for tag groups
|
|
1088
1123
|
const showEyeIcons = hiddenAttributes !== undefined;
|
|
1089
|
-
const legendGroups = computeLegendGroups(parsed.tagGroups, showEyeIcons);
|
|
1124
|
+
const legendGroups = computeLegendGroups(parsed.tagGroups, showEyeIcons, usedValuesByGroup);
|
|
1090
1125
|
let finalWidth = totalWidth;
|
|
1091
1126
|
let finalHeight = totalHeight;
|
|
1092
1127
|
|
|
1093
|
-
const legendPosition = parsed.options?.['legend-position'] ?? '
|
|
1128
|
+
const legendPosition = parsed.options?.['legend-position'] ?? 'top';
|
|
1094
1129
|
|
|
1095
1130
|
// When a tag group is active, only that group is laid out (full size).
|
|
1096
1131
|
// When none is active, all groups are laid out minified.
|
|
@@ -1133,28 +1168,31 @@ export function layoutOrg(
|
|
|
1133
1168
|
|
|
1134
1169
|
finalHeight = totalHeight + LEGEND_GAP + LEGEND_HEIGHT;
|
|
1135
1170
|
} else {
|
|
1136
|
-
// Top: horizontal row
|
|
1171
|
+
// Top: horizontal row above chart content, left-aligned
|
|
1172
|
+
const legendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
|
|
1173
|
+
|
|
1174
|
+
// Push all chart content down
|
|
1175
|
+
for (const n of layoutNodes) n.y += legendShift;
|
|
1176
|
+
for (const c of containers) c.y += legendShift;
|
|
1177
|
+
for (const e of layoutEdges) {
|
|
1178
|
+
for (const p of e.points) p.y += legendShift;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1137
1181
|
const totalGroupsWidth =
|
|
1138
1182
|
visibleGroups.reduce((s, g) => s + effectiveW(g), 0) +
|
|
1139
1183
|
(visibleGroups.length - 1) * LEGEND_GROUP_GAP;
|
|
1140
|
-
const legendStartX = totalWidth - MARGIN + LEGEND_GAP;
|
|
1141
|
-
const legendY = MARGIN;
|
|
1142
1184
|
|
|
1143
|
-
let cx =
|
|
1185
|
+
let cx = MARGIN;
|
|
1144
1186
|
for (const g of visibleGroups) {
|
|
1145
1187
|
g.x = cx;
|
|
1146
|
-
g.y =
|
|
1188
|
+
g.y = MARGIN;
|
|
1147
1189
|
cx += effectiveW(g) + LEGEND_GROUP_GAP;
|
|
1148
1190
|
}
|
|
1149
1191
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const legendBottom = legendY + LEGEND_HEIGHT + MARGIN;
|
|
1156
|
-
if (legendBottom > finalHeight) {
|
|
1157
|
-
finalHeight = legendBottom;
|
|
1192
|
+
finalHeight += legendShift;
|
|
1193
|
+
const neededWidth = totalGroupsWidth + MARGIN * 2;
|
|
1194
|
+
if (neededWidth > finalWidth) {
|
|
1195
|
+
finalWidth = neededWidth;
|
|
1158
1196
|
}
|
|
1159
1197
|
}
|
|
1160
1198
|
}
|
package/src/org/renderer.ts
CHANGED
|
@@ -77,14 +77,12 @@ function nodeFill(
|
|
|
77
77
|
isDark: boolean,
|
|
78
78
|
nodeColor?: string
|
|
79
79
|
): string {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
|
|
80
|
+
const color = nodeColor ?? palette.primary;
|
|
81
|
+
return mix(color, isDark ? palette.surface : palette.bg, 25);
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
function nodeStroke(palette: PaletteColors, nodeColor?: string): string {
|
|
87
|
-
return nodeColor ?? palette.
|
|
85
|
+
return nodeColor ?? palette.primary;
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
function containerFill(
|
|
@@ -206,6 +204,15 @@ export function renderOrg(
|
|
|
206
204
|
.attr('class', 'org-container')
|
|
207
205
|
.attr('data-line-number', String(c.lineNumber)) as GSelection;
|
|
208
206
|
|
|
207
|
+
// Expose active tag group value for legend-entry hover dimming
|
|
208
|
+
if (activeTagGroup) {
|
|
209
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
210
|
+
const metaValue = c.metadata[tagKey];
|
|
211
|
+
if (metaValue) {
|
|
212
|
+
cG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
209
216
|
// Toggle attribute for containers that have (or had) children
|
|
210
217
|
if (c.hasChildren) {
|
|
211
218
|
cG.attr('data-node-toggle', c.nodeId)
|
|
@@ -333,6 +340,15 @@ export function renderOrg(
|
|
|
333
340
|
.attr('class', 'org-node')
|
|
334
341
|
.attr('data-line-number', String(node.lineNumber)) as GSelection;
|
|
335
342
|
|
|
343
|
+
// Expose active tag group value for legend-entry hover dimming
|
|
344
|
+
if (activeTagGroup) {
|
|
345
|
+
const tagKey = activeTagGroup.toLowerCase();
|
|
346
|
+
const metaValue = node.metadata[tagKey];
|
|
347
|
+
if (metaValue) {
|
|
348
|
+
nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
336
352
|
// Toggle attribute for nodes that have (or had) children
|
|
337
353
|
if (node.hasChildren) {
|
|
338
354
|
nodeG
|
|
@@ -447,27 +463,34 @@ export function renderOrg(
|
|
|
447
463
|
|
|
448
464
|
// Render legend — kanban-style pills.
|
|
449
465
|
// Skip in export mode (unless legend-only chart).
|
|
466
|
+
// Legend-only (no nodes): all groups rendered as expanded capsules.
|
|
450
467
|
// Active group: only that group rendered as capsule (pill + entries).
|
|
451
468
|
// No active group: all groups rendered as standalone pills.
|
|
452
|
-
|
|
469
|
+
const legendOnly = layout.nodes.length === 0;
|
|
470
|
+
if (!exportDims || legendOnly) for (const group of layout.legend) {
|
|
453
471
|
const isActive =
|
|
454
|
-
|
|
455
|
-
|
|
472
|
+
legendOnly ||
|
|
473
|
+
(activeTagGroup != null &&
|
|
474
|
+
group.name.toLowerCase() === activeTagGroup.toLowerCase());
|
|
456
475
|
|
|
457
|
-
// When a group is active, skip all other groups entirely
|
|
458
|
-
if (activeTagGroup != null && !isActive) continue;
|
|
476
|
+
// When a group is active, skip all other groups entirely (not in legend-only mode)
|
|
477
|
+
if (!legendOnly && activeTagGroup != null && !isActive) continue;
|
|
459
478
|
|
|
460
|
-
const groupBg =
|
|
479
|
+
const groupBg = isDark
|
|
480
|
+
? mix(palette.surface, palette.bg, 50)
|
|
481
|
+
: mix(palette.surface, palette.bg, 30);
|
|
461
482
|
|
|
483
|
+
// Pill label: just the group name (alias is for DSL shorthand only)
|
|
484
|
+
const pillLabel = group.name;
|
|
462
485
|
const pillWidth =
|
|
463
|
-
|
|
486
|
+
pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
464
487
|
|
|
465
488
|
const gEl = contentG
|
|
466
489
|
.append('g')
|
|
467
490
|
.attr('transform', `translate(${group.x}, ${group.y})`)
|
|
468
491
|
.attr('class', 'org-legend-group')
|
|
469
492
|
.attr('data-legend-group', group.name.toLowerCase())
|
|
470
|
-
.style('cursor', 'pointer');
|
|
493
|
+
.style('cursor', legendOnly ? 'default' : 'pointer');
|
|
471
494
|
|
|
472
495
|
// Outer capsule background (active only)
|
|
473
496
|
if (isActive) {
|
|
@@ -516,13 +539,18 @@ export function renderOrg(
|
|
|
516
539
|
.attr('font-weight', '500')
|
|
517
540
|
.attr('fill', isActive ? palette.text : palette.textMuted)
|
|
518
541
|
.attr('text-anchor', 'middle')
|
|
519
|
-
.text(
|
|
542
|
+
.text(pillLabel);
|
|
520
543
|
|
|
521
544
|
// Entries inside capsule (active only)
|
|
522
545
|
if (isActive) {
|
|
523
546
|
let entryX = pillX + pillWidth + 4;
|
|
524
547
|
for (const entry of group.entries) {
|
|
525
|
-
gEl
|
|
548
|
+
const entryG = gEl
|
|
549
|
+
.append('g')
|
|
550
|
+
.attr('data-legend-entry', entry.value.toLowerCase())
|
|
551
|
+
.style('cursor', 'pointer');
|
|
552
|
+
|
|
553
|
+
entryG
|
|
526
554
|
.append('circle')
|
|
527
555
|
.attr('cx', entryX + LEGEND_DOT_R)
|
|
528
556
|
.attr('cy', LEGEND_HEIGHT / 2)
|
|
@@ -530,15 +558,16 @@ export function renderOrg(
|
|
|
530
558
|
.attr('fill', entry.color);
|
|
531
559
|
|
|
532
560
|
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
533
|
-
|
|
561
|
+
const entryLabel = entry.value;
|
|
562
|
+
entryG
|
|
534
563
|
.append('text')
|
|
535
564
|
.attr('x', textX)
|
|
536
565
|
.attr('y', LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
|
|
537
566
|
.attr('font-size', LEGEND_ENTRY_FONT_SIZE)
|
|
538
567
|
.attr('fill', palette.textMuted)
|
|
539
|
-
.text(
|
|
568
|
+
.text(entryLabel);
|
|
540
569
|
|
|
541
|
-
entryX = textX +
|
|
570
|
+
entryX = textX + entryLabel.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
542
571
|
}
|
|
543
572
|
}
|
|
544
573
|
}
|
package/src/org/resolver.ts
CHANGED
|
@@ -200,6 +200,7 @@ async function resolveFile(
|
|
|
200
200
|
const bodyStartIndex = findBodyStart(lines);
|
|
201
201
|
|
|
202
202
|
// Collect header lines (chart:, title:, options, tags:)
|
|
203
|
+
let tagsLineNumber = 0; // 1-based line number of the tags: directive
|
|
203
204
|
for (let i = 0; i < bodyStartIndex; i++) {
|
|
204
205
|
const trimmed = lines[i].trim();
|
|
205
206
|
if (trimmed === '' || trimmed.startsWith('//')) {
|
|
@@ -212,6 +213,7 @@ async function resolveFile(
|
|
|
212
213
|
const tagsMatch = trimmed.match(TAGS_RE);
|
|
213
214
|
if (tagsMatch) {
|
|
214
215
|
tagsDirective = tagsMatch[1].trim();
|
|
216
|
+
tagsLineNumber = i + 1; // 1-based
|
|
215
217
|
continue;
|
|
216
218
|
}
|
|
217
219
|
|
|
@@ -228,7 +230,7 @@ async function resolveFile(
|
|
|
228
230
|
tagsFileGroups = extractTagGroups(tagsLines);
|
|
229
231
|
} catch {
|
|
230
232
|
diagnostics.push(
|
|
231
|
-
makeDgmoError(
|
|
233
|
+
makeDgmoError(tagsLineNumber, `Tags file not found: ${tagsDirective}`)
|
|
232
234
|
);
|
|
233
235
|
}
|
|
234
236
|
}
|
package/src/render.ts
CHANGED
|
@@ -49,6 +49,9 @@ export async function render(
|
|
|
49
49
|
theme?: 'light' | 'dark' | 'transparent';
|
|
50
50
|
palette?: string;
|
|
51
51
|
branding?: boolean;
|
|
52
|
+
c4Level?: 'context' | 'containers' | 'components' | 'deployment';
|
|
53
|
+
c4System?: string;
|
|
54
|
+
c4Container?: string;
|
|
52
55
|
},
|
|
53
56
|
): Promise<string> {
|
|
54
57
|
const theme = options?.theme ?? 'light';
|
|
@@ -66,5 +69,10 @@ export async function render(
|
|
|
66
69
|
|
|
67
70
|
// D3 and unknown/null frameworks both go through D3 renderer
|
|
68
71
|
await ensureDom();
|
|
69
|
-
return renderD3ForExport(content, theme, paletteColors, undefined, {
|
|
72
|
+
return renderD3ForExport(content, theme, paletteColors, undefined, {
|
|
73
|
+
branding,
|
|
74
|
+
c4Level: options?.c4Level,
|
|
75
|
+
c4System: options?.c4System,
|
|
76
|
+
c4Container: options?.c4Container,
|
|
77
|
+
});
|
|
70
78
|
}
|
|
@@ -203,6 +203,7 @@ const PARTICIPANT_RULES: readonly InferenceRule[] = [
|
|
|
203
203
|
{ pattern: /User$/i, type: 'actor' },
|
|
204
204
|
{ pattern: /Actor$/i, type: 'actor' },
|
|
205
205
|
{ pattern: /Analyst$/i, type: 'actor' },
|
|
206
|
+
{ pattern: /Staff$/i, type: 'actor' },
|
|
206
207
|
|
|
207
208
|
// ── 7. Frontend patterns ────────────────────────────────
|
|
208
209
|
{ pattern: /App$/i, type: 'frontend' },
|
package/src/sequence/renderer.ts
CHANGED
|
@@ -1925,7 +1925,8 @@ export function renderSequenceDiagram(
|
|
|
1925
1925
|
'data-line-number',
|
|
1926
1926
|
String(messages[step.messageIndex].lineNumber)
|
|
1927
1927
|
)
|
|
1928
|
-
.attr('data-msg-index', String(step.messageIndex))
|
|
1928
|
+
.attr('data-msg-index', String(step.messageIndex))
|
|
1929
|
+
.attr('data-step-index', String(i));
|
|
1929
1930
|
|
|
1930
1931
|
if (step.label) {
|
|
1931
1932
|
const labelEl = svg
|
|
@@ -1940,7 +1941,8 @@ export function renderSequenceDiagram(
|
|
|
1940
1941
|
'data-line-number',
|
|
1941
1942
|
String(messages[step.messageIndex].lineNumber)
|
|
1942
1943
|
)
|
|
1943
|
-
.attr('data-msg-index', String(step.messageIndex))
|
|
1944
|
+
.attr('data-msg-index', String(step.messageIndex))
|
|
1945
|
+
.attr('data-step-index', String(i));
|
|
1944
1946
|
renderInlineText(labelEl, step.label, palette);
|
|
1945
1947
|
}
|
|
1946
1948
|
} else {
|
|
@@ -1966,7 +1968,8 @@ export function renderSequenceDiagram(
|
|
|
1966
1968
|
'data-line-number',
|
|
1967
1969
|
String(messages[step.messageIndex].lineNumber)
|
|
1968
1970
|
)
|
|
1969
|
-
.attr('data-msg-index', String(step.messageIndex))
|
|
1971
|
+
.attr('data-msg-index', String(step.messageIndex))
|
|
1972
|
+
.attr('data-step-index', String(i));
|
|
1970
1973
|
|
|
1971
1974
|
if (step.label) {
|
|
1972
1975
|
const midX = (x1 + x2) / 2;
|
|
@@ -1982,7 +1985,8 @@ export function renderSequenceDiagram(
|
|
|
1982
1985
|
'data-line-number',
|
|
1983
1986
|
String(messages[step.messageIndex].lineNumber)
|
|
1984
1987
|
)
|
|
1985
|
-
.attr('data-msg-index', String(step.messageIndex))
|
|
1988
|
+
.attr('data-msg-index', String(step.messageIndex))
|
|
1989
|
+
.attr('data-step-index', String(i));
|
|
1986
1990
|
renderInlineText(labelEl, step.label, palette);
|
|
1987
1991
|
}
|
|
1988
1992
|
}
|
|
@@ -2011,7 +2015,8 @@ export function renderSequenceDiagram(
|
|
|
2011
2015
|
'data-line-number',
|
|
2012
2016
|
String(messages[step.messageIndex].lineNumber)
|
|
2013
2017
|
)
|
|
2014
|
-
.attr('data-msg-index', String(step.messageIndex))
|
|
2018
|
+
.attr('data-msg-index', String(step.messageIndex))
|
|
2019
|
+
.attr('data-step-index', String(i));
|
|
2015
2020
|
|
|
2016
2021
|
if (step.label) {
|
|
2017
2022
|
const midX = (x1 + x2) / 2;
|
|
@@ -2027,7 +2032,8 @@ export function renderSequenceDiagram(
|
|
|
2027
2032
|
'data-line-number',
|
|
2028
2033
|
String(messages[step.messageIndex].lineNumber)
|
|
2029
2034
|
)
|
|
2030
|
-
.attr('data-msg-index', String(step.messageIndex))
|
|
2035
|
+
.attr('data-msg-index', String(step.messageIndex))
|
|
2036
|
+
.attr('data-step-index', String(i));
|
|
2031
2037
|
renderInlineText(labelEl, step.label, palette);
|
|
2032
2038
|
}
|
|
2033
2039
|
}
|