@diagrammo/dgmo 0.8.10 → 0.8.12
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 +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 +491 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +489 -125
- package/dist/index.js.map +1 -1
- package/gallery/fixtures/er.dgmo +36 -0
- package/gallery/fixtures/kanban.dgmo +27 -0
- package/package.json +14 -17
- package/src/boxes-and-lines/parser.ts +2 -0
- package/src/boxes-and-lines/renderer.ts +13 -5
- package/src/c4/layout.ts +31 -10
- package/src/c4/parser.ts +5 -1
- package/src/completion.ts +17 -2
- package/src/d3.ts +220 -102
- package/src/echarts.ts +57 -58
- package/src/editor/index.ts +1 -2
- package/src/er/parser.ts +5 -1
- package/src/gantt/parser.ts +8 -0
- package/src/gantt/renderer.ts +6 -7
- package/src/gantt/resolver.ts +19 -14
- package/src/gantt/types.ts +1 -0
- package/src/index.ts +2 -0
- package/src/infra/parser.ts +4 -0
- package/src/kanban/parser.ts +4 -1
- package/src/kanban/renderer.ts +11 -8
- package/src/label-layout.ts +286 -0
- package/src/org/parser.ts +3 -0
- package/src/sequence/parser.ts +6 -0
- package/src/sequence/renderer.ts +24 -9
- package/src/sharing.ts +15 -5
- package/src/sitemap/parser.ts +2 -0
- package/src/utils/arrows.ts +1 -1
- package/src/utils/legend-layout.ts +8 -8
- package/src/utils/legend-svg.ts +2 -2
- package/src/utils/legend-types.ts +0 -3
- package/src/utils/parsing.ts +1 -1
- package/src/utils/tag-groups.ts +65 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
er Pirate Fleet
|
|
2
|
+
|
|
3
|
+
ships
|
|
4
|
+
id int pk
|
|
5
|
+
name varchar
|
|
6
|
+
ship_type varchar
|
|
7
|
+
cannons int
|
|
8
|
+
1-aboard-* crew_members
|
|
9
|
+
1-1 captains
|
|
10
|
+
1-carries-* treasure
|
|
11
|
+
|
|
12
|
+
captains
|
|
13
|
+
id int pk
|
|
14
|
+
name varchar
|
|
15
|
+
ship_id int fk
|
|
16
|
+
bounty int
|
|
17
|
+
?-frequents-1 ports
|
|
18
|
+
*-has-1 crew_members
|
|
19
|
+
|
|
20
|
+
crew_members
|
|
21
|
+
id int pk
|
|
22
|
+
name varchar
|
|
23
|
+
ship_id int fk
|
|
24
|
+
role varchar nullable
|
|
25
|
+
|
|
26
|
+
treasure
|
|
27
|
+
id int pk
|
|
28
|
+
name varchar
|
|
29
|
+
value int
|
|
30
|
+
ship_id int fk, nullable
|
|
31
|
+
|
|
32
|
+
ports
|
|
33
|
+
id int pk
|
|
34
|
+
name varchar
|
|
35
|
+
region varchar unique
|
|
36
|
+
1-docks-* ships
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
kanban Plunder Sprint 7
|
|
2
|
+
|
|
3
|
+
tag Priority
|
|
4
|
+
Low(green)
|
|
5
|
+
Urgent(red)
|
|
6
|
+
High(orange)
|
|
7
|
+
|
|
8
|
+
tag Crew c
|
|
9
|
+
Blackbeard(red)
|
|
10
|
+
Anne Bonny(purple)
|
|
11
|
+
Calico Jack(teal)
|
|
12
|
+
|
|
13
|
+
[Awaiting Orders](red)
|
|
14
|
+
Recruit gunners at Tortuga | priority: High, c: Calico Jack
|
|
15
|
+
Chart new trade route | priority: Urgent, c: Anne Bonny
|
|
16
|
+
Scout the Windward Passage
|
|
17
|
+
Avoid Royal Navy patrols
|
|
18
|
+
Resupply rum and powder | priority: Low, c: Calico Jack
|
|
19
|
+
|
|
20
|
+
[Underway](orange) | wip: 2
|
|
21
|
+
Forge letters of marque | priority: High, c: Anne Bonny
|
|
22
|
+
Raid merchant convoy | priority: Urgent, c: Blackbeard
|
|
23
|
+
Three ships spotted off Nassau
|
|
24
|
+
|
|
25
|
+
[Done](green)
|
|
26
|
+
Bribe the harbour master | priority: High, c: Anne Bonny
|
|
27
|
+
Repair hull damage | priority: Low, c: Blackbeard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diagrammo/dgmo",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.12",
|
|
4
4
|
"description": "DGMO diagram markup language — parser, renderer, and color system",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -74,10 +74,10 @@
|
|
|
74
74
|
"prepare": "husky"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@dagrejs/dagre": "^
|
|
77
|
+
"@dagrejs/dagre": "^3.0.0",
|
|
78
78
|
"@resvg/resvg-js": "^2.6.2",
|
|
79
79
|
"d3-array": "^3.2.4",
|
|
80
|
-
"d3-cloud": "^1.2.
|
|
80
|
+
"d3-cloud": "^1.2.9",
|
|
81
81
|
"d3-hierarchy": "^3.1.2",
|
|
82
82
|
"d3-scale": "^4.0.2",
|
|
83
83
|
"d3-selection": "^3.0.0",
|
|
@@ -85,32 +85,30 @@
|
|
|
85
85
|
"echarts": "^6.0.0",
|
|
86
86
|
"lz-string": "^1.5.0"
|
|
87
87
|
},
|
|
88
|
-
"optionalDependencies": {
|
|
89
|
-
"jsdom": "^28.1.0"
|
|
90
|
-
},
|
|
91
88
|
"devDependencies": {
|
|
92
89
|
"@codemirror/language": "^6.12.3",
|
|
93
|
-
"@codemirror/state": "^6.6.0",
|
|
94
90
|
"@eslint/js": "^10.0.1",
|
|
95
91
|
"@lezer/generator": "^1.8.0",
|
|
96
|
-
"@types/d3-array": "^3.2.
|
|
92
|
+
"@types/d3-array": "^3.2.2",
|
|
97
93
|
"@types/d3-cloud": "^1.2.9",
|
|
98
94
|
"@types/d3-hierarchy": "^3.1.7",
|
|
99
|
-
"@types/d3-scale": "^4.0.
|
|
95
|
+
"@types/d3-scale": "^4.0.9",
|
|
100
96
|
"@types/d3-selection": "^3.0.11",
|
|
101
|
-
"@types/d3-shape": "^3.1.
|
|
102
|
-
"@types/jsdom": "^28.0.
|
|
97
|
+
"@types/d3-shape": "^3.1.8",
|
|
98
|
+
"@types/jsdom": "^28.0.1",
|
|
103
99
|
"cspell": "^9.7.0",
|
|
104
|
-
"
|
|
100
|
+
"esbuild": "^0.28.0",
|
|
101
|
+
"eslint": "^10.2.0",
|
|
105
102
|
"husky": "^9.1.7",
|
|
106
103
|
"jscpd": "^4.0.8",
|
|
107
|
-
"
|
|
104
|
+
"jsdom": "^29.0.1",
|
|
105
|
+
"knip": "^6.3.0",
|
|
108
106
|
"lint-staged": "^16.4.0",
|
|
109
107
|
"prettier": "^3.8.1",
|
|
110
108
|
"tsup": "^8.5.1",
|
|
111
|
-
"typescript": "^
|
|
112
|
-
"typescript-eslint": "^8.
|
|
113
|
-
"vitest": "^4.
|
|
109
|
+
"typescript": "^6.0.2",
|
|
110
|
+
"typescript-eslint": "^8.58.0",
|
|
111
|
+
"vitest": "^4.1.2"
|
|
114
112
|
},
|
|
115
113
|
"lint-staged": {
|
|
116
114
|
"*.ts": [
|
|
@@ -120,7 +118,6 @@
|
|
|
120
118
|
},
|
|
121
119
|
"peerDependencies": {
|
|
122
120
|
"@codemirror/language": "^6.12.3",
|
|
123
|
-
"@codemirror/state": "^6.6.0",
|
|
124
121
|
"@lezer/common": "^1.5.1",
|
|
125
122
|
"@lezer/highlight": "^1.2.3",
|
|
126
123
|
"@lezer/lr": "^1.4.8"
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
matchTagBlockHeading,
|
|
10
10
|
injectDefaultTagMetadata,
|
|
11
11
|
validateTagValues,
|
|
12
|
+
validateTagGroupNames,
|
|
12
13
|
stripDefaultModifier,
|
|
13
14
|
} from '../utils/tag-groups';
|
|
14
15
|
import type { TagGroup } from '../utils/tag-groups';
|
|
@@ -531,6 +532,7 @@ export function parseBoxesAndLines(content: string): ParsedBoxesAndLines {
|
|
|
531
532
|
if (result.tagGroups.length > 0) {
|
|
532
533
|
injectDefaultTagMetadata(result.nodes, result.tagGroups);
|
|
533
534
|
validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
|
|
535
|
+
validateTagGroupNames(result.tagGroups, pushWarning);
|
|
534
536
|
}
|
|
535
537
|
|
|
536
538
|
return result;
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
TITLE_Y,
|
|
15
15
|
} from '../utils/title-constants';
|
|
16
16
|
import { contrastText, mix } from '../palettes/color-utils';
|
|
17
|
-
import { resolveTagColor } from '../utils/tag-groups';
|
|
17
|
+
import { resolveTagColor, resolveActiveTagGroup } from '../utils/tag-groups';
|
|
18
18
|
import type { TagGroup } from '../utils/tag-groups';
|
|
19
19
|
import type { PaletteColors } from '../palettes';
|
|
20
20
|
import type { ParsedBoxesAndLines, BLNode } from './types';
|
|
@@ -292,7 +292,7 @@ function resolveEdgeLabelOverlaps(
|
|
|
292
292
|
|
|
293
293
|
// ── Main render function ───────────────────────────────────
|
|
294
294
|
|
|
295
|
-
|
|
295
|
+
interface BLRenderOptions {
|
|
296
296
|
onClickItem?: (lineNumber: number) => void;
|
|
297
297
|
exportDims?: { width?: number; height?: number };
|
|
298
298
|
activeTagGroup?: string | null;
|
|
@@ -315,8 +315,12 @@ export function renderBoxesAndLines(
|
|
|
315
315
|
const height = exportDims?.height ?? container.clientHeight;
|
|
316
316
|
if (width <= 0 || height <= 0) return;
|
|
317
317
|
|
|
318
|
-
// Determine active tag group
|
|
319
|
-
const activeGroup =
|
|
318
|
+
// Determine active tag group — shared utility handles priority chain
|
|
319
|
+
const activeGroup = resolveActiveTagGroup(
|
|
320
|
+
parsed.tagGroups,
|
|
321
|
+
parsed.options['active-tag'],
|
|
322
|
+
activeTagGroup
|
|
323
|
+
);
|
|
320
324
|
|
|
321
325
|
// Build hidden set
|
|
322
326
|
const hidden = hiddenTagValues ?? parsed.initialHiddenTagValues;
|
|
@@ -736,9 +740,13 @@ export function renderBoxesAndLinesForExport(
|
|
|
736
740
|
layout: BLLayoutResult,
|
|
737
741
|
palette: PaletteColors,
|
|
738
742
|
isDark: boolean,
|
|
739
|
-
options?: {
|
|
743
|
+
options?: {
|
|
744
|
+
exportDims?: { width: number; height: number };
|
|
745
|
+
activeTagGroup?: string | null;
|
|
746
|
+
}
|
|
740
747
|
): void {
|
|
741
748
|
renderBoxesAndLines(container, parsed, layout, palette, isDark, {
|
|
742
749
|
exportDims: options?.exportDims,
|
|
750
|
+
activeTagGroup: options?.activeTagGroup,
|
|
743
751
|
});
|
|
744
752
|
}
|
package/src/c4/layout.ts
CHANGED
|
@@ -18,6 +18,27 @@ import {
|
|
|
18
18
|
measureLegendText,
|
|
19
19
|
} from '../utils/legend-constants';
|
|
20
20
|
|
|
21
|
+
/** dagre node label shape after layout(). */
|
|
22
|
+
interface DagreNodeLabel {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** dagre edge label shape after layout(). */
|
|
31
|
+
interface DagreEdgeLabel {
|
|
32
|
+
points: { x: number; y: number }[];
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
37
|
+
const gNode = (g: any, name: string): DagreNodeLabel => g.node(name);
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
const gEdge = (g: any, v: string, w: string): DagreEdgeLabel | undefined =>
|
|
40
|
+
g.edge(v, w);
|
|
41
|
+
|
|
21
42
|
// ============================================================
|
|
22
43
|
// Types
|
|
23
44
|
// ============================================================
|
|
@@ -213,7 +234,7 @@ function computeEdgePenalty(
|
|
|
213
234
|
* closer to their neighbors, producing cleaner visual layouts.
|
|
214
235
|
*/
|
|
215
236
|
function reduceCrossings(
|
|
216
|
-
g: dagre.graphlib.Graph
|
|
237
|
+
g: InstanceType<typeof dagre.graphlib.Graph>,
|
|
217
238
|
edgeList: { source: string; target: string }[],
|
|
218
239
|
nodeGroupMap?: Map<string, string>
|
|
219
240
|
): void {
|
|
@@ -229,7 +250,7 @@ function reduceCrossings(
|
|
|
229
250
|
// Build geometry map for edge-node collision scoring
|
|
230
251
|
const nodeGeometry = new Map<string, NodeGeometry>();
|
|
231
252
|
for (const name of g.nodes()) {
|
|
232
|
-
const pos = g
|
|
253
|
+
const pos = gNode(g, name);
|
|
233
254
|
if (pos)
|
|
234
255
|
nodeGeometry.set(name, {
|
|
235
256
|
y: pos.y,
|
|
@@ -241,7 +262,7 @@ function reduceCrossings(
|
|
|
241
262
|
// Group nodes by rank
|
|
242
263
|
const rankMap = new Map<number, string[]>();
|
|
243
264
|
for (const name of g.nodes()) {
|
|
244
|
-
const pos = g
|
|
265
|
+
const pos = gNode(g, name);
|
|
245
266
|
if (!pos) continue;
|
|
246
267
|
const rankY = Math.round(pos.y);
|
|
247
268
|
if (!rankMap.has(rankY)) rankMap.set(rankY, []);
|
|
@@ -250,7 +271,7 @@ function reduceCrossings(
|
|
|
250
271
|
|
|
251
272
|
// Sort each rank by current x position
|
|
252
273
|
for (const [, rankNodes] of rankMap) {
|
|
253
|
-
rankNodes.sort((a, b) => g
|
|
274
|
+
rankNodes.sort((a, b) => gNode(g, a).x - gNode(g, b).x);
|
|
254
275
|
}
|
|
255
276
|
|
|
256
277
|
let anyMoved = false;
|
|
@@ -285,13 +306,13 @@ function reduceCrossings(
|
|
|
285
306
|
|
|
286
307
|
// Collect the x-slots for this partition (sorted)
|
|
287
308
|
const xSlots = partition
|
|
288
|
-
.map((name) => g
|
|
309
|
+
.map((name) => gNode(g, name).x)
|
|
289
310
|
.sort((a, b) => a - b);
|
|
290
311
|
|
|
291
312
|
// Build position map snapshot
|
|
292
313
|
const basePositions = new Map<string, number>();
|
|
293
314
|
for (const name of g.nodes()) {
|
|
294
|
-
const pos = g
|
|
315
|
+
const pos = gNode(g, name);
|
|
295
316
|
if (pos) basePositions.set(name, pos.x);
|
|
296
317
|
}
|
|
297
318
|
|
|
@@ -379,7 +400,7 @@ function reduceCrossings(
|
|
|
379
400
|
// Apply best permutation if it differs from current
|
|
380
401
|
if (bestPerm.some((name, i) => name !== partition[i])) {
|
|
381
402
|
for (let i = 0; i < bestPerm.length; i++) {
|
|
382
|
-
g
|
|
403
|
+
gNode(g, bestPerm[i]!).x = xSlots[i]!;
|
|
383
404
|
// Update in the original rankNodes too
|
|
384
405
|
const rankIdx = rankNodes.indexOf(partition[i]!);
|
|
385
406
|
if (rankIdx >= 0) rankNodes[rankIdx] = bestPerm[i]!;
|
|
@@ -392,10 +413,10 @@ function reduceCrossings(
|
|
|
392
413
|
// Recompute edge waypoints if any positions changed
|
|
393
414
|
if (anyMoved) {
|
|
394
415
|
for (const edge of edgeList) {
|
|
395
|
-
const edgeData = g
|
|
416
|
+
const edgeData = gEdge(g, edge.source, edge.target);
|
|
396
417
|
if (!edgeData) continue;
|
|
397
|
-
const srcPos = g
|
|
398
|
-
const tgtPos = g
|
|
418
|
+
const srcPos = gNode(g, edge.source);
|
|
419
|
+
const tgtPos = gNode(g, edge.target);
|
|
399
420
|
if (!srcPos || !tgtPos) continue;
|
|
400
421
|
|
|
401
422
|
const srcBottom = { x: srcPos.x, y: srcPos.y + srcPos.height / 2 };
|
package/src/c4/parser.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { TagGroup } from '../utils/tag-groups';
|
|
|
8
8
|
import {
|
|
9
9
|
matchTagBlockHeading,
|
|
10
10
|
stripDefaultModifier,
|
|
11
|
+
validateTagGroupNames,
|
|
11
12
|
} from '../utils/tag-groups';
|
|
12
13
|
import { inferParticipantType } from '../sequence/participant-inference';
|
|
13
14
|
import {
|
|
@@ -84,7 +85,7 @@ const VALID_SHAPES = new Set<string>([
|
|
|
84
85
|
]);
|
|
85
86
|
|
|
86
87
|
/** Known top-level option keys for C4 diagrams. */
|
|
87
|
-
const KNOWN_C4_OPTIONS = new Set<string>(['layout']);
|
|
88
|
+
const KNOWN_C4_OPTIONS = new Set<string>(['layout', 'active-tag']);
|
|
88
89
|
|
|
89
90
|
/** Known C4 boolean options (bare keyword = on). */
|
|
90
91
|
const KNOWN_C4_BOOLEANS = new Set<string>(['direction-tb']);
|
|
@@ -829,6 +830,9 @@ export function parseC4(content: string, palette?: PaletteColors): ParsedC4 {
|
|
|
829
830
|
// ── Post-parse validation ───────────────────────────────
|
|
830
831
|
validateRelationshipTargets(result, knownNames, pushError);
|
|
831
832
|
validateDeploymentRefs(result, knownNames, pushError);
|
|
833
|
+
validateTagGroupNames(result.tagGroups, (line, msg) =>
|
|
834
|
+
pushError(line, msg, 'warning')
|
|
835
|
+
);
|
|
832
836
|
|
|
833
837
|
return result;
|
|
834
838
|
}
|
package/src/completion.ts
CHANGED
|
@@ -258,21 +258,33 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
258
258
|
},
|
|
259
259
|
}),
|
|
260
260
|
],
|
|
261
|
-
[
|
|
261
|
+
[
|
|
262
|
+
'er',
|
|
263
|
+
withGlobals({
|
|
264
|
+
'active-tag': { description: 'Active tag group name' },
|
|
265
|
+
}),
|
|
266
|
+
],
|
|
262
267
|
[
|
|
263
268
|
'org',
|
|
264
269
|
withGlobals({
|
|
265
270
|
'sub-node-label': { description: 'Label for sub-nodes' },
|
|
266
271
|
'show-sub-node-count': { description: 'Show sub-node counts' },
|
|
272
|
+
'active-tag': { description: 'Active tag group name' },
|
|
267
273
|
}),
|
|
268
274
|
],
|
|
269
275
|
[
|
|
270
276
|
'kanban',
|
|
271
277
|
withGlobals({
|
|
272
278
|
'no-auto-color': { description: 'Disable automatic card coloring' },
|
|
279
|
+
'active-tag': { description: 'Active tag group name' },
|
|
280
|
+
}),
|
|
281
|
+
],
|
|
282
|
+
[
|
|
283
|
+
'c4',
|
|
284
|
+
withGlobals({
|
|
285
|
+
'active-tag': { description: 'Active tag group name' },
|
|
273
286
|
}),
|
|
274
287
|
],
|
|
275
|
-
['c4', withGlobals()],
|
|
276
288
|
[
|
|
277
289
|
'state',
|
|
278
290
|
withGlobals({
|
|
@@ -284,6 +296,7 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
284
296
|
'sitemap',
|
|
285
297
|
withGlobals({
|
|
286
298
|
'direction-tb': { description: 'Switch to top-to-bottom layout' },
|
|
299
|
+
'active-tag': { description: 'Active tag group name' },
|
|
287
300
|
}),
|
|
288
301
|
],
|
|
289
302
|
[
|
|
@@ -298,6 +311,7 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
298
311
|
'slo-availability': { description: 'SLO availability target (0-1)' },
|
|
299
312
|
'slo-p90-latency-ms': { description: 'SLO p90 latency target in ms' },
|
|
300
313
|
'slo-warning-margin': { description: 'SLO warning margin percentage' },
|
|
314
|
+
'active-tag': { description: 'Active tag group name' },
|
|
301
315
|
}),
|
|
302
316
|
],
|
|
303
317
|
[
|
|
@@ -310,6 +324,7 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
310
324
|
sort: { description: 'Sort order', values: ['time', 'group', 'tag'] },
|
|
311
325
|
'critical-path': { description: 'Show critical path' },
|
|
312
326
|
dependencies: { description: 'Show dependencies' },
|
|
327
|
+
'active-tag': { description: 'Active tag group name' },
|
|
313
328
|
}),
|
|
314
329
|
],
|
|
315
330
|
[
|