@diagrammo/dgmo 0.15.1 → 0.17.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 +612 -734
- package/dist/advanced.d.cts +42 -36
- package/dist/advanced.d.ts +42 -36
- package/dist/advanced.js +612 -733
- package/dist/auto.cjs +508 -620
- package/dist/auto.js +105 -105
- package/dist/auto.mjs +508 -620
- package/dist/cli.cjs +144 -144
- 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 +497 -608
- package/dist/index.js +497 -608
- package/dist/internal.cjs +612 -734
- package/dist/internal.d.cts +42 -36
- package/dist/internal.d.ts +42 -36
- package/dist/internal.js +612 -733
- package/dist/pert.d.cts +2 -2
- package/dist/pert.d.ts +2 -2
- package/docs/language-reference.md +97 -84
- package/docs/migration-sequence-color-to-tags.md +1 -1
- 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 +11 -11
- package/gallery/fixtures/sequence-tags.dgmo +10 -10
- package/gallery/fixtures/sequence.dgmo +4 -4
- 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 +7 -3
- package/src/advanced.ts +0 -1
- package/src/auto/index.ts +2 -2
- package/src/boxes-and-lines/layout.ts +1 -2
- package/src/boxes-and-lines/renderer.ts +5 -1
- package/src/c4/parser.ts +2 -2
- package/src/c4/renderer.ts +15 -8
- package/src/chart.ts +18 -9
- package/src/class/parser.ts +8 -7
- package/src/class/renderer.ts +17 -6
- package/src/cli.ts +8 -8
- package/src/completion.ts +14 -17
- package/src/cycle/parser.ts +15 -1
- package/src/cycle/renderer.ts +6 -3
- package/src/d3.ts +88 -49
- package/src/diagnostics.ts +20 -0
- package/src/echarts.ts +28 -11
- package/src/editor/dgmo.grammar +1 -3
- package/src/editor/dgmo.grammar.d.ts +1 -1
- 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 +19 -12
- 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 +6 -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 +8 -6
- package/src/journey-map/parser.ts +11 -4
- package/src/journey-map/renderer.ts +3 -1
- package/src/kanban/parser.ts +11 -7
- package/src/kanban/renderer.ts +3 -1
- package/src/mindmap/parser.ts +4 -5
- package/src/mindmap/renderer.ts +2 -1
- package/src/org/parser.ts +3 -3
- package/src/org/renderer.ts +4 -3
- package/src/pert/analyzer.ts +10 -10
- package/src/pert/layout.ts +1 -1
- package/src/pert/parser.ts +8 -8
- package/src/pert/renderer.ts +7 -2
- package/src/pert/types.ts +1 -1
- package/src/pyramid/parser.ts +13 -1
- package/src/raci/parser.ts +42 -12
- package/src/raci/renderer.ts +2 -1
- package/src/raci/types.ts +4 -3
- package/src/ring/parser.ts +13 -1
- package/src/sequence/parser.ts +81 -23
- package/src/sequence/participant-inference.ts +18 -181
- package/src/sequence/renderer.ts +48 -137
- package/src/sitemap/layout.ts +0 -2
- package/src/sitemap/parser.ts +12 -38
- package/src/sitemap/renderer.ts +13 -13
- package/src/sitemap/types.ts +0 -1
- package/src/tech-radar/parser.ts +2 -2
- 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/extract-alias.ts +1 -1
- package/src/utils/inline-markdown.ts +1 -1
- 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/utils/time-ticks.ts +1 -1
- package/src/wireframe/parser.ts +3 -3
package/src/sequence/renderer.ts
CHANGED
|
@@ -49,7 +49,6 @@ const PARTICIPANT_BOX_HEIGHT = 50;
|
|
|
49
49
|
const TOP_MARGIN = 20;
|
|
50
50
|
const TITLE_HEIGHT = 30;
|
|
51
51
|
const PARTICIPANT_Y_OFFSET = 10;
|
|
52
|
-
const SERVICE_BORDER_RADIUS = 10;
|
|
53
52
|
const MESSAGE_START_OFFSET = 30;
|
|
54
53
|
const LIFELINE_TAIL = 30;
|
|
55
54
|
const ARROWHEAD_SIZE = 8;
|
|
@@ -180,25 +179,6 @@ function renderRectParticipant(
|
|
|
180
179
|
.attr('stroke-width', SW);
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
function renderServiceParticipant(
|
|
184
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
185
|
-
palette: PaletteColors,
|
|
186
|
-
isDark: boolean,
|
|
187
|
-
color?: string,
|
|
188
|
-
solid?: boolean
|
|
189
|
-
): void {
|
|
190
|
-
g.append('rect')
|
|
191
|
-
.attr('x', -W / 2)
|
|
192
|
-
.attr('y', 0)
|
|
193
|
-
.attr('width', W)
|
|
194
|
-
.attr('height', H)
|
|
195
|
-
.attr('rx', SERVICE_BORDER_RADIUS)
|
|
196
|
-
.attr('ry', SERVICE_BORDER_RADIUS)
|
|
197
|
-
.attr('fill', fill(palette, isDark, color, solid))
|
|
198
|
-
.attr('stroke', stroke(palette, color))
|
|
199
|
-
.attr('stroke-width', SW);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
182
|
function renderActorParticipant(
|
|
203
183
|
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
204
184
|
palette: PaletteColors,
|
|
@@ -443,99 +423,6 @@ function renderCacheParticipant(
|
|
|
443
423
|
.attr('stroke-dasharray', dash);
|
|
444
424
|
}
|
|
445
425
|
|
|
446
|
-
function renderNetworkingParticipant(
|
|
447
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
448
|
-
palette: PaletteColors,
|
|
449
|
-
isDark: boolean,
|
|
450
|
-
color?: string,
|
|
451
|
-
solid?: boolean
|
|
452
|
-
): void {
|
|
453
|
-
// Hexagon fitting within W x H
|
|
454
|
-
const inset = 16;
|
|
455
|
-
const points = [
|
|
456
|
-
`${-W / 2 + inset},0`,
|
|
457
|
-
`${W / 2 - inset},0`,
|
|
458
|
-
`${W / 2},${H / 2}`,
|
|
459
|
-
`${W / 2 - inset},${H}`,
|
|
460
|
-
`${-W / 2 + inset},${H}`,
|
|
461
|
-
`${-W / 2},${H / 2}`,
|
|
462
|
-
].join(' ');
|
|
463
|
-
g.append('polygon')
|
|
464
|
-
.attr('points', points)
|
|
465
|
-
.attr('fill', fill(palette, isDark, color, solid))
|
|
466
|
-
.attr('stroke', stroke(palette, color))
|
|
467
|
-
.attr('stroke-width', SW);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function renderFrontendParticipant(
|
|
471
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
472
|
-
palette: PaletteColors,
|
|
473
|
-
isDark: boolean,
|
|
474
|
-
color?: string,
|
|
475
|
-
solid?: boolean
|
|
476
|
-
): void {
|
|
477
|
-
// Monitor shape fitting within W x H
|
|
478
|
-
const screenH = H - 10;
|
|
479
|
-
const s = stroke(palette, color);
|
|
480
|
-
g.append('rect')
|
|
481
|
-
.attr('x', -W / 2)
|
|
482
|
-
.attr('y', 0)
|
|
483
|
-
.attr('width', W)
|
|
484
|
-
.attr('height', screenH)
|
|
485
|
-
.attr('rx', 3)
|
|
486
|
-
.attr('ry', 3)
|
|
487
|
-
.attr('fill', fill(palette, isDark, color, solid))
|
|
488
|
-
.attr('stroke', s)
|
|
489
|
-
.attr('stroke-width', SW);
|
|
490
|
-
// Stand
|
|
491
|
-
g.append('line')
|
|
492
|
-
.attr('x1', 0)
|
|
493
|
-
.attr('y1', screenH)
|
|
494
|
-
.attr('x2', 0)
|
|
495
|
-
.attr('y2', H - 2)
|
|
496
|
-
.attr('stroke', s)
|
|
497
|
-
.attr('stroke-width', SW);
|
|
498
|
-
// Base
|
|
499
|
-
g.append('line')
|
|
500
|
-
.attr('x1', -14)
|
|
501
|
-
.attr('y1', H - 2)
|
|
502
|
-
.attr('x2', 14)
|
|
503
|
-
.attr('y2', H - 2)
|
|
504
|
-
.attr('stroke', s)
|
|
505
|
-
.attr('stroke-width', SW);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function renderExternalParticipant(
|
|
509
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
510
|
-
palette: PaletteColors,
|
|
511
|
-
isDark: boolean,
|
|
512
|
-
color?: string,
|
|
513
|
-
solid?: boolean
|
|
514
|
-
): void {
|
|
515
|
-
// Dashed border rectangle
|
|
516
|
-
g.append('rect')
|
|
517
|
-
.attr('x', -W / 2)
|
|
518
|
-
.attr('y', 0)
|
|
519
|
-
.attr('width', W)
|
|
520
|
-
.attr('height', H)
|
|
521
|
-
.attr('rx', 2)
|
|
522
|
-
.attr('ry', 2)
|
|
523
|
-
.attr('fill', fill(palette, isDark, color, solid))
|
|
524
|
-
.attr('stroke', stroke(palette, color))
|
|
525
|
-
.attr('stroke-width', SW)
|
|
526
|
-
.attr('stroke-dasharray', '6 3');
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
function renderGatewayParticipant(
|
|
530
|
-
g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
|
|
531
|
-
palette: PaletteColors,
|
|
532
|
-
isDark: boolean,
|
|
533
|
-
color?: string,
|
|
534
|
-
_solid?: boolean
|
|
535
|
-
): void {
|
|
536
|
-
renderRectParticipant(g, palette, isDark, color);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
426
|
// ============================================================
|
|
540
427
|
// Collapsible Section Support
|
|
541
428
|
// ============================================================
|
|
@@ -1528,7 +1415,7 @@ export function renderSequenceDiagram(
|
|
|
1528
1415
|
// Ensure contentBottomY accounts for all note extents
|
|
1529
1416
|
const lastStep = renderSteps[renderSteps.length - 1];
|
|
1530
1417
|
const lastIsSelfCall =
|
|
1531
|
-
lastStep
|
|
1418
|
+
lastStep?.type === 'call' && lastStep.from === lastStep.to;
|
|
1532
1419
|
const lastStepTrailing = lastIsSelfCall ? SELF_CALL_HEIGHT + 25 : stepSpacing;
|
|
1533
1420
|
let contentBottomY =
|
|
1534
1421
|
renderSteps.length > 0
|
|
@@ -1968,6 +1855,13 @@ export function renderSequenceDiagram(
|
|
|
1968
1855
|
// FRAME_PADDING_TOP declared earlier (near BLOCK_HEADER_SPACE)
|
|
1969
1856
|
const FRAME_PADDING_BOTTOM = 15;
|
|
1970
1857
|
const FRAME_LABEL_HEIGHT = 18;
|
|
1858
|
+
// Self-loop projects ACTIVATION_WIDTH/2 + SELF_CALL_WIDTH (=35) past the
|
|
1859
|
+
// lifeline; FRAME_PADDING_X (=30) leaves no breathing room. When a block
|
|
1860
|
+
// contains a self-arrow, extend the frame on the loop's side so the loop
|
|
1861
|
+
// sits comfortably inside.
|
|
1862
|
+
const SELF_ARROW_PROJECTION = ACTIVATION_WIDTH / 2 + SELF_CALL_WIDTH;
|
|
1863
|
+
const SELF_ARROW_FRAME_PAD = 10;
|
|
1864
|
+
const frameRightmostX = Math.max(...Array.from(participantX.values()));
|
|
1971
1865
|
|
|
1972
1866
|
// Collect message indices from an element subtree
|
|
1973
1867
|
const collectMsgIndices = (els: SequenceElement[]): number[] => {
|
|
@@ -2063,14 +1957,46 @@ export function renderSequenceDiagram(
|
|
|
2063
1957
|
}
|
|
2064
1958
|
}
|
|
2065
1959
|
|
|
2066
|
-
|
|
1960
|
+
// Self-arrow geometry: extend frame on the loop's side so loops sit
|
|
1961
|
+
// comfortably inside, and extend vertically if the block's last step
|
|
1962
|
+
// is a self-call (whose loop drops SELF_CALL_HEIGHT below stepY).
|
|
1963
|
+
let extraLeft = 0;
|
|
1964
|
+
let extraRight = 0;
|
|
1965
|
+
let maxStepIsSelfCall = false;
|
|
1966
|
+
for (const mi of allIndices) {
|
|
1967
|
+
const m = messages[mi];
|
|
1968
|
+
if (m.from === m.to) {
|
|
1969
|
+
const px = participantX.get(m.from);
|
|
1970
|
+
if (px !== undefined) {
|
|
1971
|
+
const flipLeft = px === frameRightmostX;
|
|
1972
|
+
if (flipLeft) {
|
|
1973
|
+
const loopMin = px - SELF_ARROW_PROJECTION;
|
|
1974
|
+
const need =
|
|
1975
|
+
minPX - FRAME_PADDING_X - loopMin + SELF_ARROW_FRAME_PAD;
|
|
1976
|
+
if (need > 0) extraLeft = Math.max(extraLeft, need);
|
|
1977
|
+
} else {
|
|
1978
|
+
const loopMax = px + SELF_ARROW_PROJECTION;
|
|
1979
|
+
const need =
|
|
1980
|
+
loopMax - (maxPX + FRAME_PADDING_X) + SELF_ARROW_FRAME_PAD;
|
|
1981
|
+
if (need > 0) extraRight = Math.max(extraRight, need);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
if (msgToLastStep.get(mi) === maxStep) {
|
|
1985
|
+
maxStepIsSelfCall = true;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
const frameX = minPX - FRAME_PADDING_X - extraLeft;
|
|
2067
1991
|
const frameY = stepY(minStep) - FRAME_PADDING_TOP;
|
|
2068
|
-
const frameW =
|
|
1992
|
+
const frameW =
|
|
1993
|
+
maxPX - minPX + FRAME_PADDING_X * 2 + extraLeft + extraRight;
|
|
2069
1994
|
const frameH =
|
|
2070
1995
|
stepY(maxStep) -
|
|
2071
1996
|
stepY(minStep) +
|
|
2072
1997
|
FRAME_PADDING_TOP +
|
|
2073
|
-
FRAME_PADDING_BOTTOM
|
|
1998
|
+
FRAME_PADDING_BOTTOM +
|
|
1999
|
+
(maxStepIsSelfCall ? SELF_CALL_HEIGHT : 0);
|
|
2074
2000
|
|
|
2075
2001
|
// Frame border
|
|
2076
2002
|
svg
|
|
@@ -2093,7 +2019,7 @@ export function renderSequenceDiagram(
|
|
|
2093
2019
|
x: frameX + 6,
|
|
2094
2020
|
y: frameY + FRAME_LABEL_HEIGHT - 4,
|
|
2095
2021
|
text: `${el.type} ${el.label}`,
|
|
2096
|
-
bold:
|
|
2022
|
+
bold: false,
|
|
2097
2023
|
italic: false,
|
|
2098
2024
|
blockLine: el.lineNumber,
|
|
2099
2025
|
});
|
|
@@ -2121,7 +2047,7 @@ export function renderSequenceDiagram(
|
|
|
2121
2047
|
y: dividerY + 14,
|
|
2122
2048
|
text: `else if ${branchData.label}`,
|
|
2123
2049
|
bold: false,
|
|
2124
|
-
italic:
|
|
2050
|
+
italic: false,
|
|
2125
2051
|
blockLine: branchData.lineNumber,
|
|
2126
2052
|
});
|
|
2127
2053
|
}
|
|
@@ -2150,7 +2076,7 @@ export function renderSequenceDiagram(
|
|
|
2150
2076
|
y: dividerY + 14,
|
|
2151
2077
|
text: 'else',
|
|
2152
2078
|
bold: false,
|
|
2153
|
-
italic:
|
|
2079
|
+
italic: false,
|
|
2154
2080
|
blockLine: el.elseLineNumber,
|
|
2155
2081
|
});
|
|
2156
2082
|
}
|
|
@@ -2292,7 +2218,7 @@ export function renderSequenceDiagram(
|
|
|
2292
2218
|
|
|
2293
2219
|
// Render section dividers
|
|
2294
2220
|
const leftmostX = Math.min(...Array.from(participantX.values()));
|
|
2295
|
-
const rightmostX =
|
|
2221
|
+
const rightmostX = frameRightmostX;
|
|
2296
2222
|
const sectionLineX1 = leftmostX - PARTICIPANT_BOX_WIDTH / 2 - 10;
|
|
2297
2223
|
const sectionLineX2 = rightmostX + PARTICIPANT_BOX_WIDTH / 2 + 10;
|
|
2298
2224
|
|
|
@@ -2762,7 +2688,7 @@ export function renderSequenceDiagram(
|
|
|
2762
2688
|
const legendConfig: LegendConfig = {
|
|
2763
2689
|
groups: resolvedGroups,
|
|
2764
2690
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
2765
|
-
mode: '
|
|
2691
|
+
mode: 'preview',
|
|
2766
2692
|
};
|
|
2767
2693
|
const legendState: LegendState = {
|
|
2768
2694
|
activeGroup: activeTagGroup ?? null,
|
|
@@ -2853,27 +2779,12 @@ function renderParticipant(
|
|
|
2853
2779
|
case 'database':
|
|
2854
2780
|
renderDatabaseParticipant(g, palette, isDark, color, solid);
|
|
2855
2781
|
break;
|
|
2856
|
-
case 'service':
|
|
2857
|
-
renderServiceParticipant(g, palette, isDark, color, solid);
|
|
2858
|
-
break;
|
|
2859
2782
|
case 'queue':
|
|
2860
2783
|
renderQueueParticipant(g, palette, isDark, color, solid);
|
|
2861
2784
|
break;
|
|
2862
2785
|
case 'cache':
|
|
2863
2786
|
renderCacheParticipant(g, palette, isDark, color, solid);
|
|
2864
2787
|
break;
|
|
2865
|
-
case 'networking':
|
|
2866
|
-
renderNetworkingParticipant(g, palette, isDark, color, solid);
|
|
2867
|
-
break;
|
|
2868
|
-
case 'frontend':
|
|
2869
|
-
renderFrontendParticipant(g, palette, isDark, color, solid);
|
|
2870
|
-
break;
|
|
2871
|
-
case 'external':
|
|
2872
|
-
renderExternalParticipant(g, palette, isDark, color, solid);
|
|
2873
|
-
break;
|
|
2874
|
-
case 'gateway':
|
|
2875
|
-
renderGatewayParticipant(g, palette, isDark, color, solid);
|
|
2876
|
-
break;
|
|
2877
2788
|
default:
|
|
2878
2789
|
renderRectParticipant(g, palette, isDark, color, solid);
|
|
2879
2790
|
break;
|
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
|
});
|
package/src/sitemap/parser.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
5
|
import type { PaletteColors } from '../palettes';
|
|
6
|
-
import { resolveColorWithDiagnostic } from '../colors';
|
|
7
6
|
import type { DgmoError } from '../diagnostics';
|
|
8
7
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
9
8
|
import { normalizeName } from '../utils/name-normalize';
|
|
@@ -20,7 +19,6 @@ import {
|
|
|
20
19
|
measureIndent,
|
|
21
20
|
extractColor,
|
|
22
21
|
parsePipeMetadata,
|
|
23
|
-
inferArrowColor,
|
|
24
22
|
MULTIPLE_PIPE_ERROR,
|
|
25
23
|
parseFirstLine,
|
|
26
24
|
OPTION_NOCOLON_RE,
|
|
@@ -39,10 +37,11 @@ const CONTAINER_RE = /^\[([^\]]+)\]\s*(?:\|\s*(.+))?$/;
|
|
|
39
37
|
const METADATA_RE = /^([^:]+):\s*(.+)$/;
|
|
40
38
|
|
|
41
39
|
/**
|
|
42
|
-
* Arrow line: `-label
|
|
43
|
-
*
|
|
40
|
+
* Arrow line: `-label->` or `->` followed by target label.
|
|
41
|
+
* Edges have no color slot (spec §1.7).
|
|
42
|
+
* Captures: [1] label, [2] target
|
|
44
43
|
*/
|
|
45
|
-
const ARROW_RE = /^-([
|
|
44
|
+
const ARROW_RE = /^-([^>][^>]*?)?\s*->\s*(.+)$/;
|
|
46
45
|
const BARE_ARROW_RE = /^->\s*(.+)$/;
|
|
47
46
|
|
|
48
47
|
// ============================================================
|
|
@@ -51,12 +50,11 @@ const BARE_ARROW_RE = /^->\s*(.+)$/;
|
|
|
51
50
|
|
|
52
51
|
function parseArrowLine(
|
|
53
52
|
trimmed: string,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
_palette: PaletteColors | undefined,
|
|
54
|
+
_lineNumber: number,
|
|
55
|
+
_diagnostics: DgmoError[]
|
|
57
56
|
): {
|
|
58
57
|
label?: string;
|
|
59
|
-
color?: string;
|
|
60
58
|
target: string;
|
|
61
59
|
targetIsGroup: boolean;
|
|
62
60
|
} | null {
|
|
@@ -71,33 +69,14 @@ function parseArrowLine(
|
|
|
71
69
|
};
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
// Labeled
|
|
72
|
+
// Labeled arrow: -label-> Target
|
|
75
73
|
const arrowMatch = trimmed.match(ARROW_RE);
|
|
76
74
|
if (arrowMatch) {
|
|
77
75
|
const label = arrowMatch[1]?.trim() || undefined;
|
|
78
|
-
|
|
79
|
-
? resolveColorWithDiagnostic(
|
|
80
|
-
arrowMatch[2].trim(),
|
|
81
|
-
lineNumber,
|
|
82
|
-
diagnostics,
|
|
83
|
-
palette
|
|
84
|
-
)
|
|
85
|
-
: undefined;
|
|
86
|
-
if (label && !color) {
|
|
87
|
-
const inferred = inferArrowColor(label);
|
|
88
|
-
if (inferred)
|
|
89
|
-
color = resolveColorWithDiagnostic(
|
|
90
|
-
inferred,
|
|
91
|
-
lineNumber,
|
|
92
|
-
diagnostics,
|
|
93
|
-
palette
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
const rawTarget = arrowMatch[3].trim();
|
|
76
|
+
const rawTarget = arrowMatch[2].trim();
|
|
97
77
|
const groupMatch = rawTarget.match(/^\[(.+)\]$/);
|
|
98
78
|
return {
|
|
99
79
|
label,
|
|
100
|
-
color,
|
|
101
80
|
target: groupMatch ? groupMatch[1].trim() : rawTarget,
|
|
102
81
|
targetIsGroup: !!groupMatch,
|
|
103
82
|
};
|
|
@@ -183,7 +162,7 @@ export function parseSitemap(
|
|
|
183
162
|
result.diagnostics.push(makeDgmoError(line, message, 'warning'));
|
|
184
163
|
};
|
|
185
164
|
|
|
186
|
-
if (!content
|
|
165
|
+
if (!content?.trim()) {
|
|
187
166
|
return fail(0, 'No content provided');
|
|
188
167
|
}
|
|
189
168
|
|
|
@@ -216,7 +195,6 @@ export function parseSitemap(
|
|
|
216
195
|
targetLabel: string;
|
|
217
196
|
targetIsGroup: boolean;
|
|
218
197
|
label?: string;
|
|
219
|
-
color?: string;
|
|
220
198
|
lineNumber: number;
|
|
221
199
|
}[] = [];
|
|
222
200
|
|
|
@@ -309,7 +287,7 @@ export function parseSitemap(
|
|
|
309
287
|
}
|
|
310
288
|
}
|
|
311
289
|
|
|
312
|
-
// Tag group entries (indented Value
|
|
290
|
+
// Tag group entries (indented `Value color` under tag heading; §1.5)
|
|
313
291
|
// First entry is the default unless another is marked `default`
|
|
314
292
|
if (currentTagGroup && !contentStarted) {
|
|
315
293
|
const indent = measureIndent(line);
|
|
@@ -319,7 +297,7 @@ export function parseSitemap(
|
|
|
319
297
|
if (!color) {
|
|
320
298
|
pushError(
|
|
321
299
|
lineNumber,
|
|
322
|
-
`Expected 'Value
|
|
300
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
323
301
|
);
|
|
324
302
|
continue;
|
|
325
303
|
}
|
|
@@ -365,7 +343,6 @@ export function parseSitemap(
|
|
|
365
343
|
targetLabel: arrowInfo.target,
|
|
366
344
|
targetIsGroup: arrowInfo.targetIsGroup,
|
|
367
345
|
label: arrowInfo.label,
|
|
368
|
-
color: arrowInfo.color,
|
|
369
346
|
lineNumber,
|
|
370
347
|
});
|
|
371
348
|
}
|
|
@@ -486,7 +463,6 @@ export function parseSitemap(
|
|
|
486
463
|
sourceId: arrow.sourceNode.id,
|
|
487
464
|
targetId: aliasHit,
|
|
488
465
|
label: arrow.label,
|
|
489
|
-
color: arrow.color,
|
|
490
466
|
lineNumber: arrow.lineNumber,
|
|
491
467
|
});
|
|
492
468
|
continue;
|
|
@@ -508,7 +484,6 @@ export function parseSitemap(
|
|
|
508
484
|
sourceId: arrow.sourceNode.id,
|
|
509
485
|
targetId: targetContainer.id,
|
|
510
486
|
label: arrow.label,
|
|
511
|
-
color: arrow.color,
|
|
512
487
|
lineNumber: arrow.lineNumber,
|
|
513
488
|
});
|
|
514
489
|
} else {
|
|
@@ -526,7 +501,6 @@ export function parseSitemap(
|
|
|
526
501
|
sourceId: arrow.sourceNode.id,
|
|
527
502
|
targetId: targetNode.id,
|
|
528
503
|
label: arrow.label,
|
|
529
|
-
color: arrow.color,
|
|
530
504
|
lineNumber: arrow.lineNumber,
|
|
531
505
|
});
|
|
532
506
|
}
|
package/src/sitemap/renderer.ts
CHANGED
|
@@ -116,7 +116,8 @@ export function renderSitemap(
|
|
|
116
116
|
onClickItem?: (lineNumber: number) => void,
|
|
117
117
|
exportDims?: { width?: number; height?: number },
|
|
118
118
|
activeTagGroup?: string | null,
|
|
119
|
-
hiddenAttributes?: Set<string
|
|
119
|
+
hiddenAttributes?: Set<string>,
|
|
120
|
+
exportMode?: boolean
|
|
120
121
|
): void {
|
|
121
122
|
// Clear existing content
|
|
122
123
|
d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
|
|
@@ -184,11 +185,9 @@ export function renderSitemap(
|
|
|
184
185
|
.attr('points', `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`)
|
|
185
186
|
.attr('fill', palette.textMuted);
|
|
186
187
|
|
|
187
|
-
//
|
|
188
|
+
// Edges have no color slot (spec §1.7); keep empty set so the marker-setup
|
|
189
|
+
// loop is a no-op but the symbol stays available for future color sources.
|
|
188
190
|
const edgeColors = new Set<string>();
|
|
189
|
-
for (const edge of layout.edges) {
|
|
190
|
-
if (edge.color) edgeColors.add(edge.color);
|
|
191
|
-
}
|
|
192
191
|
for (const color of edgeColors) {
|
|
193
192
|
const id = `sm-arrow-${color.replace('#', '')}`;
|
|
194
193
|
defs
|
|
@@ -379,10 +378,8 @@ export function renderSitemap(
|
|
|
379
378
|
.attr('class', 'sitemap-edge-group')
|
|
380
379
|
.attr('data-line-number', String(edge.lineNumber));
|
|
381
380
|
|
|
382
|
-
const edgeColor =
|
|
383
|
-
const markerId =
|
|
384
|
-
? `sm-arrow-${edge.color.replace('#', '')}`
|
|
385
|
-
: 'sm-arrow';
|
|
381
|
+
const edgeColor = palette.textMuted;
|
|
382
|
+
const markerId = 'sm-arrow';
|
|
386
383
|
|
|
387
384
|
const gen = edge.deferred ? lineGeneratorLinear : lineGenerator;
|
|
388
385
|
const pathD = gen(edge.points);
|
|
@@ -602,7 +599,8 @@ export function renderSitemap(
|
|
|
602
599
|
isDark,
|
|
603
600
|
activeTagGroup,
|
|
604
601
|
undefined,
|
|
605
|
-
hiddenAttributes
|
|
602
|
+
hiddenAttributes,
|
|
603
|
+
exportMode
|
|
606
604
|
);
|
|
607
605
|
}
|
|
608
606
|
|
|
@@ -647,7 +645,8 @@ export function renderSitemap(
|
|
|
647
645
|
isDark,
|
|
648
646
|
activeTagGroup,
|
|
649
647
|
width,
|
|
650
|
-
hiddenAttributes
|
|
648
|
+
hiddenAttributes,
|
|
649
|
+
exportMode
|
|
651
650
|
);
|
|
652
651
|
}
|
|
653
652
|
}
|
|
@@ -663,7 +662,8 @@ function renderLegend(
|
|
|
663
662
|
isDark: boolean,
|
|
664
663
|
activeTagGroup?: string | null,
|
|
665
664
|
fixedWidth?: number,
|
|
666
|
-
hiddenAttributes?: Set<string
|
|
665
|
+
hiddenAttributes?: Set<string>,
|
|
666
|
+
exportMode?: boolean
|
|
667
667
|
): void {
|
|
668
668
|
if (legendGroups.length === 0) return;
|
|
669
669
|
|
|
@@ -678,7 +678,7 @@ function renderLegend(
|
|
|
678
678
|
const legendConfig: LegendConfig = {
|
|
679
679
|
groups,
|
|
680
680
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
681
|
-
mode: '
|
|
681
|
+
mode: exportMode ? 'export' : 'preview',
|
|
682
682
|
capsulePillAddonWidth: eyeAddonWidth,
|
|
683
683
|
};
|
|
684
684
|
const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
|
package/src/sitemap/types.ts
CHANGED
package/src/tech-radar/parser.ts
CHANGED
|
@@ -62,7 +62,7 @@ export function parseTechRadar(content: string): ParsedTechRadar {
|
|
|
62
62
|
result.diagnostics.push(makeDgmoError(line, message, 'warning'));
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
if (!content
|
|
65
|
+
if (!content?.trim()) {
|
|
66
66
|
return fail(0, 'No content provided');
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -115,7 +115,7 @@ export function parseTechRadar(content: string): ParsedTechRadar {
|
|
|
115
115
|
// --- First line: chart type + title ---
|
|
116
116
|
if (!headerParsed) {
|
|
117
117
|
const firstLine = parseFirstLine(trimmed);
|
|
118
|
-
if (firstLine
|
|
118
|
+
if (firstLine?.chartType === 'tech-radar') {
|
|
119
119
|
result.title = firstLine.title ?? '';
|
|
120
120
|
result.titleLineNumber = lineNumber;
|
|
121
121
|
headerParsed = true;
|
|
@@ -163,7 +163,7 @@ export function renderTechRadar(
|
|
|
163
163
|
},
|
|
164
164
|
],
|
|
165
165
|
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
166
|
-
mode: '
|
|
166
|
+
mode: options?.exportMode ? 'export' : 'preview',
|
|
167
167
|
controlsGroup: {
|
|
168
168
|
toggles: [
|
|
169
169
|
{
|
|
@@ -1195,7 +1195,8 @@ export function renderTechRadarForExport(
|
|
|
1195
1195
|
palette: PaletteColors,
|
|
1196
1196
|
isDark: boolean,
|
|
1197
1197
|
exportDims?: D3ExportDimensions,
|
|
1198
|
-
viewState?: CompactViewState
|
|
1198
|
+
viewState?: CompactViewState,
|
|
1199
|
+
exportMode?: boolean
|
|
1199
1200
|
): void {
|
|
1200
1201
|
renderTechRadar(
|
|
1201
1202
|
container,
|
|
@@ -1204,6 +1205,7 @@ export function renderTechRadarForExport(
|
|
|
1204
1205
|
isDark,
|
|
1205
1206
|
undefined,
|
|
1206
1207
|
exportDims,
|
|
1207
|
-
viewState
|
|
1208
|
+
viewState,
|
|
1209
|
+
{ exportMode }
|
|
1208
1210
|
);
|
|
1209
1211
|
}
|
package/src/tech-radar/types.ts
CHANGED
|
@@ -78,4 +78,6 @@ export interface TechRadarRenderOptions {
|
|
|
78
78
|
onLegendGroupToggle?: (groupName: string) => void;
|
|
79
79
|
/** Active line from the editor cursor — triggers popover/expansion for that blip. */
|
|
80
80
|
activeLine?: number | null;
|
|
81
|
+
/** True when rendering for export (PNG/SVG/PDF) — controls whether collapsed legend pills and cog are stripped. */
|
|
82
|
+
exportMode?: boolean;
|
|
81
83
|
}
|
package/src/utils/arrows.ts
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
import type { DgmoError } from '../diagnostics';
|
|
24
24
|
import { makeDgmoError } from '../diagnostics';
|
|
25
|
-
import { RECOGNIZED_COLOR_NAMES } from '../colors';
|
|
26
25
|
|
|
27
26
|
interface ParsedArrow {
|
|
28
27
|
from: string;
|
|
@@ -140,10 +139,9 @@ export interface ParseInArrowLabelResult {
|
|
|
140
139
|
*
|
|
141
140
|
* This helper is intentionally chart-agnostic: it operates on an already
|
|
142
141
|
* extracted label string, leaving each chart's existing arrow-finding
|
|
143
|
-
* tokenization in place.
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* `matchColorParens()` from this module for the shared lookup.
|
|
142
|
+
* tokenization in place. Edges no longer have a color slot on any chart
|
|
143
|
+
* type (see spec §1.7 "Edge color is not a feature"); arrow content is
|
|
144
|
+
* pure label text.
|
|
147
145
|
*/
|
|
148
146
|
export function parseInArrowLabel(
|
|
149
147
|
rawLabel: string,
|
|
@@ -162,29 +160,6 @@ export function parseInArrowLabel(
|
|
|
162
160
|
return { label: trimmed, diagnostics };
|
|
163
161
|
}
|
|
164
162
|
|
|
165
|
-
// ============================================================
|
|
166
|
-
// matchColorParens — shared TD-11 helper for flowchart and state
|
|
167
|
-
// ============================================================
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Test whether a string matches the TD-11 color-parens form `(colorName)`
|
|
171
|
-
* where `colorName` is one of the 11 recognized palette color names from
|
|
172
|
-
* `src/colors.ts:RECOGNIZED_COLOR_NAMES`. Returns the lowercase color name
|
|
173
|
-
* on a match, or `null` on fall-through (whole string becomes a label).
|
|
174
|
-
*
|
|
175
|
-
* Used by flowchart and state parsers to keep the color-parens recognition
|
|
176
|
-
* rule in one place — do NOT re-implement the regex in chart parsers.
|
|
177
|
-
*/
|
|
178
|
-
export function matchColorParens(content: string): string | null {
|
|
179
|
-
const m = content.match(/^\(([A-Za-z]+)\)$/);
|
|
180
|
-
if (!m) return null;
|
|
181
|
-
const candidate = m[1].toLowerCase();
|
|
182
|
-
if ((RECOGNIZED_COLOR_NAMES as readonly string[]).includes(candidate)) {
|
|
183
|
-
return candidate;
|
|
184
|
-
}
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
163
|
// Forward (call) patterns — participant names may contain spaces, so use non-greedy (.+?)
|
|
189
164
|
const SYNC_LABELED_RE = /^(.+?)\s*-(.+)->\s*(.+)$/;
|
|
190
165
|
const ASYNC_LABELED_RE = /^(.+?)\s*~(.+)~>\s*(.+)$/;
|
|
@@ -32,7 +32,7 @@ export const ALIAS_TOKEN_RE = /^[A-Za-z][A-Za-z0-9_]{0,11}$/;
|
|
|
32
32
|
* DGMO grammar keywords with all chart-type tokens (per F5).
|
|
33
33
|
*
|
|
34
34
|
* Articles (`a`, `an`, `the`) are intentionally NOT reserved — the
|
|
35
|
-
* spec's worked examples use `Alice is
|
|
35
|
+
* spec's worked examples use `Alice is an actor as a`, where `a`
|
|
36
36
|
* is a perfectly fine single-letter alias. Reserving English
|
|
37
37
|
* articles would break the canonical example pattern.
|
|
38
38
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Inline Markdown — shared parsing + SVG rendering for text fields
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
|
-
import * as d3Selection from 'd3-selection';
|
|
5
|
+
import type * as d3Selection from 'd3-selection';
|
|
6
6
|
import type { PaletteColors } from '../palettes';
|
|
7
7
|
import { safeHref } from './safe-href';
|
|
8
8
|
|
package/src/utils/legend-d3.ts
CHANGED
|
@@ -46,7 +46,14 @@ export function renderLegendD3(
|
|
|
46
46
|
let currentState = { ...state };
|
|
47
47
|
let currentLayout: LegendLayout;
|
|
48
48
|
|
|
49
|
-
const legendG = container
|
|
49
|
+
const legendG = container
|
|
50
|
+
.append('g')
|
|
51
|
+
.attr('class', 'dgmo-legend')
|
|
52
|
+
.attr('data-legend-title-relation', config.position.titleRelation)
|
|
53
|
+
.attr(
|
|
54
|
+
'data-legend-capsule-addon-width',
|
|
55
|
+
String(config.capsulePillAddonWidth ?? 0)
|
|
56
|
+
);
|
|
50
57
|
|
|
51
58
|
function render() {
|
|
52
59
|
currentLayout = computeLegendLayout(config, currentState, width);
|
|
@@ -270,11 +277,10 @@ function renderPill(
|
|
|
270
277
|
groupBg: string,
|
|
271
278
|
callbacks?: LegendCallbacks
|
|
272
279
|
): void {
|
|
273
|
-
// Collapsed tag-group pills
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
// pills
|
|
277
|
-
// `data-export-ignore` separately.
|
|
280
|
+
// Collapsed tag-group pills are hidden in export mode
|
|
281
|
+
// (`LegendConfig.mode === 'export'`) — the layout engine filters them
|
|
282
|
+
// in `computeLegendLayout`. See
|
|
283
|
+
// tech-spec-hide-inactive-tag-pills-in-exports.md.
|
|
278
284
|
const g = parent
|
|
279
285
|
.append('g')
|
|
280
286
|
.attr('transform', `translate(${pill.x},${pill.y})`)
|