@diagrammo/dgmo 0.8.14 → 0.8.16
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 +101 -101
- package/dist/index.cjs +623 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -9
- package/dist/index.d.ts +41 -9
- package/dist/index.js +620 -119
- package/dist/index.js.map +1 -1
- package/docs/guide/colors.md +26 -22
- package/docs/language-reference.md +3 -1
- package/package.json +1 -1
- package/src/chart.ts +22 -4
- package/src/class/parser.ts +9 -2
- package/src/colors.ts +64 -8
- package/src/d3.ts +71 -34
- package/src/dgmo-mermaid.ts +8 -2
- package/src/echarts.ts +19 -4
- package/src/er/parser.ts +12 -4
- package/src/graph/flowchart-parser.ts +36 -6
- package/src/graph/state-parser.ts +34 -6
- package/src/index.ts +9 -1
- package/src/infra/parser.ts +7 -1
- package/src/kanban/parser.ts +17 -5
- package/src/kanban/renderer.ts +524 -8
- package/src/palettes/bold.ts +4 -0
- package/src/palettes/catppuccin.ts +4 -0
- package/src/palettes/dracula.ts +4 -0
- package/src/palettes/gruvbox.ts +4 -0
- package/src/palettes/monokai.ts +4 -0
- package/src/palettes/nord.ts +4 -0
- package/src/palettes/one-dark.ts +4 -0
- package/src/palettes/rose-pine.ts +4 -0
- package/src/palettes/solarized.ts +4 -0
- package/src/palettes/tokyo-night.ts +4 -0
- package/src/palettes/types.ts +2 -0
- package/src/sequence/parser.ts +6 -1
- package/src/sequence/renderer.ts +2 -6
- package/src/sharing.ts +11 -3
- package/src/sitemap/parser.ts +43 -10
- package/src/utils/duration.ts +10 -1
- package/src/utils/parsing.ts +22 -6
|
@@ -46,6 +46,8 @@ export const rosePinePalette: PaletteConfig = {
|
|
|
46
46
|
teal: '#286983', // Pine
|
|
47
47
|
cyan: '#56949f', // Foam
|
|
48
48
|
gray: '#9893a5', // Muted
|
|
49
|
+
black: '#575279',
|
|
50
|
+
white: '#fffaf3',
|
|
49
51
|
},
|
|
50
52
|
},
|
|
51
53
|
dark: {
|
|
@@ -69,6 +71,8 @@ export const rosePinePalette: PaletteConfig = {
|
|
|
69
71
|
teal: '#3e8fb0', // Pine
|
|
70
72
|
cyan: '#9ccfd8', // Foam
|
|
71
73
|
gray: '#6e6a86', // Muted
|
|
74
|
+
black: '#2a273f',
|
|
75
|
+
white: '#e0def4',
|
|
72
76
|
},
|
|
73
77
|
},
|
|
74
78
|
};
|
|
@@ -39,6 +39,8 @@ export const solarizedPalette: PaletteConfig = {
|
|
|
39
39
|
teal: '#2aa198',
|
|
40
40
|
cyan: '#2aa198', // Solarized has no separate cyan — reuse teal
|
|
41
41
|
gray: '#586e75', // base01
|
|
42
|
+
black: '#657b83',
|
|
43
|
+
white: '#eee8d5',
|
|
42
44
|
},
|
|
43
45
|
},
|
|
44
46
|
dark: {
|
|
@@ -62,6 +64,8 @@ export const solarizedPalette: PaletteConfig = {
|
|
|
62
64
|
teal: '#2aa198',
|
|
63
65
|
cyan: '#2aa198',
|
|
64
66
|
gray: '#586e75', // base01
|
|
67
|
+
black: '#073642',
|
|
68
|
+
white: '#839496',
|
|
65
69
|
},
|
|
66
70
|
},
|
|
67
71
|
};
|
|
@@ -48,6 +48,8 @@ export const tokyoNightPalette: PaletteConfig = {
|
|
|
48
48
|
teal: '#118c74',
|
|
49
49
|
cyan: '#007197',
|
|
50
50
|
gray: '#8990b3', // Day dark3
|
|
51
|
+
black: '#1a1b26',
|
|
52
|
+
white: '#d0d5e3',
|
|
51
53
|
},
|
|
52
54
|
},
|
|
53
55
|
dark: {
|
|
@@ -71,6 +73,8 @@ export const tokyoNightPalette: PaletteConfig = {
|
|
|
71
73
|
teal: '#1abc9c',
|
|
72
74
|
cyan: '#7dcfff',
|
|
73
75
|
gray: '#565f89', // Night comment
|
|
76
|
+
black: '#292e42',
|
|
77
|
+
white: '#c0caf5',
|
|
74
78
|
},
|
|
75
79
|
},
|
|
76
80
|
};
|
package/src/palettes/types.ts
CHANGED
package/src/sequence/parser.ts
CHANGED
|
@@ -604,7 +604,12 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
604
604
|
// First entry is the default unless another is marked `default`
|
|
605
605
|
if (currentTagGroup && !contentStarted && measureIndent(raw) > 0) {
|
|
606
606
|
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
607
|
-
const { label, color } = extractColor(
|
|
607
|
+
const { label, color } = extractColor(
|
|
608
|
+
cleanEntry,
|
|
609
|
+
undefined,
|
|
610
|
+
result.diagnostics,
|
|
611
|
+
lineNumber
|
|
612
|
+
);
|
|
608
613
|
if (!color) {
|
|
609
614
|
pushError(
|
|
610
615
|
lineNumber,
|
package/src/sequence/renderer.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from '../utils/inline-markdown';
|
|
13
13
|
export { parseInlineMarkdown, truncateBareUrl };
|
|
14
14
|
import { FONT_FAMILY } from '../fonts';
|
|
15
|
-
import { resolveColor } from '../colors';
|
|
16
15
|
import type {
|
|
17
16
|
ParsedSequenceDgmo,
|
|
18
17
|
SequenceElement,
|
|
@@ -936,10 +935,7 @@ export function renderSequenceDiagram(
|
|
|
936
935
|
);
|
|
937
936
|
if (tg) {
|
|
938
937
|
for (const entry of tg.entries) {
|
|
939
|
-
tagValueToColor.set(
|
|
940
|
-
entry.value.toLowerCase(),
|
|
941
|
-
resolveColor(entry.color) ?? entry.color
|
|
942
|
-
);
|
|
938
|
+
tagValueToColor.set(entry.value.toLowerCase(), entry.color);
|
|
943
939
|
}
|
|
944
940
|
}
|
|
945
941
|
}
|
|
@@ -1598,7 +1594,7 @@ export function renderSequenceDiagram(
|
|
|
1598
1594
|
name: tg.name,
|
|
1599
1595
|
entries: tg.entries.map((e) => ({
|
|
1600
1596
|
value: e.value,
|
|
1601
|
-
color:
|
|
1597
|
+
color: e.color,
|
|
1602
1598
|
})),
|
|
1603
1599
|
}));
|
|
1604
1600
|
const legendConfig: LegendConfig = {
|
package/src/sharing.ts
CHANGED
|
@@ -17,11 +17,13 @@ export interface DiagramViewState {
|
|
|
17
17
|
export interface DecodedDiagramUrl {
|
|
18
18
|
dsl: string;
|
|
19
19
|
viewState: DiagramViewState;
|
|
20
|
+
filename?: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface EncodeDiagramUrlOptions {
|
|
23
24
|
baseUrl?: string;
|
|
24
25
|
viewState?: DiagramViewState;
|
|
26
|
+
filename?: string;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export type EncodeDiagramUrlResult =
|
|
@@ -80,6 +82,10 @@ export function encodeDiagramUrl(
|
|
|
80
82
|
hash += `&th=${encodeURIComponent(options.viewState.theme)}`;
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
if (options?.filename) {
|
|
86
|
+
hash += `&fn=${encodeURIComponent(options.filename)}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
// Encode in both query param AND hash fragment — some share mechanisms
|
|
84
90
|
// strip one or the other (iOS share sheet strips #, AirDrop strips ?)
|
|
85
91
|
return { url: `${baseUrl}?${hash}#${hash}` };
|
|
@@ -97,6 +103,7 @@ export function encodeDiagramUrl(
|
|
|
97
103
|
*/
|
|
98
104
|
export function decodeDiagramUrl(hash: string): DecodedDiagramUrl {
|
|
99
105
|
const empty: DecodedDiagramUrl = { dsl: '', viewState: {} };
|
|
106
|
+
let filename: string | undefined;
|
|
100
107
|
if (!hash) return empty;
|
|
101
108
|
|
|
102
109
|
let raw = hash;
|
|
@@ -134,6 +141,7 @@ export function decodeDiagramUrl(hash: string): DecodedDiagramUrl {
|
|
|
134
141
|
if (key === 'pal' && val) viewState.palette = val;
|
|
135
142
|
if (key === 'th' && (val === 'light' || val === 'dark'))
|
|
136
143
|
viewState.theme = val;
|
|
144
|
+
if (key === 'fn' && val) filename = val;
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
// Strip 'dgmo=' prefix
|
|
@@ -141,12 +149,12 @@ export function decodeDiagramUrl(hash: string): DecodedDiagramUrl {
|
|
|
141
149
|
payload = payload.slice(5);
|
|
142
150
|
}
|
|
143
151
|
|
|
144
|
-
if (!payload) return { dsl: '', viewState };
|
|
152
|
+
if (!payload) return { dsl: '', viewState, filename };
|
|
145
153
|
|
|
146
154
|
try {
|
|
147
155
|
const result = decompressFromEncodedURIComponent(payload);
|
|
148
|
-
return { dsl: result ?? '', viewState };
|
|
156
|
+
return { dsl: result ?? '', viewState, filename };
|
|
149
157
|
} catch {
|
|
150
|
-
return { dsl: '', viewState };
|
|
158
|
+
return { dsl: '', viewState, filename };
|
|
151
159
|
}
|
|
152
160
|
}
|
package/src/sitemap/parser.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
5
|
import type { PaletteColors } from '../palettes';
|
|
6
|
-
import {
|
|
6
|
+
import { resolveColorWithDiagnostic } from '../colors';
|
|
7
|
+
import type { DgmoError } from '../diagnostics';
|
|
7
8
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
8
9
|
import type { TagGroup } from '../utils/tag-groups';
|
|
9
10
|
import {
|
|
@@ -46,7 +47,9 @@ const BARE_ARROW_RE = /^->\s*(.+)$/;
|
|
|
46
47
|
|
|
47
48
|
function parseArrowLine(
|
|
48
49
|
trimmed: string,
|
|
49
|
-
palette
|
|
50
|
+
palette: PaletteColors | undefined,
|
|
51
|
+
lineNumber: number,
|
|
52
|
+
diagnostics: DgmoError[]
|
|
50
53
|
): {
|
|
51
54
|
label?: string;
|
|
52
55
|
color?: string;
|
|
@@ -69,10 +72,22 @@ function parseArrowLine(
|
|
|
69
72
|
if (arrowMatch) {
|
|
70
73
|
const label = arrowMatch[1]?.trim() || undefined;
|
|
71
74
|
let color = arrowMatch[2]
|
|
72
|
-
? (
|
|
75
|
+
? resolveColorWithDiagnostic(
|
|
76
|
+
arrowMatch[2].trim(),
|
|
77
|
+
lineNumber,
|
|
78
|
+
diagnostics,
|
|
79
|
+
palette
|
|
80
|
+
)
|
|
73
81
|
: undefined;
|
|
74
82
|
if (label && !color) {
|
|
75
|
-
|
|
83
|
+
const inferred = inferArrowColor(label);
|
|
84
|
+
if (inferred)
|
|
85
|
+
color = resolveColorWithDiagnostic(
|
|
86
|
+
inferred,
|
|
87
|
+
lineNumber,
|
|
88
|
+
diagnostics,
|
|
89
|
+
palette
|
|
90
|
+
);
|
|
76
91
|
}
|
|
77
92
|
const rawTarget = arrowMatch[3].trim();
|
|
78
93
|
const groupMatch = rawTarget.match(/^\[(.+)\]$/);
|
|
@@ -321,7 +336,12 @@ export function parseSitemap(
|
|
|
321
336
|
|
|
322
337
|
// Check for arrow syntax (must check before metadata — arrows contain `:` in labels
|
|
323
338
|
// but also start with `-`)
|
|
324
|
-
const arrowInfo = parseArrowLine(
|
|
339
|
+
const arrowInfo = parseArrowLine(
|
|
340
|
+
trimmed,
|
|
341
|
+
palette,
|
|
342
|
+
lineNumber,
|
|
343
|
+
result.diagnostics
|
|
344
|
+
);
|
|
325
345
|
if (arrowInfo) {
|
|
326
346
|
// Find the source node: the most recent node on the indent stack
|
|
327
347
|
// at a shallower indent (same pattern as metadata attachment)
|
|
@@ -351,7 +371,12 @@ export function parseSitemap(
|
|
|
351
371
|
|
|
352
372
|
if (containerMatch) {
|
|
353
373
|
const rawLabel = containerMatch[1].trim();
|
|
354
|
-
const { label, color } = extractColor(
|
|
374
|
+
const { label, color } = extractColor(
|
|
375
|
+
rawLabel,
|
|
376
|
+
palette,
|
|
377
|
+
result.diagnostics,
|
|
378
|
+
lineNumber
|
|
379
|
+
);
|
|
355
380
|
|
|
356
381
|
// Parse optional pipe metadata on the container line
|
|
357
382
|
const pipeStr = containerMatch[2];
|
|
@@ -401,7 +426,8 @@ export function parseSitemap(
|
|
|
401
426
|
palette,
|
|
402
427
|
++nodeCounter,
|
|
403
428
|
aliasMap,
|
|
404
|
-
pushWarning
|
|
429
|
+
pushWarning,
|
|
430
|
+
result.diagnostics
|
|
405
431
|
);
|
|
406
432
|
attachNode(node, indent, indentStack, result);
|
|
407
433
|
labelToNode.set(node.label.toLowerCase(), node);
|
|
@@ -416,7 +442,8 @@ export function parseSitemap(
|
|
|
416
442
|
palette,
|
|
417
443
|
++nodeCounter,
|
|
418
444
|
aliasMap,
|
|
419
|
-
pushWarning
|
|
445
|
+
pushWarning,
|
|
446
|
+
result.diagnostics
|
|
420
447
|
);
|
|
421
448
|
attachNode(node, indent, indentStack, result);
|
|
422
449
|
labelToNode.set(node.label.toLowerCase(), node);
|
|
@@ -503,11 +530,17 @@ function parseNodeLabel(
|
|
|
503
530
|
palette: PaletteColors | undefined,
|
|
504
531
|
counter: number,
|
|
505
532
|
aliasMap: Map<string, string> = new Map(),
|
|
506
|
-
warnFn?: (line: number, msg: string) => void
|
|
533
|
+
warnFn?: (line: number, msg: string) => void,
|
|
534
|
+
diagnostics?: DgmoError[]
|
|
507
535
|
): SitemapNode {
|
|
508
536
|
const segments = trimmed.split('|').map((s) => s.trim());
|
|
509
537
|
const rawLabel = segments[0];
|
|
510
|
-
const { label, color } = extractColor(
|
|
538
|
+
const { label, color } = extractColor(
|
|
539
|
+
rawLabel,
|
|
540
|
+
palette,
|
|
541
|
+
diagnostics,
|
|
542
|
+
lineNumber
|
|
543
|
+
);
|
|
511
544
|
const metadata = parsePipeMetadata(
|
|
512
545
|
segments,
|
|
513
546
|
aliasMap,
|
package/src/utils/duration.ts
CHANGED
|
@@ -87,10 +87,19 @@ export function addBusinessDays(
|
|
|
87
87
|
const days = Math.round(Math.abs(count));
|
|
88
88
|
if (days === 0) return new Date(startDate);
|
|
89
89
|
|
|
90
|
+
// Guard: an invalid start date would produce an infinite loop because
|
|
91
|
+
// isWorkday() always returns false for NaN day-of-week. This happens
|
|
92
|
+
// mid-edit when the user is typing a partial date like `start 2026-`.
|
|
93
|
+
if (Number.isNaN(startDate.getTime())) return new Date(startDate);
|
|
94
|
+
|
|
95
|
+
// Guard: a workweek with no workdays (or fully blocked by holidays) would
|
|
96
|
+
// also loop forever. Bound the search at days * 14 calendar days, which
|
|
97
|
+
// covers up to 2 weeks of skipped days per business day requested.
|
|
90
98
|
const current = new Date(startDate);
|
|
91
99
|
let remaining = days;
|
|
100
|
+
let safety = days * 14 + 14;
|
|
92
101
|
|
|
93
|
-
while (remaining > 0) {
|
|
102
|
+
while (remaining > 0 && safety-- > 0) {
|
|
94
103
|
current.setDate(current.getDate() + direction);
|
|
95
104
|
if (isWorkday(current, workweek, holidaySet)) {
|
|
96
105
|
remaining--;
|
package/src/utils/parsing.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* pipe-metadata parsing.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { resolveColor } from '../colors';
|
|
7
|
+
import { resolveColor, resolveColorWithDiagnostic } from '../colors';
|
|
8
|
+
import type { DgmoError } from '../diagnostics';
|
|
8
9
|
import type { PaletteColors } from '../palettes';
|
|
9
10
|
|
|
10
11
|
// ── All known chart types ────────────────────────────────────
|
|
@@ -65,14 +66,22 @@ export const COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
|
65
66
|
/** Extract an optional trailing color suffix from a label, resolving via palette. */
|
|
66
67
|
export function extractColor(
|
|
67
68
|
label: string,
|
|
68
|
-
palette?: PaletteColors
|
|
69
|
+
palette?: PaletteColors,
|
|
70
|
+
diagnostics?: DgmoError[],
|
|
71
|
+
line?: number
|
|
69
72
|
): { label: string; color?: string } {
|
|
70
73
|
const m = label.match(COLOR_SUFFIX_RE);
|
|
71
74
|
if (!m) return { label };
|
|
72
75
|
const colorName = m[1].trim();
|
|
76
|
+
let color: string | undefined;
|
|
77
|
+
if (diagnostics && line !== undefined) {
|
|
78
|
+
color = resolveColorWithDiagnostic(colorName, line, diagnostics, palette);
|
|
79
|
+
} else {
|
|
80
|
+
color = resolveColor(colorName, palette) ?? undefined;
|
|
81
|
+
}
|
|
73
82
|
return {
|
|
74
83
|
label: label.substring(0, m.index!).trim(),
|
|
75
|
-
color
|
|
84
|
+
color,
|
|
76
85
|
};
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -299,7 +308,8 @@ export function parseSeriesNames(
|
|
|
299
308
|
value: string,
|
|
300
309
|
lines: string[],
|
|
301
310
|
lineIndex: number,
|
|
302
|
-
palette?: PaletteColors
|
|
311
|
+
palette?: PaletteColors,
|
|
312
|
+
diagnostics?: DgmoError[]
|
|
303
313
|
): {
|
|
304
314
|
series: string;
|
|
305
315
|
names: string[];
|
|
@@ -328,8 +338,14 @@ export function parseSeriesNames(
|
|
|
328
338
|
}
|
|
329
339
|
const names: string[] = [];
|
|
330
340
|
const nameColors: (string | undefined)[] = [];
|
|
331
|
-
for (
|
|
332
|
-
const
|
|
341
|
+
for (let i = 0; i < rawNames.length; i++) {
|
|
342
|
+
const raw = rawNames[i];
|
|
343
|
+
const extracted = extractColor(
|
|
344
|
+
raw,
|
|
345
|
+
palette,
|
|
346
|
+
diagnostics,
|
|
347
|
+
nameLineNumbers[i]
|
|
348
|
+
);
|
|
333
349
|
nameColors.push(extracted.color);
|
|
334
350
|
names.push(extracted.label);
|
|
335
351
|
}
|