@diagrammo/dgmo 0.2.19 → 0.2.21
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 +33 -33
- package/dist/cli.cjs +150 -144
- package/dist/index.cjs +9475 -8087
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -1
- package/dist/index.d.ts +124 -1
- package/dist/index.js +9345 -7965
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chart.ts +40 -9
- package/src/class/parser.ts +37 -6
- package/src/class/renderer.ts +11 -8
- package/src/class/types.ts +4 -0
- package/src/cli.ts +38 -3
- package/src/d3.ts +159 -48
- package/src/dgmo-mermaid.ts +7 -1
- package/src/dgmo-router.ts +74 -4
- package/src/diagnostics.ts +77 -0
- package/src/echarts.ts +23 -14
- package/src/er/layout.ts +49 -7
- package/src/er/parser.ts +31 -4
- package/src/er/renderer.ts +2 -1
- package/src/er/types.ts +3 -0
- package/src/graph/flowchart-parser.ts +34 -4
- package/src/graph/flowchart-renderer.ts +35 -32
- package/src/graph/types.ts +4 -0
- package/src/index.ts +22 -0
- package/src/kanban/mutations.ts +183 -0
- package/src/kanban/parser.ts +389 -0
- package/src/kanban/renderer.ts +564 -0
- package/src/kanban/types.ts +45 -0
- package/src/org/layout.ts +97 -66
- package/src/org/parser.ts +50 -15
- package/src/org/renderer.ts +91 -159
- package/src/org/resolver.ts +470 -0
- package/src/sequence/parser.ts +90 -33
- package/src/sequence/renderer.ts +13 -5
package/src/org/renderer.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { layoutOrg } from './layout';
|
|
|
15
15
|
// ============================================================
|
|
16
16
|
|
|
17
17
|
const DIAGRAM_PADDING = 20;
|
|
18
|
+
const MAX_SCALE = 3;
|
|
18
19
|
const TITLE_HEIGHT = 30;
|
|
19
20
|
const TITLE_FONT_SIZE = 18;
|
|
20
21
|
const LABEL_FONT_SIZE = 13;
|
|
@@ -35,29 +36,17 @@ const CONTAINER_HEADER_HEIGHT = 28;
|
|
|
35
36
|
const COLLAPSE_BAR_HEIGHT = 6;
|
|
36
37
|
const COLLAPSE_BAR_INSET = 0;
|
|
37
38
|
|
|
38
|
-
// Legend
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
// Eye icon (12×12 viewBox, scaled from 0,0 to 12,12)
|
|
51
|
-
const EYE_ICON_SIZE = 12;
|
|
52
|
-
const EYE_ICON_GAP = 6;
|
|
53
|
-
// Open eye: elliptical outline + circle pupil
|
|
54
|
-
const EYE_OPEN_PATH =
|
|
55
|
-
'M1 6C1 6 3 2 6 2C9 2 11 6 11 6C11 6 9 10 6 10C3 10 1 6 1 6Z';
|
|
56
|
-
const EYE_PUPIL_CX = 6;
|
|
57
|
-
const EYE_PUPIL_CY = 6;
|
|
58
|
-
const EYE_PUPIL_R = 1.8;
|
|
59
|
-
// Closed eye: same outline + diagonal slash
|
|
60
|
-
const EYE_SLASH_PATH = 'M2 2L10 10';
|
|
39
|
+
// Legend (kanban-style pills)
|
|
40
|
+
const LEGEND_HEIGHT = 28;
|
|
41
|
+
const LEGEND_PILL_PAD = 16;
|
|
42
|
+
const LEGEND_PILL_FONT_SIZE = 11;
|
|
43
|
+
const LEGEND_PILL_FONT_W = LEGEND_PILL_FONT_SIZE * 0.6;
|
|
44
|
+
const LEGEND_CAPSULE_PAD = 4;
|
|
45
|
+
const LEGEND_DOT_R = 4;
|
|
46
|
+
const LEGEND_ENTRY_FONT_SIZE = 10;
|
|
47
|
+
const LEGEND_ENTRY_FONT_W = LEGEND_ENTRY_FONT_SIZE * 0.6;
|
|
48
|
+
const LEGEND_ENTRY_DOT_GAP = 4;
|
|
49
|
+
const LEGEND_ENTRY_TRAIL = 8;
|
|
61
50
|
|
|
62
51
|
// ============================================================
|
|
63
52
|
// Color helpers (inline to avoid cross-module import issues)
|
|
@@ -144,7 +133,7 @@ export function renderOrg(
|
|
|
144
133
|
const diagramH = layout.height + titleOffset;
|
|
145
134
|
const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
|
|
146
135
|
const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
|
|
147
|
-
const scale = Math.min(scaleX, scaleY);
|
|
136
|
+
const scale = Math.min(MAX_SCALE, scaleX, scaleY);
|
|
148
137
|
|
|
149
138
|
// Center the diagram
|
|
150
139
|
const scaledW = diagramW * scale;
|
|
@@ -209,6 +198,7 @@ export function renderOrg(
|
|
|
209
198
|
}
|
|
210
199
|
|
|
211
200
|
// Render container backgrounds (bottom layer)
|
|
201
|
+
const colorOff = parsed.options?.color === 'off';
|
|
212
202
|
for (const c of layout.containers) {
|
|
213
203
|
const cG = contentG
|
|
214
204
|
.append('g')
|
|
@@ -234,8 +224,8 @@ export function renderOrg(
|
|
|
234
224
|
});
|
|
235
225
|
}
|
|
236
226
|
|
|
237
|
-
const fill = containerFill(palette, isDark, c.color);
|
|
238
|
-
const stroke = containerStroke(palette, c.color);
|
|
227
|
+
const fill = containerFill(palette, isDark, colorOff ? undefined : c.color);
|
|
228
|
+
const stroke = containerStroke(palette, colorOff ? undefined : c.color);
|
|
239
229
|
|
|
240
230
|
// Background rect
|
|
241
231
|
cG.append('rect')
|
|
@@ -301,7 +291,7 @@ export function renderOrg(
|
|
|
301
291
|
.attr('y', c.height - COLLAPSE_BAR_HEIGHT)
|
|
302
292
|
.attr('width', c.width - COLLAPSE_BAR_INSET * 2)
|
|
303
293
|
.attr('height', COLLAPSE_BAR_HEIGHT)
|
|
304
|
-
.attr('fill', containerStroke(palette, c.color))
|
|
294
|
+
.attr('fill', containerStroke(palette, colorOff ? undefined : c.color))
|
|
305
295
|
.attr('clip-path', `url(#${clipId})`)
|
|
306
296
|
.attr('class', 'org-collapse-bar');
|
|
307
297
|
}
|
|
@@ -360,8 +350,8 @@ export function renderOrg(
|
|
|
360
350
|
}
|
|
361
351
|
|
|
362
352
|
// Card background
|
|
363
|
-
const fill = nodeFill(palette, isDark, node.color);
|
|
364
|
-
const stroke = nodeStroke(palette, node.color);
|
|
353
|
+
const fill = nodeFill(palette, isDark, colorOff ? undefined : node.color);
|
|
354
|
+
const stroke = nodeStroke(palette, colorOff ? undefined : node.color);
|
|
365
355
|
|
|
366
356
|
const rect = nodeG
|
|
367
357
|
.append('rect')
|
|
@@ -448,22 +438,30 @@ export function renderOrg(
|
|
|
448
438
|
.attr('y', node.height - COLLAPSE_BAR_HEIGHT)
|
|
449
439
|
.attr('width', node.width - COLLAPSE_BAR_INSET * 2)
|
|
450
440
|
.attr('height', COLLAPSE_BAR_HEIGHT)
|
|
451
|
-
.attr('fill', nodeStroke(palette, node.color))
|
|
441
|
+
.attr('fill', nodeStroke(palette, colorOff ? undefined : node.color))
|
|
452
442
|
.attr('clip-path', `url(#${clipId})`)
|
|
453
443
|
.attr('class', 'org-collapse-bar');
|
|
454
444
|
}
|
|
455
445
|
|
|
456
446
|
}
|
|
457
447
|
|
|
458
|
-
// Render legend —
|
|
459
|
-
|
|
448
|
+
// Render legend — kanban-style pills.
|
|
449
|
+
// Skip in export mode (unless legend-only chart).
|
|
450
|
+
// Active group: only that group rendered as capsule (pill + entries).
|
|
451
|
+
// No active group: all groups rendered as standalone pills.
|
|
452
|
+
if (!exportDims || layout.nodes.length === 0) for (const group of layout.legend) {
|
|
460
453
|
const isActive =
|
|
461
454
|
activeTagGroup != null &&
|
|
462
455
|
group.name.toLowerCase() === activeTagGroup.toLowerCase();
|
|
463
456
|
|
|
464
|
-
// When a group is active, skip
|
|
457
|
+
// When a group is active, skip all other groups entirely
|
|
465
458
|
if (activeTagGroup != null && !isActive) continue;
|
|
466
459
|
|
|
460
|
+
const groupBg = mix(palette.surface, palette.bg, isDark ? 35 : 20);
|
|
461
|
+
|
|
462
|
+
const pillWidth =
|
|
463
|
+
group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
|
|
464
|
+
|
|
467
465
|
const gEl = contentG
|
|
468
466
|
.append('g')
|
|
469
467
|
.attr('transform', `translate(${group.x}, ${group.y})`)
|
|
@@ -471,143 +469,77 @@ export function renderOrg(
|
|
|
471
469
|
.attr('data-legend-group', group.name.toLowerCase())
|
|
472
470
|
.style('cursor', 'pointer');
|
|
473
471
|
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
.attr('fill', legendFill);
|
|
472
|
+
// Outer capsule background (active only)
|
|
473
|
+
if (isActive) {
|
|
474
|
+
gEl
|
|
475
|
+
.append('rect')
|
|
476
|
+
.attr('width', group.width)
|
|
477
|
+
.attr('height', LEGEND_HEIGHT)
|
|
478
|
+
.attr('rx', LEGEND_HEIGHT / 2)
|
|
479
|
+
.attr('fill', groupBg);
|
|
480
|
+
}
|
|
484
481
|
|
|
482
|
+
const pillX = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
483
|
+
const pillY = isActive ? LEGEND_CAPSULE_PAD : 0;
|
|
484
|
+
const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
|
|
485
|
+
|
|
486
|
+
// Pill background
|
|
487
|
+
gEl
|
|
488
|
+
.append('rect')
|
|
489
|
+
.attr('x', pillX)
|
|
490
|
+
.attr('y', pillY)
|
|
491
|
+
.attr('width', pillWidth)
|
|
492
|
+
.attr('height', pillH)
|
|
493
|
+
.attr('rx', pillH / 2)
|
|
494
|
+
.attr('fill', isActive ? palette.bg : groupBg);
|
|
495
|
+
|
|
496
|
+
// Active pill border
|
|
485
497
|
if (isActive) {
|
|
486
|
-
|
|
487
|
-
.
|
|
488
|
-
.attr('
|
|
489
|
-
.attr('
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
.attr('
|
|
493
|
-
.attr('
|
|
494
|
-
.attr('stroke
|
|
498
|
+
gEl
|
|
499
|
+
.append('rect')
|
|
500
|
+
.attr('x', pillX)
|
|
501
|
+
.attr('y', pillY)
|
|
502
|
+
.attr('width', pillWidth)
|
|
503
|
+
.attr('height', pillH)
|
|
504
|
+
.attr('rx', pillH / 2)
|
|
505
|
+
.attr('fill', 'none')
|
|
506
|
+
.attr('stroke', mix(palette.textMuted, palette.bg, 50))
|
|
507
|
+
.attr('stroke-width', 0.75);
|
|
495
508
|
}
|
|
496
509
|
|
|
497
|
-
//
|
|
510
|
+
// Pill text
|
|
498
511
|
gEl
|
|
499
512
|
.append('text')
|
|
500
|
-
.attr('x',
|
|
501
|
-
.attr('y',
|
|
502
|
-
.attr('
|
|
503
|
-
.attr('font-
|
|
504
|
-
.attr('
|
|
513
|
+
.attr('x', pillX + pillWidth / 2)
|
|
514
|
+
.attr('y', LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2)
|
|
515
|
+
.attr('font-size', LEGEND_PILL_FONT_SIZE)
|
|
516
|
+
.attr('font-weight', '500')
|
|
517
|
+
.attr('fill', isActive ? palette.text : palette.textMuted)
|
|
518
|
+
.attr('text-anchor', 'middle')
|
|
505
519
|
.text(group.name);
|
|
506
520
|
|
|
507
|
-
//
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
LEGEND_PAD + group.name.length * LEGEND_CHAR_WIDTH + EYE_ICON_GAP;
|
|
513
|
-
const eyeY = (LEGEND_HEADER_H - EYE_ICON_SIZE) / 2;
|
|
514
|
-
|
|
515
|
-
const eyeG = gEl
|
|
516
|
-
.append('g')
|
|
517
|
-
.attr('class', 'org-legend-eye')
|
|
518
|
-
.attr('data-legend-visibility', groupKey)
|
|
519
|
-
.attr('transform', `translate(${eyeX}, ${eyeY})`);
|
|
520
|
-
|
|
521
|
-
// Transparent hit area
|
|
522
|
-
eyeG
|
|
523
|
-
.append('rect')
|
|
524
|
-
.attr('x', -4)
|
|
525
|
-
.attr('y', -4)
|
|
526
|
-
.attr('width', EYE_ICON_SIZE + 8)
|
|
527
|
-
.attr('height', EYE_ICON_SIZE + 8)
|
|
528
|
-
.attr('fill', 'transparent');
|
|
529
|
-
|
|
530
|
-
// Eye outline
|
|
531
|
-
eyeG
|
|
532
|
-
.append('path')
|
|
533
|
-
.attr('d', EYE_OPEN_PATH)
|
|
534
|
-
.attr('fill', isHidden ? 'none' : palette.textMuted)
|
|
535
|
-
.attr('fill-opacity', isHidden ? 0 : 0.15)
|
|
536
|
-
.attr('stroke', palette.textMuted)
|
|
537
|
-
.attr('stroke-width', 1.2)
|
|
538
|
-
.attr('opacity', isHidden ? 0.5 : 0.7);
|
|
539
|
-
|
|
540
|
-
if (!isHidden) {
|
|
541
|
-
// Pupil (only when visible)
|
|
542
|
-
eyeG
|
|
521
|
+
// Entries inside capsule (active only)
|
|
522
|
+
if (isActive) {
|
|
523
|
+
let entryX = pillX + pillWidth + 4;
|
|
524
|
+
for (const entry of group.entries) {
|
|
525
|
+
gEl
|
|
543
526
|
.append('circle')
|
|
544
|
-
.attr('cx',
|
|
545
|
-
.attr('cy',
|
|
546
|
-
.attr('r',
|
|
527
|
+
.attr('cx', entryX + LEGEND_DOT_R)
|
|
528
|
+
.attr('cy', LEGEND_HEIGHT / 2)
|
|
529
|
+
.attr('r', LEGEND_DOT_R)
|
|
530
|
+
.attr('fill', entry.color);
|
|
531
|
+
|
|
532
|
+
const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
|
|
533
|
+
gEl
|
|
534
|
+
.append('text')
|
|
535
|
+
.attr('x', textX)
|
|
536
|
+
.attr('y', LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
|
|
537
|
+
.attr('font-size', LEGEND_ENTRY_FONT_SIZE)
|
|
547
538
|
.attr('fill', palette.textMuted)
|
|
548
|
-
.
|
|
549
|
-
} else {
|
|
550
|
-
// Slash through the eye (hidden state)
|
|
551
|
-
eyeG
|
|
552
|
-
.append('line')
|
|
553
|
-
.attr('x1', 2)
|
|
554
|
-
.attr('y1', 2)
|
|
555
|
-
.attr('x2', 10)
|
|
556
|
-
.attr('y2', 10)
|
|
557
|
-
.attr('stroke', palette.textMuted)
|
|
558
|
-
.attr('stroke-width', 1.5)
|
|
559
|
-
.attr('opacity', 0.5);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
539
|
+
.text(entry.value);
|
|
562
540
|
|
|
563
|
-
|
|
564
|
-
const entryWidths = group.entries.map(
|
|
565
|
-
(e) =>
|
|
566
|
-
LEGEND_DOT_R * 2 + LEGEND_DOT_TEXT_GAP + e.value.length * LEGEND_CHAR_WIDTH
|
|
567
|
-
);
|
|
568
|
-
const numRows = Math.ceil(group.entries.length / LEGEND_MAX_PER_ROW);
|
|
569
|
-
const colWidths: number[] = [];
|
|
570
|
-
for (let col = 0; col < LEGEND_MAX_PER_ROW; col++) {
|
|
571
|
-
let maxW = 0;
|
|
572
|
-
for (let r = 0; r < numRows; r++) {
|
|
573
|
-
const idx = r * LEGEND_MAX_PER_ROW + col;
|
|
574
|
-
if (idx < entryWidths.length && entryWidths[idx] > maxW) {
|
|
575
|
-
maxW = entryWidths[idx];
|
|
576
|
-
}
|
|
541
|
+
entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
|
|
577
542
|
}
|
|
578
|
-
if (maxW > 0) colWidths.push(maxW);
|
|
579
|
-
}
|
|
580
|
-
const colX: number[] = [LEGEND_PAD];
|
|
581
|
-
for (let c = 1; c < colWidths.length; c++) {
|
|
582
|
-
colX.push(colX[c - 1] + colWidths[c - 1] + LEGEND_ENTRY_GAP);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Entries: colored dot + value label
|
|
586
|
-
for (let i = 0; i < group.entries.length; i++) {
|
|
587
|
-
const entry = group.entries[i];
|
|
588
|
-
const row = Math.floor(i / LEGEND_MAX_PER_ROW);
|
|
589
|
-
const col = i % LEGEND_MAX_PER_ROW;
|
|
590
|
-
const entryX = colX[col];
|
|
591
|
-
|
|
592
|
-
const entryY =
|
|
593
|
-
LEGEND_HEADER_H + row * LEGEND_ENTRY_H + LEGEND_ENTRY_H / 2;
|
|
594
|
-
|
|
595
|
-
// Colored dot
|
|
596
|
-
gEl
|
|
597
|
-
.append('circle')
|
|
598
|
-
.attr('cx', entryX + LEGEND_DOT_R)
|
|
599
|
-
.attr('cy', entryY)
|
|
600
|
-
.attr('r', LEGEND_DOT_R)
|
|
601
|
-
.attr('fill', entry.color);
|
|
602
|
-
|
|
603
|
-
// Value label
|
|
604
|
-
gEl
|
|
605
|
-
.append('text')
|
|
606
|
-
.attr('x', entryX + LEGEND_DOT_R * 2 + LEGEND_DOT_TEXT_GAP)
|
|
607
|
-
.attr('y', entryY + LEGEND_FONT_SIZE / 2 - 2)
|
|
608
|
-
.attr('fill', palette.text)
|
|
609
|
-
.attr('font-size', LEGEND_FONT_SIZE)
|
|
610
|
-
.text(entry.value);
|
|
611
543
|
}
|
|
612
544
|
}
|
|
613
545
|
}
|
|
@@ -622,7 +554,7 @@ export function renderOrgForExport(
|
|
|
622
554
|
palette: PaletteColors
|
|
623
555
|
): string {
|
|
624
556
|
const parsed = parseOrg(content, palette);
|
|
625
|
-
if (parsed.error
|
|
557
|
+
if (parsed.error) return '';
|
|
626
558
|
|
|
627
559
|
// Extract hide option for export: cards sized without hidden attributes
|
|
628
560
|
const hideOption = parsed.options?.['hide'];
|