@markdy/core 1.0.7 → 1.0.9
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/index.d.ts +1 -1
- package/dist/index.js +251 -96
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
type LayoutDirection = "LR" | "RL" | "TB" | "BT";
|
|
6
6
|
type EdgeKind = "request" | "response" | "event" | "dependency";
|
|
7
7
|
type DiagramType = "architecture" | "flowchart" | "tree" | "state" | "sequence" | "constellation" | "loop" | "flywheel" | "medallion" | "quadrant" | "swimlane" | "pyramid" | "radar" | "timeline" | "gantt" | "venn" | "layers" | "nested";
|
|
8
|
-
type NodeShape = "card" | "rounded" | "diamond" | "circle" | "pill" | "terminal";
|
|
8
|
+
type NodeShape = "card" | "rounded" | "diamond" | "circle" | "pill" | "terminal" | "container";
|
|
9
9
|
type SceneMeta = {
|
|
10
10
|
title?: string;
|
|
11
11
|
width: number;
|
package/dist/index.js
CHANGED
|
@@ -489,12 +489,12 @@ function canonicalNodeKind(kind) {
|
|
|
489
489
|
}
|
|
490
490
|
|
|
491
491
|
// src/compiler.ts
|
|
492
|
-
var SAFE =
|
|
493
|
-
var TITLE_BAND =
|
|
494
|
-
var NODE_W =
|
|
495
|
-
var NODE_H =
|
|
496
|
-
var VENN_NODE_SIZE =
|
|
497
|
-
var GROUP_PAD =
|
|
492
|
+
var SAFE = 48;
|
|
493
|
+
var TITLE_BAND = 84;
|
|
494
|
+
var NODE_W = 180;
|
|
495
|
+
var NODE_H = 76;
|
|
496
|
+
var VENN_NODE_SIZE = 220;
|
|
497
|
+
var GROUP_PAD = 28;
|
|
498
498
|
var DEFAULTS = {
|
|
499
499
|
show: 0.35,
|
|
500
500
|
hide: 0.35,
|
|
@@ -594,7 +594,7 @@ function assignRanks(nodeIds, edges, direction) {
|
|
|
594
594
|
return ranks;
|
|
595
595
|
}
|
|
596
596
|
function snapGrid(n) {
|
|
597
|
-
return Math.round(n /
|
|
597
|
+
return Math.round(n / 4) * 4;
|
|
598
598
|
}
|
|
599
599
|
function layoutRanked(ast, edges, opts) {
|
|
600
600
|
const nodeIds = Object.keys(ast.nodes);
|
|
@@ -607,31 +607,106 @@ function layoutRanked(ast, edges, opts) {
|
|
|
607
607
|
if (!byRank.has(r)) byRank.set(r, []);
|
|
608
608
|
byRank.get(r).push(id);
|
|
609
609
|
}
|
|
610
|
-
|
|
610
|
+
const nodeGroupIndex = /* @__PURE__ */ new Map();
|
|
611
|
+
const groupIds = Object.keys(ast.groups);
|
|
612
|
+
groupIds.forEach((gId, gIdx) => {
|
|
613
|
+
for (const memberId of ast.groups[gId].members) {
|
|
614
|
+
nodeGroupIndex.set(memberId, gIdx);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
for (const ids of byRank.values()) {
|
|
618
|
+
ids.sort((a, b) => {
|
|
619
|
+
const ga = nodeGroupIndex.has(a) ? nodeGroupIndex.get(a) : 9999;
|
|
620
|
+
const gb = nodeGroupIndex.has(b) ? nodeGroupIndex.get(b) : 9999;
|
|
621
|
+
if (ga !== gb) return ga - gb;
|
|
622
|
+
return a.localeCompare(b);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
611
625
|
const isVertical = direction === "TB" || direction === "BT";
|
|
612
626
|
const contentW = ast.meta.width - SAFE * 2;
|
|
613
627
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
614
628
|
const maxRank = Math.max(...byRank.keys(), 0);
|
|
615
629
|
const rankCount = maxRank + 1;
|
|
630
|
+
const maxInRank = Math.max(...[...byRank.values()].map((v) => v.length), 1);
|
|
616
631
|
const nodes = [];
|
|
632
|
+
if (opts.columnLayout) {
|
|
633
|
+
const count = nodeIds.length;
|
|
634
|
+
const colSpacing = Math.max(NODE_W + 32, contentW / Math.max(count, 1));
|
|
635
|
+
const totalW2 = (count - 1) * colSpacing + NODE_W;
|
|
636
|
+
const startX2 = SAFE + Math.max(0, (contentW - totalW2) / 2);
|
|
637
|
+
nodeIds.forEach((id, idx) => {
|
|
638
|
+
const decl = ast.nodes[id];
|
|
639
|
+
const role = nodeRole(decl.kind);
|
|
640
|
+
const x = startX2 + idx * colSpacing;
|
|
641
|
+
const y = TITLE_BAND + 32;
|
|
642
|
+
nodes.push({
|
|
643
|
+
id,
|
|
644
|
+
kind: decl.kind,
|
|
645
|
+
role,
|
|
646
|
+
label: decl.label,
|
|
647
|
+
x: snapGrid(x),
|
|
648
|
+
y: snapGrid(y),
|
|
649
|
+
width: NODE_W,
|
|
650
|
+
height: NODE_H,
|
|
651
|
+
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
652
|
+
props: decl.props,
|
|
653
|
+
opacity: 0,
|
|
654
|
+
shape: nodeShape(decl.kind, dtype),
|
|
655
|
+
focal: decl.props.focal === true || decl.props.accent === true,
|
|
656
|
+
column: idx
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
return nodes;
|
|
660
|
+
}
|
|
661
|
+
if (isVertical) {
|
|
662
|
+
const rowGap = Math.max(NODE_H + 40, Math.min(180, contentH / Math.max(rankCount, 1)));
|
|
663
|
+
const totalH = (rankCount - 1) * rowGap + NODE_H;
|
|
664
|
+
const startY = TITLE_BAND + Math.max(0, (contentH - totalH) / 2);
|
|
665
|
+
for (const [rank, ids] of [...byRank.entries()].sort((a, b) => a[0] - b[0])) {
|
|
666
|
+
const rowCount = ids.length;
|
|
667
|
+
const colSpacing = Math.max(NODE_W + 36, Math.min(260, contentW / Math.max(rowCount, 1)));
|
|
668
|
+
const rankW = (rowCount - 1) * colSpacing + NODE_W;
|
|
669
|
+
const rankStartX = SAFE + Math.max(0, (contentW - rankW) / 2);
|
|
670
|
+
const y = startY + rank * rowGap;
|
|
671
|
+
ids.forEach((id, idx) => {
|
|
672
|
+
const decl = ast.nodes[id];
|
|
673
|
+
const role = nodeRole(decl.kind);
|
|
674
|
+
const x = rankStartX + idx * colSpacing;
|
|
675
|
+
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
676
|
+
nodes.push({
|
|
677
|
+
id,
|
|
678
|
+
kind: decl.kind,
|
|
679
|
+
role,
|
|
680
|
+
label: decl.label,
|
|
681
|
+
x: snapGrid(x),
|
|
682
|
+
y: snapGrid(y),
|
|
683
|
+
width: NODE_W,
|
|
684
|
+
height: NODE_H,
|
|
685
|
+
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
686
|
+
props: decl.props,
|
|
687
|
+
opacity: 0,
|
|
688
|
+
shape: nodeShape(decl.kind, dtype),
|
|
689
|
+
focal
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
return nodes;
|
|
694
|
+
}
|
|
695
|
+
const maxPossibleStep = rankCount > 1 ? (contentW - NODE_W) / (rankCount - 1) : 0;
|
|
696
|
+
const colGap = rankCount > 1 ? Math.min(220, Math.max(50, maxPossibleStep)) : 0;
|
|
697
|
+
const totalW = (rankCount - 1) * colGap + NODE_W;
|
|
698
|
+
const startX = SAFE + Math.max(0, (contentW - totalW) / 2);
|
|
617
699
|
for (const [rank, ids] of [...byRank.entries()].sort((a, b) => a[0] - b[0])) {
|
|
618
700
|
const rowCount = ids.length;
|
|
619
|
-
const
|
|
620
|
-
|
|
701
|
+
const maxPossibleRowStep = rowCount > 1 ? (contentH - NODE_H) / (rowCount - 1) : 0;
|
|
702
|
+
const rowSpacing = rowCount > 1 ? Math.min(150, Math.max(36, maxPossibleRowStep)) : 0;
|
|
703
|
+
const colH = (rowCount - 1) * rowSpacing + NODE_H;
|
|
704
|
+
const colStartY = TITLE_BAND + Math.max(0, (contentH - colH) / 2);
|
|
705
|
+
const x = startX + rank * colGap;
|
|
706
|
+
ids.forEach((id, idx) => {
|
|
621
707
|
const decl = ast.nodes[id];
|
|
622
708
|
const role = nodeRole(decl.kind);
|
|
623
|
-
|
|
624
|
-
let y;
|
|
625
|
-
if (opts.columnLayout) {
|
|
626
|
-
x = SAFE + contentW / (rowCount + 1) * (idx + 1) - NODE_W / 2;
|
|
627
|
-
y = TITLE_BAND + 48;
|
|
628
|
-
} else if (isVertical) {
|
|
629
|
-
x = SAFE + contentW / (rowCount + 1) * (idx + 1) - NODE_W / 2;
|
|
630
|
-
y = TITLE_BAND + contentH / Math.max(rankCount, 1) * rank + (contentH / Math.max(rankCount, 1) - NODE_H) / 2;
|
|
631
|
-
} else {
|
|
632
|
-
x = SAFE + contentW / Math.max(rankCount, 1) * rank + (contentW / Math.max(rankCount, 1) - NODE_W) / 2;
|
|
633
|
-
y = TITLE_BAND + contentH / (rowCount + 1) * (idx + 1) - NODE_H / 2;
|
|
634
|
-
}
|
|
709
|
+
const y = colStartY + idx * rowSpacing;
|
|
635
710
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
636
711
|
nodes.push({
|
|
637
712
|
id,
|
|
@@ -646,8 +721,7 @@ function layoutRanked(ast, edges, opts) {
|
|
|
646
721
|
props: decl.props,
|
|
647
722
|
opacity: 0,
|
|
648
723
|
shape: nodeShape(decl.kind, dtype),
|
|
649
|
-
focal
|
|
650
|
-
column: opts.columnLayout ? idx : void 0
|
|
724
|
+
focal
|
|
651
725
|
});
|
|
652
726
|
});
|
|
653
727
|
}
|
|
@@ -655,6 +729,7 @@ function layoutRanked(ast, edges, opts) {
|
|
|
655
729
|
}
|
|
656
730
|
function layoutTree(ast, edges) {
|
|
657
731
|
const nodeIds = Object.keys(ast.nodes);
|
|
732
|
+
if (nodeIds.length === 0) return [];
|
|
658
733
|
const children = /* @__PURE__ */ new Map();
|
|
659
734
|
const parent = /* @__PURE__ */ new Map();
|
|
660
735
|
for (const id of nodeIds) children.set(id, []);
|
|
@@ -665,49 +740,95 @@ function layoutTree(ast, edges) {
|
|
|
665
740
|
parent.set(e.to, e.from);
|
|
666
741
|
children.get(e.from).push(e.to);
|
|
667
742
|
}
|
|
668
|
-
const
|
|
743
|
+
const roots = nodeIds.filter((id) => !parent.has(id));
|
|
744
|
+
if (roots.length === 0) roots.push(nodeIds[0]);
|
|
669
745
|
const depth = /* @__PURE__ */ new Map();
|
|
670
|
-
const queue = [
|
|
671
|
-
depth.set(
|
|
746
|
+
const queue = [...roots];
|
|
747
|
+
for (const r of roots) depth.set(r, 0);
|
|
672
748
|
while (queue.length) {
|
|
673
749
|
const id = queue.shift();
|
|
750
|
+
const d = depth.get(id);
|
|
674
751
|
for (const child of children.get(id) ?? []) {
|
|
675
|
-
if (depth.has(child))
|
|
676
|
-
|
|
677
|
-
|
|
752
|
+
if (!depth.has(child)) {
|
|
753
|
+
depth.set(child, d + 1);
|
|
754
|
+
queue.push(child);
|
|
755
|
+
}
|
|
678
756
|
}
|
|
679
757
|
}
|
|
680
758
|
for (const id of nodeIds) if (!depth.has(id)) depth.set(id, 0);
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
759
|
+
const maxDepth = Math.max(...depth.values(), 0);
|
|
760
|
+
const subtreeSpan = /* @__PURE__ */ new Map();
|
|
761
|
+
const minLeafWidth = NODE_W + 48;
|
|
762
|
+
function measureSubtree(id) {
|
|
763
|
+
const kids = children.get(id) ?? [];
|
|
764
|
+
if (kids.length === 0) {
|
|
765
|
+
subtreeSpan.set(id, minLeafWidth);
|
|
766
|
+
return minLeafWidth;
|
|
767
|
+
}
|
|
768
|
+
let total = 0;
|
|
769
|
+
for (const kid of kids) {
|
|
770
|
+
total += measureSubtree(kid);
|
|
771
|
+
}
|
|
772
|
+
const span = Math.max(minLeafWidth, total);
|
|
773
|
+
subtreeSpan.set(id, span);
|
|
774
|
+
return span;
|
|
775
|
+
}
|
|
776
|
+
let totalRootsWidth = 0;
|
|
777
|
+
for (const r of roots) {
|
|
778
|
+
totalRootsWidth += measureSubtree(r);
|
|
686
779
|
}
|
|
687
|
-
for (const ids of byDepth.values()) ids.sort();
|
|
688
780
|
const contentW = ast.meta.width - SAFE * 2;
|
|
689
781
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
690
|
-
const
|
|
782
|
+
const treeStartX = SAFE + Math.max(0, (contentW - totalRootsWidth) / 2);
|
|
783
|
+
const levelHeight = Math.max(NODE_H + 48, Math.min(160, contentH / Math.max(maxDepth + 1, 1)));
|
|
784
|
+
const totalTreeHeight = maxDepth * levelHeight + NODE_H;
|
|
785
|
+
const treeStartY = TITLE_BAND + Math.max(16, (contentH - totalTreeHeight) / 2);
|
|
786
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
787
|
+
function positionSubtree(id, leftX) {
|
|
788
|
+
const d = depth.get(id) ?? 0;
|
|
789
|
+
const y = treeStartY + d * levelHeight;
|
|
790
|
+
const kids = children.get(id) ?? [];
|
|
791
|
+
const span = subtreeSpan.get(id) ?? minLeafWidth;
|
|
792
|
+
if (kids.length === 0) {
|
|
793
|
+
const centerX = leftX + span / 2;
|
|
794
|
+
nodePositions.set(id, { x: centerX - NODE_W / 2, y });
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
let curX = leftX;
|
|
798
|
+
for (const kid of kids) {
|
|
799
|
+
const kidSpan = subtreeSpan.get(kid) ?? minLeafWidth;
|
|
800
|
+
positionSubtree(kid, curX);
|
|
801
|
+
curX += kidSpan;
|
|
802
|
+
}
|
|
803
|
+
const firstChild = nodePositions.get(kids[0]);
|
|
804
|
+
const lastChild = nodePositions.get(kids[kids.length - 1]);
|
|
805
|
+
const parentCenterX = (firstChild.x + NODE_W / 2 + lastChild.x + NODE_W / 2) / 2;
|
|
806
|
+
nodePositions.set(id, { x: parentCenterX - NODE_W / 2, y });
|
|
807
|
+
}
|
|
808
|
+
let currentRootLeft = treeStartX;
|
|
809
|
+
for (const r of roots) {
|
|
810
|
+
const span = subtreeSpan.get(r) ?? minLeafWidth;
|
|
811
|
+
positionSubtree(r, currentRootLeft);
|
|
812
|
+
currentRootLeft += span;
|
|
813
|
+
}
|
|
691
814
|
const nodes = [];
|
|
692
|
-
for (const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
shape: "card"
|
|
710
|
-
});
|
|
815
|
+
for (const id of nodeIds) {
|
|
816
|
+
const decl = ast.nodes[id];
|
|
817
|
+
const pos = nodePositions.get(id) ?? { x: SAFE, y: TITLE_BAND };
|
|
818
|
+
nodes.push({
|
|
819
|
+
id,
|
|
820
|
+
kind: decl.kind,
|
|
821
|
+
role: nodeRole(decl.kind),
|
|
822
|
+
label: decl.label,
|
|
823
|
+
x: snapGrid(pos.x),
|
|
824
|
+
y: snapGrid(pos.y),
|
|
825
|
+
width: NODE_W,
|
|
826
|
+
height: NODE_H,
|
|
827
|
+
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
828
|
+
props: decl.props,
|
|
829
|
+
opacity: 0,
|
|
830
|
+
shape: "card",
|
|
831
|
+
focal: decl.props.focal === true || decl.props.accent === true
|
|
711
832
|
});
|
|
712
833
|
}
|
|
713
834
|
return nodes;
|
|
@@ -792,8 +913,8 @@ function layoutLoop(ast) {
|
|
|
792
913
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
793
914
|
const centerX = SAFE + contentW / 2;
|
|
794
915
|
const centerY = TITLE_BAND + contentH / 2;
|
|
795
|
-
const radiusX = Math.max(
|
|
796
|
-
const radiusY = Math.max(
|
|
916
|
+
const radiusX = Math.min(contentW * 0.4, Math.max(220, (contentW - NODE_W) / 2 - 32));
|
|
917
|
+
const radiusY = Math.min(contentH * 0.38, Math.max(150, (contentH - NODE_H) / 2 - 32));
|
|
797
918
|
const nodes = [];
|
|
798
919
|
for (const id of nodeIds) {
|
|
799
920
|
const decl = ast.nodes[id];
|
|
@@ -801,8 +922,8 @@ function layoutLoop(ast) {
|
|
|
801
922
|
let x;
|
|
802
923
|
let y;
|
|
803
924
|
if (isHub) {
|
|
804
|
-
x = centerX - NODE_W * 1.
|
|
805
|
-
y = centerY - NODE_H * 1.
|
|
925
|
+
x = centerX - NODE_W * 1.25 / 2;
|
|
926
|
+
y = centerY - NODE_H * 1.15 / 2;
|
|
806
927
|
} else {
|
|
807
928
|
const idx = stationIds.indexOf(id);
|
|
808
929
|
const angle = -Math.PI / 2 + idx * 2 * Math.PI / Math.max(stationIds.length, 1);
|
|
@@ -817,8 +938,8 @@ function layoutLoop(ast) {
|
|
|
817
938
|
label: decl.label,
|
|
818
939
|
x: snapGrid(x),
|
|
819
940
|
y: snapGrid(y),
|
|
820
|
-
width: isHub ? snapGrid(NODE_W * 1.
|
|
821
|
-
height: isHub ? snapGrid(NODE_H * 1.
|
|
941
|
+
width: isHub ? snapGrid(NODE_W * 1.25) : NODE_W,
|
|
942
|
+
height: isHub ? snapGrid(NODE_H * 1.15) : NODE_H,
|
|
822
943
|
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
823
944
|
props: decl.props,
|
|
824
945
|
opacity: 0,
|
|
@@ -850,13 +971,19 @@ function layoutMedallion(ast, edges) {
|
|
|
850
971
|
const tierCount = activeTiers.length || 1;
|
|
851
972
|
const contentW = ast.meta.width - SAFE * 2;
|
|
852
973
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
974
|
+
const colGap = Math.max(NODE_W + 48, contentW / (tierCount + 1));
|
|
975
|
+
const totalW = (tierCount - 1) * colGap + NODE_W;
|
|
976
|
+
const startX = SAFE + Math.max(0, (contentW - totalW) / 2);
|
|
853
977
|
const nodes = [];
|
|
854
978
|
activeTiers.forEach(([_, ids], colIdx) => {
|
|
855
979
|
const colCount = ids.length;
|
|
856
|
-
const colX =
|
|
980
|
+
const colX = startX + colIdx * colGap;
|
|
981
|
+
const rowSpacing = Math.max(NODE_H + 32, contentH / (colCount + 1));
|
|
982
|
+
const colH = (colCount - 1) * rowSpacing + NODE_H;
|
|
983
|
+
const startY = TITLE_BAND + Math.max(0, (contentH - colH) / 2);
|
|
857
984
|
ids.forEach((id, rowIdx) => {
|
|
858
985
|
const decl = ast.nodes[id];
|
|
859
|
-
const rowY =
|
|
986
|
+
const rowY = startY + rowIdx * rowSpacing;
|
|
860
987
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
861
988
|
nodes.push({
|
|
862
989
|
id,
|
|
@@ -916,7 +1043,7 @@ function layoutQuadrant(ast) {
|
|
|
916
1043
|
ids.forEach((id, idx) => {
|
|
917
1044
|
const decl = ast.nodes[id];
|
|
918
1045
|
const offsetCount = ids.length;
|
|
919
|
-
const rowOffset = (idx - (offsetCount - 1) / 2) * (NODE_H +
|
|
1046
|
+
const rowOffset = (idx - (offsetCount - 1) / 2) * (NODE_H + 20);
|
|
920
1047
|
const x = center.x - NODE_W / 2;
|
|
921
1048
|
const y = center.y + rowOffset - NODE_H / 2;
|
|
922
1049
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
@@ -966,9 +1093,12 @@ function layoutSwimlane(ast, edges) {
|
|
|
966
1093
|
activeLanes.forEach(([_, ids], laneIdx) => {
|
|
967
1094
|
const laneY = TITLE_BAND + laneIdx * laneHeight + (laneHeight - NODE_H) / 2;
|
|
968
1095
|
const count = ids.length;
|
|
1096
|
+
const colSpacing = Math.max(NODE_W + 48, Math.min(260, contentW / Math.max(count + 1, 2)));
|
|
1097
|
+
const totalW = (count - 1) * colSpacing + NODE_W;
|
|
1098
|
+
const startX = SAFE + Math.max(0, (contentW - totalW) / 2);
|
|
969
1099
|
ids.forEach((id, colIdx) => {
|
|
970
1100
|
const decl = ast.nodes[id];
|
|
971
|
-
const colX =
|
|
1101
|
+
const colX = startX + colIdx * colSpacing;
|
|
972
1102
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
973
1103
|
nodes.push({
|
|
974
1104
|
id,
|
|
@@ -1003,16 +1133,21 @@ function layoutPyramid(ast) {
|
|
|
1003
1133
|
const tierCount = sortedTiers.length || 1;
|
|
1004
1134
|
const contentW = ast.meta.width - SAFE * 2;
|
|
1005
1135
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
1136
|
+
const centerX = SAFE + contentW / 2;
|
|
1137
|
+
const totalH = tierCount * NODE_H + (tierCount - 1) * 24;
|
|
1138
|
+
const startY = TITLE_BAND + Math.max(16, (contentH - totalH) / 2);
|
|
1006
1139
|
const nodes = [];
|
|
1007
1140
|
sortedTiers.forEach(([_, ids], tierIdx) => {
|
|
1008
|
-
const tierY =
|
|
1009
|
-
const
|
|
1010
|
-
const tierWidth = contentW *
|
|
1011
|
-
const tierStartX = SAFE + (contentW - tierWidth) / 2;
|
|
1141
|
+
const tierY = startY + tierIdx * (NODE_H + 24);
|
|
1142
|
+
const baseFraction = 0.32 + 0.44 * tierIdx / Math.max(tierCount - 1, 1);
|
|
1143
|
+
const tierWidth = contentW * baseFraction;
|
|
1012
1144
|
const count = ids.length;
|
|
1145
|
+
const nodeW = Math.max(NODE_W, Math.min(tierWidth / count - 12, 480));
|
|
1146
|
+
const totalTierW = count * nodeW + (count - 1) * 16;
|
|
1147
|
+
const tierStartX = centerX - totalTierW / 2;
|
|
1013
1148
|
ids.forEach((id, idx) => {
|
|
1014
1149
|
const decl = ast.nodes[id];
|
|
1015
|
-
const x = tierStartX +
|
|
1150
|
+
const x = tierStartX + idx * (nodeW + 16);
|
|
1016
1151
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
1017
1152
|
nodes.push({
|
|
1018
1153
|
id,
|
|
@@ -1021,7 +1156,7 @@ function layoutPyramid(ast) {
|
|
|
1021
1156
|
label: decl.label,
|
|
1022
1157
|
x: snapGrid(x),
|
|
1023
1158
|
y: snapGrid(tierY),
|
|
1024
|
-
width:
|
|
1159
|
+
width: snapGrid(nodeW),
|
|
1025
1160
|
height: NODE_H,
|
|
1026
1161
|
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
1027
1162
|
props: decl.props,
|
|
@@ -1069,8 +1204,12 @@ function layoutGantt(ast) {
|
|
|
1069
1204
|
const nodeIds = Object.keys(ast.nodes);
|
|
1070
1205
|
if (nodeIds.length === 0) return [];
|
|
1071
1206
|
const contentW = ast.meta.width - SAFE * 2;
|
|
1072
|
-
const
|
|
1073
|
-
const
|
|
1207
|
+
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
1208
|
+
const N = nodeIds.length;
|
|
1209
|
+
const rowH = Math.min(68, Math.max(52, (contentH - 24) / Math.max(N, 1)));
|
|
1210
|
+
const barH = Math.min(44, rowH - 16);
|
|
1211
|
+
const totalH = N * rowH;
|
|
1212
|
+
const startY = TITLE_BAND + Math.max(16, (contentH - totalH) / 2);
|
|
1074
1213
|
const nodes = [];
|
|
1075
1214
|
nodeIds.forEach((id, idx) => {
|
|
1076
1215
|
const decl = ast.nodes[id];
|
|
@@ -1084,7 +1223,7 @@ function layoutGantt(ast) {
|
|
|
1084
1223
|
const unitW = contentW / totalPhases;
|
|
1085
1224
|
const x = SAFE + phase * unitW;
|
|
1086
1225
|
const w = Math.max(span * unitW - 8, NODE_W);
|
|
1087
|
-
const y =
|
|
1226
|
+
const y = startY + idx * rowH + (rowH - barH) / 2;
|
|
1088
1227
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
1089
1228
|
nodes.push({
|
|
1090
1229
|
id,
|
|
@@ -1112,18 +1251,18 @@ function layoutVenn(ast) {
|
|
|
1112
1251
|
const centerX = SAFE + contentW / 2;
|
|
1113
1252
|
const centerY = TITLE_BAND + contentH / 2;
|
|
1114
1253
|
const N = nodeIds.length;
|
|
1115
|
-
const radius = Math.min(contentW, contentH) * 0.
|
|
1254
|
+
const radius = Math.min(contentW, contentH) * 0.24;
|
|
1116
1255
|
const nodes = [];
|
|
1117
1256
|
nodeIds.forEach((id, idx) => {
|
|
1118
1257
|
const decl = ast.nodes[id];
|
|
1119
1258
|
let x, y;
|
|
1120
1259
|
if (N === 2) {
|
|
1121
|
-
x = centerX + (idx === 0 ? -radius * 0.
|
|
1260
|
+
x = centerX + (idx === 0 ? -radius * 0.7 : radius * 0.7) - VENN_NODE_SIZE / 2;
|
|
1122
1261
|
y = centerY - VENN_NODE_SIZE / 2;
|
|
1123
1262
|
} else {
|
|
1124
1263
|
const angle = -Math.PI / 2 + idx * 2 * Math.PI / N;
|
|
1125
|
-
x = centerX + radius * 0.
|
|
1126
|
-
y = centerY + radius * 0.
|
|
1264
|
+
x = centerX + radius * 0.85 * Math.cos(angle) - VENN_NODE_SIZE / 2;
|
|
1265
|
+
y = centerY + radius * 0.85 * Math.sin(angle) - VENN_NODE_SIZE / 2;
|
|
1127
1266
|
}
|
|
1128
1267
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
1129
1268
|
nodes.push({
|
|
@@ -1150,22 +1289,24 @@ function layoutLayers(ast) {
|
|
|
1150
1289
|
const contentW = ast.meta.width - SAFE * 2;
|
|
1151
1290
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
1152
1291
|
const N = nodeIds.length;
|
|
1153
|
-
const layerH = Math.min(Math.max((contentH - (N - 1) *
|
|
1154
|
-
const totalH = N * layerH + (N - 1) *
|
|
1292
|
+
const layerH = Math.min(Math.max((contentH - (N - 1) * 14) / N, 52), 68);
|
|
1293
|
+
const totalH = N * layerH + (N - 1) * 14;
|
|
1155
1294
|
const startY = TITLE_BAND + (contentH - totalH) / 2;
|
|
1295
|
+
const layerW = Math.min(contentW, 880);
|
|
1296
|
+
const startX = SAFE + (contentW - layerW) / 2;
|
|
1156
1297
|
const nodes = [];
|
|
1157
1298
|
nodeIds.forEach((id, idx) => {
|
|
1158
1299
|
const decl = ast.nodes[id];
|
|
1159
|
-
const y = startY + idx * (layerH +
|
|
1300
|
+
const y = startY + idx * (layerH + 14);
|
|
1160
1301
|
const focal = decl.props.focal === true || decl.props.accent === true;
|
|
1161
1302
|
nodes.push({
|
|
1162
1303
|
id,
|
|
1163
1304
|
kind: decl.kind,
|
|
1164
1305
|
role: nodeRole(decl.kind),
|
|
1165
1306
|
label: decl.label,
|
|
1166
|
-
x: snapGrid(
|
|
1307
|
+
x: snapGrid(startX),
|
|
1167
1308
|
y: snapGrid(y),
|
|
1168
|
-
width: snapGrid(
|
|
1309
|
+
width: snapGrid(layerW),
|
|
1169
1310
|
height: snapGrid(layerH),
|
|
1170
1311
|
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
1171
1312
|
props: decl.props,
|
|
@@ -1181,17 +1322,28 @@ function layoutNested(ast) {
|
|
|
1181
1322
|
if (nodeIds.length === 0) return [];
|
|
1182
1323
|
const contentW = ast.meta.width - SAFE * 2;
|
|
1183
1324
|
const contentH = ast.meta.height - SAFE - TITLE_BAND - SAFE;
|
|
1325
|
+
const centerX = SAFE + contentW / 2;
|
|
1326
|
+
const centerY = TITLE_BAND + contentH / 2;
|
|
1184
1327
|
const N = nodeIds.length;
|
|
1185
|
-
const padX = Math.min(
|
|
1186
|
-
const padY = Math.min(
|
|
1328
|
+
const padX = Math.min(54, contentW * 0.42 / Math.max(N, 1));
|
|
1329
|
+
const padY = Math.min(48, contentH * 0.42 / Math.max(N, 1));
|
|
1187
1330
|
const nodes = [];
|
|
1188
1331
|
nodeIds.forEach((id, idx) => {
|
|
1189
1332
|
const decl = ast.nodes[id];
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1333
|
+
const isCore = idx === N - 1;
|
|
1334
|
+
let x, y, w, h;
|
|
1335
|
+
if (isCore) {
|
|
1336
|
+
w = Math.min(320, contentW - idx * padX * 2);
|
|
1337
|
+
h = 84;
|
|
1338
|
+
x = centerX - w / 2;
|
|
1339
|
+
y = centerY - h / 2 + (N > 1 ? 16 : 0);
|
|
1340
|
+
} else {
|
|
1341
|
+
x = SAFE + idx * padX;
|
|
1342
|
+
y = TITLE_BAND + idx * padY;
|
|
1343
|
+
w = contentW - idx * padX * 2;
|
|
1344
|
+
h = contentH - idx * padY * 2;
|
|
1345
|
+
}
|
|
1346
|
+
const focal = decl.props.focal === true || decl.props.accent === true || isCore;
|
|
1195
1347
|
nodes.push({
|
|
1196
1348
|
id,
|
|
1197
1349
|
kind: decl.kind,
|
|
@@ -1204,7 +1356,7 @@ function layoutNested(ast) {
|
|
|
1204
1356
|
style: decl.style ? ast.styles[decl.style]?.props : void 0,
|
|
1205
1357
|
props: decl.props,
|
|
1206
1358
|
opacity: 0,
|
|
1207
|
-
shape: "
|
|
1359
|
+
shape: isCore ? "card" : "container",
|
|
1208
1360
|
focal
|
|
1209
1361
|
});
|
|
1210
1362
|
});
|
|
@@ -1448,9 +1600,12 @@ function scheduleBeats(ast, edges) {
|
|
|
1448
1600
|
function buildSequencePlan(ast, cues) {
|
|
1449
1601
|
if (diagramType(ast) !== "sequence") return { messages: [], activations: [] };
|
|
1450
1602
|
const flowCues = cues.filter((cue) => cue.kind === "flow" && cue.segments?.[0]);
|
|
1451
|
-
const firstY = TITLE_BAND + NODE_H +
|
|
1452
|
-
const lastY = ast.meta.height - SAFE -
|
|
1453
|
-
const
|
|
1603
|
+
const firstY = TITLE_BAND + NODE_H + 56;
|
|
1604
|
+
const lastY = ast.meta.height - SAFE - 48;
|
|
1605
|
+
const availH = Math.max(120, lastY - firstY);
|
|
1606
|
+
const step = flowCues.length > 1 ? Math.max(52, Math.min(90, availH / flowCues.length)) : 0;
|
|
1607
|
+
const totalSeqH = (flowCues.length - 1) * step;
|
|
1608
|
+
const startY = flowCues.length > 1 ? firstY + Math.max(0, (availH - totalSeqH) / 2) : firstY + 40;
|
|
1454
1609
|
const messages = [];
|
|
1455
1610
|
const activations = [];
|
|
1456
1611
|
flowCues.forEach((cue, index) => {
|
|
@@ -1462,7 +1617,7 @@ function buildSequencePlan(ast, cues) {
|
|
|
1462
1617
|
to: segment.to,
|
|
1463
1618
|
kind: segment.op,
|
|
1464
1619
|
label: segment.label,
|
|
1465
|
-
y: snapGrid(
|
|
1620
|
+
y: snapGrid(startY + step * index),
|
|
1466
1621
|
start: cue.start,
|
|
1467
1622
|
duration: cue.duration,
|
|
1468
1623
|
beat: cue.beat
|
package/package.json
CHANGED