@diagrammo/dgmo 0.8.20 → 0.8.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/dist/cli.cjs +92 -90
- package/dist/editor.cjs +13 -1
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +13 -1
- package/dist/editor.js.map +1 -1
- package/dist/highlight.cjs +13 -1
- package/dist/highlight.cjs.map +1 -1
- package/dist/highlight.js +13 -1
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +4144 -940
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +318 -84
- package/dist/index.d.ts +318 -84
- package/dist/index.js +4132 -938
- package/dist/index.js.map +1 -1
- package/docs/guide/chart-mindmap.md +198 -0
- package/docs/guide/chart-sequence.md +23 -1
- package/docs/guide/chart-wireframe.md +100 -0
- package/docs/guide/index.md +8 -0
- package/docs/language-reference.md +137 -2
- package/package.json +1 -1
- package/src/boxes-and-lines/collapse.ts +21 -3
- package/src/boxes-and-lines/layout.ts +51 -9
- package/src/boxes-and-lines/parser.ts +8 -1
- package/src/boxes-and-lines/renderer.ts +121 -23
- package/src/boxes-and-lines/types.ts +1 -0
- package/src/completion.ts +26 -0
- package/src/d3.ts +153 -32
- package/src/dgmo-router.ts +6 -0
- package/src/editor/keywords.ts +12 -0
- package/src/graph/layout.ts +73 -9
- package/src/graph/state-collapse.ts +78 -0
- package/src/graph/state-renderer.ts +139 -34
- package/src/index.ts +28 -0
- package/src/kanban/renderer.ts +303 -57
- package/src/mindmap/collapse.ts +88 -0
- package/src/mindmap/layout.ts +605 -0
- package/src/mindmap/parser.ts +379 -0
- package/src/mindmap/renderer.ts +543 -0
- package/src/mindmap/text-wrap.ts +207 -0
- package/src/mindmap/types.ts +55 -0
- package/src/render.ts +18 -21
- package/src/sequence/renderer.ts +129 -18
- package/src/sharing.ts +2 -0
- package/src/sitemap/layout.ts +35 -12
- package/src/utils/export-container.ts +3 -2
- package/src/utils/legend-d3.ts +1 -0
- package/src/utils/legend-layout.ts +2 -2
- package/src/utils/parsing.ts +2 -0
- package/src/wireframe/layout.ts +460 -0
- package/src/wireframe/parser.ts +956 -0
- package/src/wireframe/renderer.ts +1293 -0
- package/src/wireframe/types.ts +110 -0
|
@@ -36,6 +36,7 @@ const CHAR_WIDTH_RATIO = 0.6;
|
|
|
36
36
|
const NODE_TEXT_PADDING = 12;
|
|
37
37
|
const GROUP_RX = 8;
|
|
38
38
|
const GROUP_LABEL_FONT_SIZE = 14;
|
|
39
|
+
const GROUP_LABEL_ZONE = 32;
|
|
39
40
|
|
|
40
41
|
type D3G = d3Selection.Selection<SVGGElement, unknown, null, undefined>;
|
|
41
42
|
type D3Svg = d3Selection.Selection<SVGSVGElement, unknown, null, undefined>;
|
|
@@ -45,19 +46,13 @@ const lineGeneratorLR = d3Shape
|
|
|
45
46
|
.line<{ x: number; y: number }>()
|
|
46
47
|
.x((d) => d.x)
|
|
47
48
|
.y((d) => d.y)
|
|
48
|
-
.curve(d3Shape.
|
|
49
|
+
.curve(d3Shape.curveBasis);
|
|
49
50
|
|
|
50
51
|
const lineGeneratorTB = d3Shape
|
|
51
52
|
.line<{ x: number; y: number }>()
|
|
52
53
|
.x((d) => d.x)
|
|
53
54
|
.y((d) => d.y)
|
|
54
|
-
.curve(d3Shape.
|
|
55
|
-
|
|
56
|
-
const lineGeneratorLinear = d3Shape
|
|
57
|
-
.line<{ x: number; y: number }>()
|
|
58
|
-
.x((d) => d.x)
|
|
59
|
-
.y((d) => d.y)
|
|
60
|
-
.curve(d3Shape.curveLinear);
|
|
55
|
+
.curve(d3Shape.curveBasis);
|
|
61
56
|
|
|
62
57
|
// ── Text fitting ───────────────────────────────────────────
|
|
63
58
|
|
|
@@ -336,8 +331,18 @@ export function renderBoxesAndLines(
|
|
|
336
331
|
// Compute diagram bounds for scaling
|
|
337
332
|
const titleOffset = parsed.title ? 40 : 0;
|
|
338
333
|
const legendH = parsed.tagGroups.length > 0 ? LEGEND_HEIGHT + 8 : 0;
|
|
334
|
+
|
|
335
|
+
// Account for group label zone extensions (renderer-only, not in layout.height)
|
|
336
|
+
const groupLabelsSet = new Set(layout.groups.map((g) => g.label));
|
|
337
|
+
let labelZoneExtension = 0;
|
|
338
|
+
for (const group of parsed.groups) {
|
|
339
|
+
if (group.children.some((c) => groupLabelsSet.has(c))) {
|
|
340
|
+
labelZoneExtension += GROUP_LABEL_ZONE;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
339
344
|
const contentW = layout.width;
|
|
340
|
-
const contentH = layout.height + titleOffset + legendH;
|
|
345
|
+
const contentH = layout.height + titleOffset + legendH + labelZoneExtension;
|
|
341
346
|
|
|
342
347
|
const scaleX = width / (contentW + DIAGRAM_PADDING * 2);
|
|
343
348
|
const scaleY = height / (contentH + DIAGRAM_PADDING * 2);
|
|
@@ -390,10 +395,29 @@ export function renderBoxesAndLines(
|
|
|
390
395
|
}
|
|
391
396
|
ensureArrowMarkers(defs, arrowColors);
|
|
392
397
|
|
|
393
|
-
// ── Render groups (bottom layer)
|
|
394
|
-
|
|
398
|
+
// ── Render groups (bottom layer, largest first for nesting) ──
|
|
399
|
+
const sortedGroups = [...layout.groups].sort(
|
|
400
|
+
(a, b) => b.width * b.height - a.width * a.height
|
|
401
|
+
);
|
|
402
|
+
// Identify groups that contain sub-groups — only those need extra label space
|
|
403
|
+
const groupLabels = new Set(layout.groups.map((g) => g.label));
|
|
404
|
+
const hasSubGroups = new Set<string>();
|
|
405
|
+
for (const group of parsed.groups) {
|
|
406
|
+
for (const child of group.children) {
|
|
407
|
+
if (groupLabels.has(child)) hasSubGroups.add(group.label);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
for (const group of sortedGroups) {
|
|
395
412
|
const gx = group.x - group.width / 2;
|
|
396
|
-
|
|
413
|
+
// Only extend top for groups that contain sub-groups (dagre under-pads these)
|
|
414
|
+
const needsExtra = !group.collapsed && hasSubGroups.has(group.label);
|
|
415
|
+
const gy = needsExtra
|
|
416
|
+
? group.y - group.height / 2 - GROUP_LABEL_ZONE
|
|
417
|
+
: group.y - group.height / 2;
|
|
418
|
+
const groupHeight = needsExtra
|
|
419
|
+
? group.height + GROUP_LABEL_ZONE
|
|
420
|
+
: group.height;
|
|
397
421
|
|
|
398
422
|
const groupG = diagramG
|
|
399
423
|
.append('g')
|
|
@@ -464,7 +488,7 @@ export function renderBoxesAndLines(
|
|
|
464
488
|
.attr('x', gx)
|
|
465
489
|
.attr('y', gy)
|
|
466
490
|
.attr('width', group.width)
|
|
467
|
-
.attr('height',
|
|
491
|
+
.attr('height', groupHeight)
|
|
468
492
|
.attr('rx', GROUP_RX)
|
|
469
493
|
.attr('ry', GROUP_RX)
|
|
470
494
|
.attr('fill', mix(palette.surface, palette.bg, 40))
|
|
@@ -516,8 +540,73 @@ export function renderBoxesAndLines(
|
|
|
516
540
|
if (isHidden) continue;
|
|
517
541
|
}
|
|
518
542
|
|
|
519
|
-
//
|
|
520
|
-
|
|
543
|
+
// Self-loop: render as a smooth circular arc below the node
|
|
544
|
+
if (le.source === le.target) {
|
|
545
|
+
const nodeLayout = layoutNodeMap.get(le.source);
|
|
546
|
+
if (nodeLayout) {
|
|
547
|
+
const edgeG = diagramG
|
|
548
|
+
.append('g')
|
|
549
|
+
.attr('class', 'bl-edge-group')
|
|
550
|
+
.attr('data-line-number', String(le.lineNumber));
|
|
551
|
+
edgeGroups.set(i, edgeG as unknown as D3G);
|
|
552
|
+
|
|
553
|
+
const markerId = `bl-arrow-${color.replace('#', '')}`;
|
|
554
|
+
const cx = nodeLayout.x;
|
|
555
|
+
const cy = nodeLayout.y;
|
|
556
|
+
const hw = nodeLayout.width / 2;
|
|
557
|
+
const hh = nodeLayout.height / 2;
|
|
558
|
+
const pad = 20; // clearance from node edge
|
|
559
|
+
|
|
560
|
+
// Arc exits from bottom of right side, swings wide, returns to right of bottom side
|
|
561
|
+
const startX = cx + hw;
|
|
562
|
+
const startY = cy + hh * 0.4;
|
|
563
|
+
const endX = cx + hw * 0.4;
|
|
564
|
+
const endY = cy + hh;
|
|
565
|
+
|
|
566
|
+
// Control points swing far out to create a smooth circular arc
|
|
567
|
+
const cp1x = startX + hw + pad;
|
|
568
|
+
const cp1y = startY;
|
|
569
|
+
const cp2x = endX;
|
|
570
|
+
const cp2y = endY + hh + pad;
|
|
571
|
+
|
|
572
|
+
edgeG
|
|
573
|
+
.append('path')
|
|
574
|
+
.attr('class', 'bl-edge')
|
|
575
|
+
.attr(
|
|
576
|
+
'd',
|
|
577
|
+
`M ${startX} ${startY} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${endX} ${endY}`
|
|
578
|
+
)
|
|
579
|
+
.attr('fill', 'none')
|
|
580
|
+
.attr('stroke', color)
|
|
581
|
+
.attr('stroke-width', EDGE_STROKE_WIDTH)
|
|
582
|
+
.attr('marker-end', `url(#${markerId})`);
|
|
583
|
+
}
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Parallel edge fan: construct explicit 5-point geometry so lines
|
|
588
|
+
// bundle at ports and visibly spread apart in the middle.
|
|
589
|
+
let points: { x: number; y: number }[];
|
|
590
|
+
if (le.yOffset !== 0 && le.parallelCount > 1) {
|
|
591
|
+
const srcLayout = layoutNodeMap.get(le.source);
|
|
592
|
+
const tgtLayout = layoutNodeMap.get(le.target);
|
|
593
|
+
const srcY = srcLayout?.y ?? le.points[0]?.y ?? 0;
|
|
594
|
+
const tgtY = tgtLayout?.y ?? le.points[le.points.length - 1]?.y ?? 0;
|
|
595
|
+
const srcX = le.points[0]?.x ?? 0;
|
|
596
|
+
const tgtX = le.points[le.points.length - 1]?.x ?? 0;
|
|
597
|
+
const midX = (srcX + tgtX) / 2;
|
|
598
|
+
const midY = (srcY + tgtY) / 2;
|
|
599
|
+
|
|
600
|
+
points = [
|
|
601
|
+
{ x: srcX, y: srcY }, // port (bundled)
|
|
602
|
+
{ x: srcX + (midX - srcX) * 0.3, y: srcY + le.yOffset * 0.5 }, // separate
|
|
603
|
+
{ x: midX, y: midY + le.yOffset }, // full spread
|
|
604
|
+
{ x: tgtX - (tgtX - midX) * 0.3, y: tgtY + le.yOffset * 0.5 }, // converge
|
|
605
|
+
{ x: tgtX, y: tgtY }, // port (bundled)
|
|
606
|
+
];
|
|
607
|
+
} else {
|
|
608
|
+
points = le.points.map((p) => ({ x: p.x, y: p.y }));
|
|
609
|
+
}
|
|
521
610
|
if (points.length < 2) continue;
|
|
522
611
|
|
|
523
612
|
const edgeG = diagramG
|
|
@@ -527,11 +616,7 @@ export function renderBoxesAndLines(
|
|
|
527
616
|
edgeGroups.set(i, edgeG as unknown as D3G);
|
|
528
617
|
|
|
529
618
|
const markerId = `bl-arrow-${color.replace('#', '')}`;
|
|
530
|
-
const gen =
|
|
531
|
-
? lineGeneratorLinear
|
|
532
|
-
: parsed.direction === 'TB'
|
|
533
|
-
? lineGeneratorTB
|
|
534
|
-
: lineGeneratorLR;
|
|
619
|
+
const gen = parsed.direction === 'TB' ? lineGeneratorTB : lineGeneratorLR;
|
|
535
620
|
const path = edgeG
|
|
536
621
|
.append('path')
|
|
537
622
|
.attr('class', 'bl-edge')
|
|
@@ -546,14 +631,25 @@ export function renderBoxesAndLines(
|
|
|
546
631
|
path.attr('marker-start', `url(#${revId})`);
|
|
547
632
|
}
|
|
548
633
|
|
|
549
|
-
// Edge label
|
|
634
|
+
// Edge label — for parallel edges, place relative to each line:
|
|
635
|
+
// negative offset (top line) → label above, zero → on line, positive → below
|
|
550
636
|
if (le.label && le.labelX != null && le.labelY != null) {
|
|
551
637
|
const lw = le.label.length * EDGE_LABEL_FONT_SIZE * CHAR_WIDTH_RATIO;
|
|
638
|
+
const labelH = EDGE_LABEL_FONT_SIZE + 6;
|
|
639
|
+
let ly: number;
|
|
640
|
+
if (le.parallelCount > 1 && le.yOffset !== 0) {
|
|
641
|
+
// Position label on the line at midpoint, shifted above/below based on offset sign
|
|
642
|
+
const lineY = le.labelY + 10 + le.yOffset; // +10 to undo the -10 in layout
|
|
643
|
+
const labelShift = le.yOffset < 0 ? -labelH : labelH;
|
|
644
|
+
ly = lineY + labelShift * 0.5;
|
|
645
|
+
} else {
|
|
646
|
+
ly = le.labelY + le.yOffset;
|
|
647
|
+
}
|
|
552
648
|
labelPositions.push({
|
|
553
649
|
x: le.labelX,
|
|
554
|
-
y:
|
|
650
|
+
y: ly,
|
|
555
651
|
width: lw + 8,
|
|
556
|
-
height:
|
|
652
|
+
height: labelH,
|
|
557
653
|
idx: i,
|
|
558
654
|
});
|
|
559
655
|
}
|
|
@@ -743,10 +839,12 @@ export function renderBoxesAndLinesForExport(
|
|
|
743
839
|
options?: {
|
|
744
840
|
exportDims?: { width: number; height: number };
|
|
745
841
|
activeTagGroup?: string | null;
|
|
842
|
+
hiddenTagValues?: Map<string, Set<string>>;
|
|
746
843
|
}
|
|
747
844
|
): void {
|
|
748
845
|
renderBoxesAndLines(container, parsed, layout, palette, isDark, {
|
|
749
846
|
exportDims: options?.exportDims,
|
|
750
847
|
activeTagGroup: options?.activeTagGroup,
|
|
848
|
+
hiddenTagValues: options?.hiddenTagValues,
|
|
751
849
|
});
|
|
752
850
|
}
|
package/src/completion.ts
CHANGED
|
@@ -335,6 +335,20 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
335
335
|
hide: { description: 'Hide tag:value pairs' },
|
|
336
336
|
}),
|
|
337
337
|
],
|
|
338
|
+
[
|
|
339
|
+
'mindmap',
|
|
340
|
+
withGlobals({
|
|
341
|
+
'hide-descriptions': { description: 'Hide node descriptions' },
|
|
342
|
+
'active-tag': { description: 'Active tag group name' },
|
|
343
|
+
}),
|
|
344
|
+
],
|
|
345
|
+
[
|
|
346
|
+
'wireframe',
|
|
347
|
+
withGlobals({
|
|
348
|
+
mobile: { description: 'Use mobile (narrow vertical) layout' },
|
|
349
|
+
'active-tag': { description: 'Active tag group name' },
|
|
350
|
+
}),
|
|
351
|
+
],
|
|
338
352
|
]);
|
|
339
353
|
|
|
340
354
|
// ============================================================
|
|
@@ -378,6 +392,8 @@ const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
|
378
392
|
infra: 'Infrastructure diagram',
|
|
379
393
|
gantt: 'Gantt chart',
|
|
380
394
|
'boxes-and-lines': 'Boxes and lines diagram',
|
|
395
|
+
mindmap: 'Mindmap diagram',
|
|
396
|
+
wireframe: 'UI wireframe diagram',
|
|
381
397
|
};
|
|
382
398
|
|
|
383
399
|
/** All chart types with descriptions, for chart type autocomplete. Excludes `multi-line` alias. */
|
|
@@ -511,6 +527,16 @@ export const PIPE_METADATA = new Map<
|
|
|
511
527
|
edge: {},
|
|
512
528
|
},
|
|
513
529
|
],
|
|
530
|
+
[
|
|
531
|
+
'mindmap',
|
|
532
|
+
{
|
|
533
|
+
node: {
|
|
534
|
+
description: { description: 'Node description text' },
|
|
535
|
+
collapsed: { description: 'Collapse node subtree by default' },
|
|
536
|
+
},
|
|
537
|
+
edge: {},
|
|
538
|
+
},
|
|
539
|
+
],
|
|
514
540
|
]);
|
|
515
541
|
|
|
516
542
|
// ============================================================
|
package/src/d3.ts
CHANGED
|
@@ -6492,12 +6492,7 @@ export async function renderForExport(
|
|
|
6492
6492
|
content: string,
|
|
6493
6493
|
theme: 'light' | 'dark' | 'transparent',
|
|
6494
6494
|
palette?: PaletteColors,
|
|
6495
|
-
|
|
6496
|
-
collapsedNodes?: Set<string>;
|
|
6497
|
-
activeTagGroup?: string | null;
|
|
6498
|
-
hiddenAttributes?: Set<string>;
|
|
6499
|
-
swimlaneTagGroup?: string | null;
|
|
6500
|
-
},
|
|
6495
|
+
viewState?: import('./sharing').CompactViewState,
|
|
6501
6496
|
options?: {
|
|
6502
6497
|
c4Level?: 'context' | 'containers' | 'components' | 'deployment';
|
|
6503
6498
|
c4System?: string;
|
|
@@ -6521,14 +6516,14 @@ export async function renderForExport(
|
|
|
6521
6516
|
const orgParsed = parseOrg(content, effectivePalette);
|
|
6522
6517
|
if (orgParsed.error) return '';
|
|
6523
6518
|
|
|
6524
|
-
// Apply interactive collapse state when provided
|
|
6525
|
-
const collapsedNodes =
|
|
6519
|
+
// Apply interactive collapse state when provided (read from unified viewState)
|
|
6520
|
+
const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : undefined;
|
|
6526
6521
|
const activeTagGroup = resolveActiveTagGroup(
|
|
6527
6522
|
orgParsed.tagGroups,
|
|
6528
6523
|
orgParsed.options['active-tag'],
|
|
6529
|
-
|
|
6524
|
+
viewState?.tag ?? options?.tagGroup
|
|
6530
6525
|
);
|
|
6531
|
-
const hiddenAttributes =
|
|
6526
|
+
const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : undefined;
|
|
6532
6527
|
|
|
6533
6528
|
const { parsed: effectiveParsed, hiddenCounts } =
|
|
6534
6529
|
collapsedNodes && collapsedNodes.size > 0
|
|
@@ -6575,14 +6570,14 @@ export async function renderForExport(
|
|
|
6575
6570
|
const sitemapParsed = parseSitemap(content, effectivePalette);
|
|
6576
6571
|
if (sitemapParsed.error || sitemapParsed.roots.length === 0) return '';
|
|
6577
6572
|
|
|
6578
|
-
// Apply interactive collapse state when provided
|
|
6579
|
-
const collapsedNodes =
|
|
6573
|
+
// Apply interactive collapse state when provided (read from unified viewState)
|
|
6574
|
+
const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : undefined;
|
|
6580
6575
|
const activeTagGroup = resolveActiveTagGroup(
|
|
6581
6576
|
sitemapParsed.tagGroups,
|
|
6582
6577
|
sitemapParsed.options['active-tag'],
|
|
6583
|
-
|
|
6578
|
+
viewState?.tag ?? options?.tagGroup
|
|
6584
6579
|
);
|
|
6585
|
-
const hiddenAttributes =
|
|
6580
|
+
const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : undefined;
|
|
6586
6581
|
|
|
6587
6582
|
const { parsed: effectiveParsed, hiddenCounts } =
|
|
6588
6583
|
collapsedNodes && collapsedNodes.size > 0
|
|
@@ -6635,8 +6630,12 @@ export async function renderForExport(
|
|
|
6635
6630
|
activeTagGroup: resolveActiveTagGroup(
|
|
6636
6631
|
kanbanParsed.tagGroups,
|
|
6637
6632
|
kanbanParsed.options['active-tag'],
|
|
6638
|
-
options?.tagGroup
|
|
6633
|
+
viewState?.tag ?? options?.tagGroup
|
|
6639
6634
|
),
|
|
6635
|
+
currentSwimlaneGroup: viewState?.swim ?? null,
|
|
6636
|
+
collapsedLanes: viewState?.cl ? new Set(viewState.cl) : undefined,
|
|
6637
|
+
collapsedColumns: viewState?.cc ? new Set(viewState.cc) : undefined,
|
|
6638
|
+
compactMeta: viewState?.cm,
|
|
6640
6639
|
});
|
|
6641
6640
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
6642
6641
|
}
|
|
@@ -6696,22 +6695,32 @@ export async function renderForExport(
|
|
|
6696
6695
|
resolveActiveTagGroup(
|
|
6697
6696
|
erParsed.tagGroups,
|
|
6698
6697
|
erParsed.options['active-tag'],
|
|
6699
|
-
options?.tagGroup
|
|
6700
|
-
)
|
|
6698
|
+
viewState?.tag ?? options?.tagGroup
|
|
6699
|
+
),
|
|
6700
|
+
viewState?.sem
|
|
6701
6701
|
);
|
|
6702
6702
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
6703
6703
|
}
|
|
6704
6704
|
|
|
6705
6705
|
if (detectedType === 'boxes-and-lines') {
|
|
6706
6706
|
const { parseBoxesAndLines } = await import('./boxes-and-lines/parser');
|
|
6707
|
-
const { layoutBoxesAndLines } = await import('./boxes-and-lines/layout');
|
|
6708
|
-
const { renderBoxesAndLinesForExport } =
|
|
6709
|
-
await import('./boxes-and-lines/renderer');
|
|
6710
|
-
|
|
6711
6707
|
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
6712
6708
|
const blParsed = parseBoxesAndLines(content);
|
|
6713
6709
|
if (blParsed.error || blParsed.nodes.length === 0) return '';
|
|
6714
6710
|
|
|
6711
|
+
// Convert viewState.htv (Record<string, string[]>) to Map<string, Set<string>>
|
|
6712
|
+
let blHiddenTagValues: Map<string, Set<string>> | undefined;
|
|
6713
|
+
if (viewState?.htv) {
|
|
6714
|
+
blHiddenTagValues = new Map();
|
|
6715
|
+
for (const [k, v] of Object.entries(viewState.htv)) {
|
|
6716
|
+
blHiddenTagValues.set(k, new Set(v));
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
6719
|
+
|
|
6720
|
+
const { layoutBoxesAndLines } = await import('./boxes-and-lines/layout');
|
|
6721
|
+
const { renderBoxesAndLinesForExport } =
|
|
6722
|
+
await import('./boxes-and-lines/renderer');
|
|
6723
|
+
|
|
6715
6724
|
const blLayout = layoutBoxesAndLines(blParsed);
|
|
6716
6725
|
const PADDING = 20;
|
|
6717
6726
|
const titleOffset = blParsed.title ? 40 : 0;
|
|
@@ -6727,12 +6736,106 @@ export async function renderForExport(
|
|
|
6727
6736
|
theme === 'dark',
|
|
6728
6737
|
{
|
|
6729
6738
|
exportDims: { width: exportWidth, height: exportHeight },
|
|
6730
|
-
activeTagGroup: options?.tagGroup,
|
|
6739
|
+
activeTagGroup: viewState?.tag ?? options?.tagGroup,
|
|
6740
|
+
hiddenTagValues: blHiddenTagValues,
|
|
6731
6741
|
}
|
|
6732
6742
|
);
|
|
6733
6743
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
6734
6744
|
}
|
|
6735
6745
|
|
|
6746
|
+
if (detectedType === 'mindmap') {
|
|
6747
|
+
const { parseMindmap } = await import('./mindmap/parser');
|
|
6748
|
+
const { layoutMindmap } = await import('./mindmap/layout');
|
|
6749
|
+
const { collapseMindmapTree } = await import('./mindmap/collapse');
|
|
6750
|
+
const { renderMindmap } = await import('./mindmap/renderer');
|
|
6751
|
+
|
|
6752
|
+
const isDark = theme === 'dark';
|
|
6753
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
6754
|
+
|
|
6755
|
+
const mmParsed = parseMindmap(content, effectivePalette);
|
|
6756
|
+
if (mmParsed.error) return '';
|
|
6757
|
+
|
|
6758
|
+
const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : undefined;
|
|
6759
|
+
const activeTagGroup = resolveActiveTagGroup(
|
|
6760
|
+
mmParsed.tagGroups,
|
|
6761
|
+
mmParsed.options['active-tag'],
|
|
6762
|
+
viewState?.tag ?? options?.tagGroup
|
|
6763
|
+
);
|
|
6764
|
+
const hideDescriptions =
|
|
6765
|
+
mmParsed.options['hide-descriptions'] === 'true' ||
|
|
6766
|
+
viewState?.hd === true;
|
|
6767
|
+
|
|
6768
|
+
const { roots: effectiveRoots, hiddenCounts } =
|
|
6769
|
+
collapsedNodes && collapsedNodes.size > 0
|
|
6770
|
+
? collapseMindmapTree(mmParsed.roots, collapsedNodes)
|
|
6771
|
+
: { roots: mmParsed.roots, hiddenCounts: new Map<string, number>() };
|
|
6772
|
+
|
|
6773
|
+
const effectiveParsed = { ...mmParsed, roots: effectiveRoots };
|
|
6774
|
+
|
|
6775
|
+
const mmLayout = layoutMindmap(effectiveParsed, effectivePalette, {
|
|
6776
|
+
interactive: false,
|
|
6777
|
+
hiddenCounts: hiddenCounts.size > 0 ? hiddenCounts : undefined,
|
|
6778
|
+
activeTagGroup,
|
|
6779
|
+
hideDescriptions,
|
|
6780
|
+
});
|
|
6781
|
+
|
|
6782
|
+
const PADDING = 20;
|
|
6783
|
+
const titleOffset = effectiveParsed.title ? 30 : 0;
|
|
6784
|
+
const exportWidth = mmLayout.width + PADDING * 2;
|
|
6785
|
+
const exportHeight = mmLayout.height + PADDING * 2 + titleOffset;
|
|
6786
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
6787
|
+
|
|
6788
|
+
const colorByDepth = viewState?.cbd === true;
|
|
6789
|
+
|
|
6790
|
+
renderMindmap(
|
|
6791
|
+
container,
|
|
6792
|
+
effectiveParsed,
|
|
6793
|
+
mmLayout,
|
|
6794
|
+
effectivePalette,
|
|
6795
|
+
isDark,
|
|
6796
|
+
undefined,
|
|
6797
|
+
{ width: exportWidth, height: exportHeight },
|
|
6798
|
+
undefined,
|
|
6799
|
+
hideDescriptions,
|
|
6800
|
+
colorByDepth ? null : activeTagGroup,
|
|
6801
|
+
colorByDepth ? { colorByDepth: true } : undefined
|
|
6802
|
+
);
|
|
6803
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
6804
|
+
}
|
|
6805
|
+
|
|
6806
|
+
if (detectedType === 'wireframe') {
|
|
6807
|
+
const { parseWireframe } = await import('./wireframe/parser');
|
|
6808
|
+
const { layoutWireframe } = await import('./wireframe/layout');
|
|
6809
|
+
const { renderWireframe } = await import('./wireframe/renderer');
|
|
6810
|
+
|
|
6811
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
6812
|
+
const wireframeParsed = parseWireframe(content);
|
|
6813
|
+
if (
|
|
6814
|
+
wireframeParsed.error ||
|
|
6815
|
+
(wireframeParsed.roots.length === 0 &&
|
|
6816
|
+
wireframeParsed.modals.length === 0)
|
|
6817
|
+
)
|
|
6818
|
+
return '';
|
|
6819
|
+
|
|
6820
|
+
const wireframeLayout = layoutWireframe(wireframeParsed);
|
|
6821
|
+
|
|
6822
|
+
const exportWidth = wireframeLayout.width;
|
|
6823
|
+
const exportHeight = wireframeLayout.height;
|
|
6824
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
6825
|
+
|
|
6826
|
+
renderWireframe(
|
|
6827
|
+
container,
|
|
6828
|
+
wireframeParsed,
|
|
6829
|
+
wireframeLayout,
|
|
6830
|
+
effectivePalette,
|
|
6831
|
+
theme === 'dark',
|
|
6832
|
+
undefined,
|
|
6833
|
+
{ width: exportWidth, height: exportHeight },
|
|
6834
|
+
theme
|
|
6835
|
+
);
|
|
6836
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
6837
|
+
}
|
|
6838
|
+
|
|
6736
6839
|
if (detectedType === 'c4') {
|
|
6737
6840
|
const { parseC4 } = await import('./c4/parser');
|
|
6738
6841
|
const {
|
|
@@ -6748,10 +6851,18 @@ export async function renderForExport(
|
|
|
6748
6851
|
const c4Parsed = parseC4(content, effectivePalette);
|
|
6749
6852
|
if (c4Parsed.error || c4Parsed.elements.length === 0) return '';
|
|
6750
6853
|
|
|
6751
|
-
// Container/component-level rendering
|
|
6752
|
-
const c4Level =
|
|
6753
|
-
|
|
6754
|
-
|
|
6854
|
+
// Container/component-level rendering (viewState fallback for share links)
|
|
6855
|
+
const c4Level =
|
|
6856
|
+
options?.c4Level ??
|
|
6857
|
+
(viewState?.c4l as
|
|
6858
|
+
| 'context'
|
|
6859
|
+
| 'containers'
|
|
6860
|
+
| 'components'
|
|
6861
|
+
| 'deployment'
|
|
6862
|
+
| undefined) ??
|
|
6863
|
+
'context';
|
|
6864
|
+
const c4System = options?.c4System ?? viewState?.c4s;
|
|
6865
|
+
const c4Container = options?.c4Container ?? viewState?.c4c;
|
|
6755
6866
|
|
|
6756
6867
|
const c4Layout =
|
|
6757
6868
|
c4Level === 'deployment'
|
|
@@ -6788,7 +6899,7 @@ export async function renderForExport(
|
|
|
6788
6899
|
resolveActiveTagGroup(
|
|
6789
6900
|
c4Parsed.tagGroups,
|
|
6790
6901
|
c4Parsed.options['active-tag'],
|
|
6791
|
-
options?.tagGroup
|
|
6902
|
+
viewState?.tag ?? options?.tagGroup
|
|
6792
6903
|
)
|
|
6793
6904
|
);
|
|
6794
6905
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -6834,7 +6945,7 @@ export async function renderForExport(
|
|
|
6834
6945
|
const activeTagGroup = resolveActiveTagGroup(
|
|
6835
6946
|
infraParsed.tagGroups,
|
|
6836
6947
|
infraParsed.options['active-tag'],
|
|
6837
|
-
options?.tagGroup
|
|
6948
|
+
viewState?.tag ?? options?.tagGroup
|
|
6838
6949
|
);
|
|
6839
6950
|
|
|
6840
6951
|
const titleOffset = infraParsed.title ? 40 : 0;
|
|
@@ -6860,7 +6971,8 @@ export async function renderForExport(
|
|
|
6860
6971
|
false,
|
|
6861
6972
|
null,
|
|
6862
6973
|
null,
|
|
6863
|
-
true
|
|
6974
|
+
true,
|
|
6975
|
+
viewState?.cg ? new Set(viewState.cg) : null
|
|
6864
6976
|
);
|
|
6865
6977
|
// Restore explicit pixel dimensions for resvg (renderer uses 100%/viewBox for app scaling)
|
|
6866
6978
|
const infraSvg = container.querySelector('svg');
|
|
@@ -6890,7 +7002,16 @@ export async function renderForExport(
|
|
|
6890
7002
|
resolved,
|
|
6891
7003
|
effectivePalette,
|
|
6892
7004
|
theme === 'dark',
|
|
6893
|
-
|
|
7005
|
+
{
|
|
7006
|
+
collapsedGroups: viewState?.cg ? new Set(viewState.cg) : undefined,
|
|
7007
|
+
currentSwimlaneGroup: viewState?.swim ?? undefined,
|
|
7008
|
+
collapsedLanes: viewState?.cl ? new Set(viewState.cl) : undefined,
|
|
7009
|
+
currentActiveGroup: resolveActiveTagGroup(
|
|
7010
|
+
resolved.tagGroups,
|
|
7011
|
+
resolved.options.activeTag ?? undefined,
|
|
7012
|
+
viewState?.tag ?? options?.tagGroup
|
|
7013
|
+
),
|
|
7014
|
+
},
|
|
6894
7015
|
{ width: EXPORT_W, height: EXPORT_H }
|
|
6895
7016
|
);
|
|
6896
7017
|
return finalizeSvgExport(container, theme, effectivePalette);
|
|
@@ -6991,9 +7112,9 @@ export async function renderForExport(
|
|
|
6991
7112
|
resolveActiveTagGroup(
|
|
6992
7113
|
parsed.timelineTagGroups,
|
|
6993
7114
|
undefined,
|
|
6994
|
-
|
|
7115
|
+
viewState?.tag ?? options?.tagGroup
|
|
6995
7116
|
),
|
|
6996
|
-
|
|
7117
|
+
viewState?.swim
|
|
6997
7118
|
);
|
|
6998
7119
|
} else if (parsed.type === 'venn') {
|
|
6999
7120
|
renderVenn(container, parsed, effectivePalette, isDark, undefined, dims);
|
package/src/dgmo-router.ts
CHANGED
|
@@ -17,6 +17,8 @@ import { looksLikeSitemap, parseSitemap } from './sitemap/parser';
|
|
|
17
17
|
import { parseInfra } from './infra/parser';
|
|
18
18
|
import { parseGantt } from './gantt/parser';
|
|
19
19
|
import { parseBoxesAndLines } from './boxes-and-lines/parser';
|
|
20
|
+
import { parseMindmap } from './mindmap/parser';
|
|
21
|
+
import { parseWireframe } from './wireframe/parser';
|
|
20
22
|
import { parseFirstLine } from './utils/parsing';
|
|
21
23
|
import { makeDgmoError, suggest } from './diagnostics';
|
|
22
24
|
import type { DgmoError } from './diagnostics';
|
|
@@ -149,6 +151,8 @@ const DIAGRAM_TYPES = new Set([
|
|
|
149
151
|
'infra',
|
|
150
152
|
'gantt',
|
|
151
153
|
'boxes-and-lines',
|
|
154
|
+
'mindmap',
|
|
155
|
+
'wireframe',
|
|
152
156
|
]);
|
|
153
157
|
const EXTENDED_CHART_TYPES = new Set([
|
|
154
158
|
'scatter',
|
|
@@ -228,6 +232,8 @@ const PARSE_DISPATCH = new Map<
|
|
|
228
232
|
['infra', (c) => parseInfra(c)],
|
|
229
233
|
['gantt', (c) => parseGantt(c)],
|
|
230
234
|
['boxes-and-lines', (c) => parseBoxesAndLines(c)],
|
|
235
|
+
['mindmap', (c) => parseMindmap(c)],
|
|
236
|
+
['wireframe', (c) => parseWireframe(c)],
|
|
231
237
|
]);
|
|
232
238
|
|
|
233
239
|
/**
|
package/src/editor/keywords.ts
CHANGED
|
@@ -13,6 +13,7 @@ export const CHART_TYPES = new Set([
|
|
|
13
13
|
'infra',
|
|
14
14
|
'gantt',
|
|
15
15
|
'boxes-and-lines',
|
|
16
|
+
'wireframe',
|
|
16
17
|
// Data chart types
|
|
17
18
|
'bar',
|
|
18
19
|
'line',
|
|
@@ -170,6 +171,17 @@ export const CONTROL_KEYWORDS = new Set([
|
|
|
170
171
|
'loop',
|
|
171
172
|
'parallel',
|
|
172
173
|
'note',
|
|
174
|
+
// Wireframe elements
|
|
175
|
+
'nav',
|
|
176
|
+
'tabs',
|
|
177
|
+
'table',
|
|
178
|
+
'image',
|
|
179
|
+
'modal',
|
|
180
|
+
'skeleton',
|
|
181
|
+
'alert',
|
|
182
|
+
'progress',
|
|
183
|
+
'chart',
|
|
184
|
+
'mobile',
|
|
173
185
|
]);
|
|
174
186
|
|
|
175
187
|
/** Status keywords — kanban. */
|