@diagrammo/dgmo 0.8.11 → 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 +169 -169
- package/dist/index.cjs +185 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +185 -48
- package/dist/index.js.map +1 -1
- package/gallery/fixtures/er.dgmo +36 -0
- package/gallery/fixtures/kanban.dgmo +27 -0
- package/package.json +1 -1
- package/src/boxes-and-lines/parser.ts +2 -0
- package/src/boxes-and-lines/renderer.ts +12 -4
- package/src/c4/parser.ts +5 -1
- package/src/completion.ts +17 -2
- package/src/d3.ts +140 -71
- package/src/echarts.ts +1 -1
- package/src/er/parser.ts +5 -1
- package/src/gantt/parser.ts +8 -0
- package/src/gantt/renderer.ts +6 -7
- package/src/gantt/types.ts +1 -0
- package/src/infra/parser.ts +4 -0
- package/src/kanban/parser.ts +4 -1
- package/src/kanban/renderer.ts +1 -1
- package/src/org/parser.ts +3 -0
- package/src/sequence/parser.ts +2 -0
- package/src/sequence/renderer.ts +8 -6
- package/src/sharing.ts +15 -5
- package/src/sitemap/parser.ts +2 -0
- package/src/utils/legend-layout.ts +7 -3
- package/src/utils/tag-groups.ts +64 -0
package/src/kanban/renderer.ts
CHANGED
|
@@ -257,7 +257,7 @@ export function renderKanban(
|
|
|
257
257
|
const legendY = DIAGRAM_PADDING + (TITLE_FONT_SIZE - LEGEND_HEIGHT) / 2;
|
|
258
258
|
const legendConfig: LegendConfig = {
|
|
259
259
|
groups: parsed.tagGroups,
|
|
260
|
-
position: { placement: 'top-center', titleRelation: '
|
|
260
|
+
position: { placement: 'top-center', titleRelation: 'inline-with-title' },
|
|
261
261
|
mode: exportDims ? 'inline' : 'fixed',
|
|
262
262
|
};
|
|
263
263
|
const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
|
package/src/org/parser.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isTagBlockHeading,
|
|
7
7
|
matchTagBlockHeading,
|
|
8
8
|
validateTagValues,
|
|
9
|
+
validateTagGroupNames,
|
|
9
10
|
stripDefaultModifier,
|
|
10
11
|
} from '../utils/tag-groups';
|
|
11
12
|
import {
|
|
@@ -54,6 +55,7 @@ const KNOWN_OPTIONS = new Set([
|
|
|
54
55
|
'sub-node-label',
|
|
55
56
|
'hide',
|
|
56
57
|
'show-sub-node-count',
|
|
58
|
+
'active-tag',
|
|
57
59
|
]);
|
|
58
60
|
/** Known org chart boolean options (bare keyword = on). */
|
|
59
61
|
const KNOWN_BOOLEANS = new Set(['show-sub-node-count', 'direction-tb']);
|
|
@@ -344,6 +346,7 @@ export function parseOrg(content: string, palette?: PaletteColors): ParsedOrg {
|
|
|
344
346
|
collectAll(result.roots);
|
|
345
347
|
|
|
346
348
|
validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
|
|
349
|
+
validateTagGroupNames(result.tagGroups, pushWarning);
|
|
347
350
|
}
|
|
348
351
|
|
|
349
352
|
if (
|
package/src/sequence/parser.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { TagGroup } from '../utils/tag-groups';
|
|
|
18
18
|
import {
|
|
19
19
|
matchTagBlockHeading,
|
|
20
20
|
validateTagValues,
|
|
21
|
+
validateTagGroupNames,
|
|
21
22
|
stripDefaultModifier,
|
|
22
23
|
} from '../utils/tag-groups';
|
|
23
24
|
|
|
@@ -1280,6 +1281,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1280
1281
|
entities.push({ metadata: g.metadata, lineNumber: g.lineNumber });
|
|
1281
1282
|
}
|
|
1282
1283
|
validateTagValues(entities, result.tagGroups, pushWarning, suggest);
|
|
1284
|
+
validateTagGroupNames(result.tagGroups, pushWarning);
|
|
1283
1285
|
}
|
|
1284
1286
|
|
|
1285
1287
|
return result;
|
package/src/sequence/renderer.ts
CHANGED
|
@@ -24,6 +24,7 @@ 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 { resolveActiveTagGroup } from '../utils/tag-groups';
|
|
27
28
|
import { LEGEND_HEIGHT } from '../utils/legend-constants';
|
|
28
29
|
import { renderLegendD3 } from '../utils/legend-d3';
|
|
29
30
|
import type { LegendConfig, LegendState } from '../utils/legend-types';
|
|
@@ -918,13 +919,14 @@ export function renderSequenceDiagram(
|
|
|
918
919
|
|
|
919
920
|
const activationsOff = parsedOptions.activations?.toLowerCase() === 'off';
|
|
920
921
|
|
|
921
|
-
// Tag resolution —
|
|
922
|
-
//
|
|
923
|
-
// then fall back to diagram-level `active-tag: Name` option for CLI/export
|
|
922
|
+
// Tag resolution — shared utility handles priority chain:
|
|
923
|
+
// programmatic override → diagram-level active-tag → auto-activate first group
|
|
924
924
|
const activeTagGroup =
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
925
|
+
resolveActiveTagGroup(
|
|
926
|
+
parsed.tagGroups,
|
|
927
|
+
parsedOptions['active-tag'],
|
|
928
|
+
options?.activeTagGroup
|
|
929
|
+
) ?? undefined;
|
|
928
930
|
let tagMap: ResolvedTagMap | undefined;
|
|
929
931
|
const tagValueToColor = new Map<string, string>();
|
|
930
932
|
if (activeTagGroup) {
|
package/src/sharing.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
decompressFromEncodedURIComponent,
|
|
4
4
|
} from 'lz-string';
|
|
5
5
|
|
|
6
|
-
const DEFAULT_BASE_URL = 'https://diagrammo.app
|
|
6
|
+
const DEFAULT_BASE_URL = 'https://online.diagrammo.app';
|
|
7
7
|
const COMPRESSED_SIZE_LIMIT = 8192; // 8 KB
|
|
8
8
|
|
|
9
9
|
export interface DiagramViewState {
|
|
@@ -27,7 +27,12 @@ export interface EncodeDiagramUrlOptions {
|
|
|
27
27
|
|
|
28
28
|
export type EncodeDiagramUrlResult =
|
|
29
29
|
| { url: string; error?: undefined }
|
|
30
|
-
| {
|
|
30
|
+
| {
|
|
31
|
+
url?: undefined;
|
|
32
|
+
error: 'too-large';
|
|
33
|
+
compressedSize: number;
|
|
34
|
+
limit: number;
|
|
35
|
+
};
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Compress a DGMO DSL string into a shareable URL.
|
|
@@ -36,14 +41,18 @@ export type EncodeDiagramUrlResult =
|
|
|
36
41
|
*/
|
|
37
42
|
export function encodeDiagramUrl(
|
|
38
43
|
dsl: string,
|
|
39
|
-
options?: EncodeDiagramUrlOptions
|
|
44
|
+
options?: EncodeDiagramUrlOptions
|
|
40
45
|
): EncodeDiagramUrlResult {
|
|
41
46
|
const baseUrl = options?.baseUrl ?? DEFAULT_BASE_URL;
|
|
42
47
|
const compressed = compressToEncodedURIComponent(dsl);
|
|
43
48
|
const byteSize = new TextEncoder().encode(compressed).byteLength;
|
|
44
49
|
|
|
45
50
|
if (byteSize > COMPRESSED_SIZE_LIMIT) {
|
|
46
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
error: 'too-large',
|
|
53
|
+
compressedSize: byteSize,
|
|
54
|
+
limit: COMPRESSED_SIZE_LIMIT,
|
|
55
|
+
};
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
let hash = `dgmo=${compressed}`;
|
|
@@ -124,7 +133,8 @@ export function decodeDiagramUrl(hash: string): DecodedDiagramUrl {
|
|
|
124
133
|
viewState.collapsedLanes = val.split(',').filter(Boolean);
|
|
125
134
|
}
|
|
126
135
|
if (key === 'pal' && val) viewState.palette = val;
|
|
127
|
-
if (key === 'th' && (val === 'light' || val === 'dark'))
|
|
136
|
+
if (key === 'th' && (val === 'light' || val === 'dark'))
|
|
137
|
+
viewState.theme = val;
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
// Strip 'dgmo=' prefix
|
package/src/sitemap/parser.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
isTagBlockHeading,
|
|
11
11
|
matchTagBlockHeading,
|
|
12
12
|
validateTagValues,
|
|
13
|
+
validateTagGroupNames,
|
|
13
14
|
stripDefaultModifier,
|
|
14
15
|
} from '../utils/tag-groups';
|
|
15
16
|
import {
|
|
@@ -476,6 +477,7 @@ export function parseSitemap(
|
|
|
476
477
|
};
|
|
477
478
|
collectAll(result.roots);
|
|
478
479
|
validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
|
|
480
|
+
validateTagGroupNames(result.tagGroups, pushWarning);
|
|
479
481
|
}
|
|
480
482
|
|
|
481
483
|
if (
|
|
@@ -271,13 +271,15 @@ export function computeLegendLayout(
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
// Position elements in rows
|
|
274
|
+
const alignLeft = config.position.titleRelation === 'inline-with-title';
|
|
274
275
|
const rows = layoutRows(
|
|
275
276
|
activeCapsule,
|
|
276
277
|
pills,
|
|
277
278
|
controlLayouts,
|
|
278
279
|
groupAvailW,
|
|
279
280
|
containerWidth,
|
|
280
|
-
totalControlsW
|
|
281
|
+
totalControlsW,
|
|
282
|
+
alignLeft
|
|
281
283
|
);
|
|
282
284
|
|
|
283
285
|
const height = rows.length * LEGEND_HEIGHT;
|
|
@@ -379,7 +381,8 @@ function layoutRows(
|
|
|
379
381
|
controls: LegendControlLayout[],
|
|
380
382
|
groupAvailW: number,
|
|
381
383
|
containerWidth: number,
|
|
382
|
-
totalControlsW: number
|
|
384
|
+
totalControlsW: number,
|
|
385
|
+
alignLeft = false
|
|
383
386
|
): Array<{
|
|
384
387
|
y: number;
|
|
385
388
|
items: Array<LegendPillLayout | LegendCapsuleLayout | LegendControlLayout>;
|
|
@@ -405,7 +408,8 @@ function layoutRows(
|
|
|
405
408
|
const itemW = item.width + LEGEND_GROUP_GAP;
|
|
406
409
|
if (currentRowW + item.width > groupAvailW && currentRowItems.length > 0) {
|
|
407
410
|
// Commit current row
|
|
408
|
-
|
|
411
|
+
if (!alignLeft)
|
|
412
|
+
centerRowItems(currentRowItems, containerWidth, totalControlsW);
|
|
409
413
|
rows.push({ y: rowY, items: currentRowItems });
|
|
410
414
|
rowY += LEGEND_HEIGHT;
|
|
411
415
|
currentRowItems = [];
|
package/src/utils/tag-groups.ts
CHANGED
|
@@ -304,6 +304,27 @@ export function validateTagValues(
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
// ── Tag Group Name Validation ────────────────────────────
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Warn when a tag group uses the reserved name "none" (case-insensitive).
|
|
311
|
+
* Should be called alongside `validateTagValues()` in each parser's
|
|
312
|
+
* post-parse validation.
|
|
313
|
+
*/
|
|
314
|
+
export function validateTagGroupNames(
|
|
315
|
+
tagGroups: ReadonlyArray<{ name: string; lineNumber: number }>,
|
|
316
|
+
pushWarning: (lineNumber: number, message: string) => void
|
|
317
|
+
): void {
|
|
318
|
+
for (const group of tagGroups) {
|
|
319
|
+
if (group.name.toLowerCase() === 'none') {
|
|
320
|
+
pushWarning(
|
|
321
|
+
group.lineNumber,
|
|
322
|
+
`'none' is a reserved keyword and cannot be used as a tag group name`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
307
328
|
// ── Default Metadata Injection ────────────────────────────
|
|
308
329
|
|
|
309
330
|
/**
|
|
@@ -340,6 +361,49 @@ export function injectDefaultTagMetadata(
|
|
|
340
361
|
}
|
|
341
362
|
}
|
|
342
363
|
|
|
364
|
+
// ── Active Tag Group Resolution ──────────────────────────────
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Determine which tag group should be active, using a priority chain:
|
|
368
|
+
*
|
|
369
|
+
* 1. Programmatic override (from render API / CLI flag) — highest priority
|
|
370
|
+
* 2. Diagram-level `active-tag` option (from parsed source)
|
|
371
|
+
* 3. Auto-activate first declared tag group
|
|
372
|
+
* 4. No coloring (null)
|
|
373
|
+
*
|
|
374
|
+
* The sentinel value `"none"` (case-insensitive) at any level means
|
|
375
|
+
* "suppress tag coloring."
|
|
376
|
+
*
|
|
377
|
+
* @param tagGroups Declared tag groups (only `.name` is used)
|
|
378
|
+
* @param explicitActiveTag Value of `active-tag` option from parsed diagram, if any
|
|
379
|
+
* @param programmaticOverride Value from render API / CLI; `undefined` = not set,
|
|
380
|
+
* `null` or `''` = explicitly no coloring
|
|
381
|
+
*/
|
|
382
|
+
export function resolveActiveTagGroup(
|
|
383
|
+
tagGroups: ReadonlyArray<{ name: string }>,
|
|
384
|
+
explicitActiveTag: string | undefined,
|
|
385
|
+
programmaticOverride?: string | null
|
|
386
|
+
): string | null {
|
|
387
|
+
// 1. Programmatic override (highest priority)
|
|
388
|
+
if (programmaticOverride !== undefined) {
|
|
389
|
+
if (!programmaticOverride) return null; // null or ''
|
|
390
|
+
if (programmaticOverride.toLowerCase() === 'none') return null;
|
|
391
|
+
return programmaticOverride;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 2. Diagram-level active-tag option
|
|
395
|
+
if (explicitActiveTag) {
|
|
396
|
+
if (explicitActiveTag.toLowerCase() === 'none') return null;
|
|
397
|
+
return explicitActiveTag;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 3. Auto-activate first declared group
|
|
401
|
+
if (tagGroups.length > 0) return tagGroups[0].name;
|
|
402
|
+
|
|
403
|
+
// 4. No tag groups → no coloring
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
|
|
343
407
|
// ── Matchers ────────────────────────────────────────────────
|
|
344
408
|
|
|
345
409
|
export function matchTagBlockHeading(trimmed: string): TagBlockMatch | null {
|