@diagrammo/dgmo 0.8.9 → 0.8.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -0
- package/dist/cli.cjs +245 -672
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +2 -3
- package/dist/editor.d.ts +2 -3
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +1623 -800
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +153 -1
- package/dist/index.d.ts +153 -1
- package/dist/index.js +1619 -802
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +28 -2
- package/gallery/fixtures/sitemap-full.dgmo +1 -0
- package/package.json +14 -17
- package/src/boxes-and-lines/layout.ts +48 -8
- package/src/boxes-and-lines/parser.ts +59 -13
- package/src/boxes-and-lines/renderer.ts +34 -138
- package/src/c4/layout.ts +31 -10
- package/src/c4/renderer.ts +25 -138
- package/src/class/renderer.ts +185 -186
- package/src/d3.ts +194 -222
- package/src/echarts.ts +56 -57
- package/src/editor/index.ts +1 -2
- package/src/er/renderer.ts +52 -245
- package/src/gantt/renderer.ts +140 -182
- package/src/gantt/resolver.ts +19 -14
- package/src/index.ts +23 -1
- package/src/infra/renderer.ts +91 -244
- package/src/kanban/renderer.ts +29 -133
- package/src/label-layout.ts +286 -0
- package/src/org/renderer.ts +103 -170
- package/src/render.ts +39 -9
- package/src/sequence/parser.ts +4 -0
- package/src/sequence/renderer.ts +47 -154
- package/src/sitemap/layout.ts +180 -38
- package/src/sitemap/parser.ts +64 -23
- package/src/sitemap/renderer.ts +73 -161
- package/src/utils/arrows.ts +1 -1
- package/src/utils/legend-constants.ts +6 -0
- package/src/utils/legend-d3.ts +400 -0
- package/src/utils/legend-layout.ts +491 -0
- package/src/utils/legend-svg.ts +28 -2
- package/src/utils/legend-types.ts +166 -0
- package/src/utils/parsing.ts +1 -1
- package/src/utils/tag-groups.ts +1 -1
package/src/sequence/parser.ts
CHANGED
|
@@ -91,6 +91,7 @@ export interface SequenceMessage {
|
|
|
91
91
|
export interface ElseIfBranch {
|
|
92
92
|
label: string;
|
|
93
93
|
children: SequenceElement[];
|
|
94
|
+
lineNumber: number;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
export interface SequenceBlock {
|
|
@@ -100,6 +101,7 @@ export interface SequenceBlock {
|
|
|
100
101
|
children: SequenceElement[];
|
|
101
102
|
elseChildren: SequenceElement[];
|
|
102
103
|
elseIfBranches?: ElseIfBranch[];
|
|
104
|
+
elseLineNumber?: number;
|
|
103
105
|
lineNumber: number;
|
|
104
106
|
}
|
|
105
107
|
|
|
@@ -1114,6 +1116,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1114
1116
|
const branch: ElseIfBranch = {
|
|
1115
1117
|
label: elseIfMatch[1].trim(),
|
|
1116
1118
|
children: [],
|
|
1119
|
+
lineNumber,
|
|
1117
1120
|
};
|
|
1118
1121
|
if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
|
|
1119
1122
|
top.block.elseIfBranches.push(branch);
|
|
@@ -1141,6 +1144,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1141
1144
|
if (top.block.type === 'if') {
|
|
1142
1145
|
top.inElse = true;
|
|
1143
1146
|
top.activeElseIfBranch = undefined;
|
|
1147
|
+
top.block.elseLineNumber = lineNumber;
|
|
1144
1148
|
}
|
|
1145
1149
|
}
|
|
1146
1150
|
continue;
|
package/src/sequence/renderer.ts
CHANGED
|
@@ -24,18 +24,9 @@ import type {
|
|
|
24
24
|
import { isSequenceBlock, isSequenceSection, isSequenceNote } from './parser';
|
|
25
25
|
import { resolveSequenceTags } from './tag-resolution';
|
|
26
26
|
import type { ResolvedTagMap } from './tag-resolution';
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
LEGEND_PILL_FONT_SIZE,
|
|
31
|
-
LEGEND_CAPSULE_PAD,
|
|
32
|
-
LEGEND_DOT_R,
|
|
33
|
-
LEGEND_ENTRY_FONT_SIZE,
|
|
34
|
-
LEGEND_ENTRY_DOT_GAP,
|
|
35
|
-
LEGEND_ENTRY_TRAIL,
|
|
36
|
-
LEGEND_GROUP_GAP,
|
|
37
|
-
measureLegendText,
|
|
38
|
-
} from '../utils/legend-constants';
|
|
27
|
+
import { LEGEND_HEIGHT } from '../utils/legend-constants';
|
|
28
|
+
import { renderLegendD3 } from '../utils/legend-d3';
|
|
29
|
+
import type { LegendConfig, LegendState } from '../utils/legend-types';
|
|
39
30
|
import { TITLE_FONT_SIZE, TITLE_FONT_WEIGHT } from '../utils/title-constants';
|
|
40
31
|
|
|
41
32
|
// ============================================================
|
|
@@ -1598,146 +1589,35 @@ export function renderSequenceDiagram(
|
|
|
1598
1589
|
// Render legend pills for tag groups
|
|
1599
1590
|
if (parsed.tagGroups.length > 0) {
|
|
1600
1591
|
const legendY = TOP_MARGIN + titleOffset;
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
totalWidth: number;
|
|
1611
|
-
entries: Array<{ value: string; color: string }>;
|
|
1612
|
-
}> = [];
|
|
1613
|
-
for (const tg of parsed.tagGroups) {
|
|
1614
|
-
if (tg.entries.length === 0) continue;
|
|
1615
|
-
const isActive =
|
|
1616
|
-
!!activeTagGroup &&
|
|
1617
|
-
tg.name.toLowerCase() === activeTagGroup.toLowerCase();
|
|
1618
|
-
const pillWidth =
|
|
1619
|
-
measureLegendText(tg.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
|
|
1620
|
-
const entries = tg.entries.map((e) => ({
|
|
1621
|
-
value: e.value,
|
|
1622
|
-
color: resolveColor(e.color) ?? e.color,
|
|
1592
|
+
// Resolve tag colors for legend entries
|
|
1593
|
+
const resolvedGroups = parsed.tagGroups
|
|
1594
|
+
.filter((tg) => tg.entries.length > 0)
|
|
1595
|
+
.map((tg) => ({
|
|
1596
|
+
name: tg.name,
|
|
1597
|
+
entries: tg.entries.map((e) => ({
|
|
1598
|
+
value: e.value,
|
|
1599
|
+
color: resolveColor(e.color) ?? e.color,
|
|
1600
|
+
})),
|
|
1623
1601
|
}));
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
let legendX = (svgWidth - totalLegendWidth) / 2;
|
|
1644
|
-
|
|
1645
|
-
const legendContainer = svg.append('g').attr('class', 'sequence-legend');
|
|
1646
|
-
if (activeTagGroup) {
|
|
1647
|
-
legendContainer.attr('data-legend-active', activeTagGroup.toLowerCase());
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
for (const item of legendItems) {
|
|
1651
|
-
const gEl = legendContainer
|
|
1652
|
-
.append('g')
|
|
1653
|
-
.attr('transform', `translate(${legendX}, ${legendY})`)
|
|
1654
|
-
.attr('class', 'sequence-legend-group')
|
|
1655
|
-
.attr('data-legend-group', item.group.name.toLowerCase())
|
|
1656
|
-
.style('cursor', 'pointer');
|
|
1657
|
-
|
|
1658
|
-
// Outer capsule background (active only)
|
|
1659
|
-
if (item.isActive) {
|
|
1660
|
-
gEl
|
|
1661
|
-
.append('rect')
|
|
1662
|
-
.attr('width', item.totalWidth)
|
|
1663
|
-
.attr('height', LEGEND_HEIGHT)
|
|
1664
|
-
.attr('rx', LEGEND_HEIGHT / 2)
|
|
1665
|
-
.attr('fill', groupBg);
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
const pillXOff = item.isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
1669
|
-
const pillYOff = LEGEND_CAPSULE_PAD;
|
|
1670
|
-
const pillH = LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2;
|
|
1671
|
-
|
|
1672
|
-
// Pill background
|
|
1673
|
-
gEl
|
|
1674
|
-
.append('rect')
|
|
1675
|
-
.attr('x', pillXOff)
|
|
1676
|
-
.attr('y', pillYOff)
|
|
1677
|
-
.attr('width', item.pillWidth)
|
|
1678
|
-
.attr('height', pillH)
|
|
1679
|
-
.attr('rx', pillH / 2)
|
|
1680
|
-
.attr('fill', item.isActive ? palette.bg : groupBg);
|
|
1681
|
-
|
|
1682
|
-
// Active pill border
|
|
1683
|
-
if (item.isActive) {
|
|
1684
|
-
gEl
|
|
1685
|
-
.append('rect')
|
|
1686
|
-
.attr('x', pillXOff)
|
|
1687
|
-
.attr('y', pillYOff)
|
|
1688
|
-
.attr('width', item.pillWidth)
|
|
1689
|
-
.attr('height', pillH)
|
|
1690
|
-
.attr('rx', pillH / 2)
|
|
1691
|
-
.attr('fill', 'none')
|
|
1692
|
-
.attr('stroke', mix(palette.textMuted, palette.bg, 50))
|
|
1693
|
-
.attr('stroke-width', 0.75);
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
// Pill text
|
|
1697
|
-
gEl
|
|
1698
|
-
.append('text')
|
|
1699
|
-
.attr('x', pillXOff + item.pillWidth / 2)
|
|
1700
|
-
.attr('y', LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2)
|
|
1701
|
-
.attr('font-size', LEGEND_PILL_FONT_SIZE)
|
|
1702
|
-
.attr('font-weight', '500')
|
|
1703
|
-
.attr('fill', item.isActive ? palette.text : palette.textMuted)
|
|
1704
|
-
.attr('text-anchor', 'middle')
|
|
1705
|
-
.text(item.group.name);
|
|
1706
|
-
|
|
1707
|
-
// Entries inside capsule (active only)
|
|
1708
|
-
if (item.isActive) {
|
|
1709
|
-
let entryX = pillXOff + item.pillWidth + 4;
|
|
1710
|
-
for (const entry of item.entries) {
|
|
1711
|
-
const entryG = gEl
|
|
1712
|
-
.append('g')
|
|
1713
|
-
.attr('data-legend-entry', entry.value.toLowerCase())
|
|
1714
|
-
.style('cursor', 'pointer');
|
|
1715
|
-
|
|
1716
|
-
entryG
|
|
1717
|
-
.append('circle')
|
|
1718
|
-
.attr('cx', entryX + LEGEND_DOT_R)
|
|
1719
|
-
.attr('cy', LEGEND_HEIGHT / 2)
|
|
1720
|
-
.attr('r', LEGEND_DOT_R)
|
|
1721
|
-
.attr('fill', entry.color);
|
|
1722
|
-
|
|
1723
|
-
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
1724
|
-
entryG
|
|
1725
|
-
.append('text')
|
|
1726
|
-
.attr('x', textX)
|
|
1727
|
-
.attr('y', LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
|
|
1728
|
-
.attr('font-size', LEGEND_ENTRY_FONT_SIZE)
|
|
1729
|
-
.attr('fill', palette.textMuted)
|
|
1730
|
-
.text(entry.value);
|
|
1731
|
-
|
|
1732
|
-
entryX =
|
|
1733
|
-
textX +
|
|
1734
|
-
measureLegendText(entry.value, LEGEND_ENTRY_FONT_SIZE) +
|
|
1735
|
-
LEGEND_ENTRY_TRAIL;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
|
-
legendX += item.totalWidth + LEGEND_GROUP_GAP;
|
|
1740
|
-
}
|
|
1602
|
+
const legendConfig: LegendConfig = {
|
|
1603
|
+
groups: resolvedGroups,
|
|
1604
|
+
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
1605
|
+
mode: 'fixed',
|
|
1606
|
+
};
|
|
1607
|
+
const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
|
|
1608
|
+
const legendG = svg
|
|
1609
|
+
.append('g')
|
|
1610
|
+
.attr('class', 'sequence-legend')
|
|
1611
|
+
.attr('transform', `translate(0,${legendY})`);
|
|
1612
|
+
renderLegendD3(
|
|
1613
|
+
legendG,
|
|
1614
|
+
legendConfig,
|
|
1615
|
+
legendState,
|
|
1616
|
+
palette,
|
|
1617
|
+
isDark,
|
|
1618
|
+
undefined,
|
|
1619
|
+
svgWidth
|
|
1620
|
+
);
|
|
1741
1621
|
}
|
|
1742
1622
|
|
|
1743
1623
|
// Render group boxes (behind participant shapes)
|
|
@@ -1882,6 +1762,7 @@ export function renderSequenceDiagram(
|
|
|
1882
1762
|
y1: number;
|
|
1883
1763
|
x2: number;
|
|
1884
1764
|
y2: number;
|
|
1765
|
+
blockLine?: number;
|
|
1885
1766
|
}> = [];
|
|
1886
1767
|
|
|
1887
1768
|
// Recursive block renderer — draws borders/dividers now, defers label text
|
|
@@ -1890,12 +1771,17 @@ export function renderSequenceDiagram(
|
|
|
1890
1771
|
if (!isSequenceBlock(el)) continue;
|
|
1891
1772
|
|
|
1892
1773
|
const ifIndices = collectMsgIndices(el.children);
|
|
1893
|
-
const elseIfBranchData: {
|
|
1774
|
+
const elseIfBranchData: {
|
|
1775
|
+
label: string;
|
|
1776
|
+
indices: number[];
|
|
1777
|
+
lineNumber: number;
|
|
1778
|
+
}[] = [];
|
|
1894
1779
|
if (el.elseIfBranches) {
|
|
1895
1780
|
for (const branch of el.elseIfBranches) {
|
|
1896
1781
|
elseIfBranchData.push({
|
|
1897
1782
|
label: branch.label,
|
|
1898
1783
|
indices: collectMsgIndices(branch.children),
|
|
1784
|
+
lineNumber: branch.lineNumber,
|
|
1899
1785
|
});
|
|
1900
1786
|
}
|
|
1901
1787
|
}
|
|
@@ -1985,6 +1871,7 @@ export function renderSequenceDiagram(
|
|
|
1985
1871
|
y1: dividerY,
|
|
1986
1872
|
x2: frameX + frameW,
|
|
1987
1873
|
y2: dividerY,
|
|
1874
|
+
blockLine: branchData.lineNumber,
|
|
1988
1875
|
});
|
|
1989
1876
|
deferredLabels.push({
|
|
1990
1877
|
x: frameX + 6,
|
|
@@ -1992,6 +1879,7 @@ export function renderSequenceDiagram(
|
|
|
1992
1879
|
text: `else if ${branchData.label}`,
|
|
1993
1880
|
bold: false,
|
|
1994
1881
|
italic: true,
|
|
1882
|
+
blockLine: branchData.lineNumber,
|
|
1995
1883
|
});
|
|
1996
1884
|
}
|
|
1997
1885
|
}
|
|
@@ -2012,6 +1900,7 @@ export function renderSequenceDiagram(
|
|
|
2012
1900
|
y1: dividerY,
|
|
2013
1901
|
x2: frameX + frameW,
|
|
2014
1902
|
y2: dividerY,
|
|
1903
|
+
blockLine: el.elseLineNumber,
|
|
2015
1904
|
});
|
|
2016
1905
|
deferredLabels.push({
|
|
2017
1906
|
x: frameX + 6,
|
|
@@ -2019,6 +1908,7 @@ export function renderSequenceDiagram(
|
|
|
2019
1908
|
text: 'else',
|
|
2020
1909
|
bold: false,
|
|
2021
1910
|
italic: true,
|
|
1911
|
+
blockLine: el.elseLineNumber,
|
|
2022
1912
|
});
|
|
2023
1913
|
}
|
|
2024
1914
|
}
|
|
@@ -2100,7 +1990,7 @@ export function renderSequenceDiagram(
|
|
|
2100
1990
|
|
|
2101
1991
|
// Render deferred else dividers (on top of activations)
|
|
2102
1992
|
for (const ln of deferredLines) {
|
|
2103
|
-
svg
|
|
1993
|
+
const line = svg
|
|
2104
1994
|
.append('line')
|
|
2105
1995
|
.attr('x1', ln.x1)
|
|
2106
1996
|
.attr('y1', ln.y1)
|
|
@@ -2108,7 +1998,10 @@ export function renderSequenceDiagram(
|
|
|
2108
1998
|
.attr('y2', ln.y2)
|
|
2109
1999
|
.attr('stroke', palette.textMuted)
|
|
2110
2000
|
.attr('stroke-width', 1)
|
|
2111
|
-
.attr('stroke-dasharray', '2 3')
|
|
2001
|
+
.attr('stroke-dasharray', '2 3')
|
|
2002
|
+
.attr('class', 'block-divider');
|
|
2003
|
+
if (ln.blockLine !== undefined)
|
|
2004
|
+
line.attr('data-block-line', String(ln.blockLine));
|
|
2112
2005
|
}
|
|
2113
2006
|
|
|
2114
2007
|
// Render deferred block labels (on top of activations)
|