@tldraw/mermaid 4.6.0-canary.00a8c03b5687
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 +195 -0
- package/dist-cjs/blueprint.js +17 -0
- package/dist-cjs/blueprint.js.map +7 -0
- package/dist-cjs/colors.js +173 -0
- package/dist-cjs/colors.js.map +7 -0
- package/dist-cjs/createMermaidDiagram.js +157 -0
- package/dist-cjs/createMermaidDiagram.js.map +7 -0
- package/dist-cjs/flowchartDiagram.js +202 -0
- package/dist-cjs/flowchartDiagram.js.map +7 -0
- package/dist-cjs/index.d.ts +114 -0
- package/dist-cjs/index.js +34 -0
- package/dist-cjs/index.js.map +7 -0
- package/dist-cjs/mindmapDiagram.js +139 -0
- package/dist-cjs/mindmapDiagram.js.map +7 -0
- package/dist-cjs/renderBlueprint.js +314 -0
- package/dist-cjs/renderBlueprint.js.map +7 -0
- package/dist-cjs/sequenceDiagram.js +686 -0
- package/dist-cjs/sequenceDiagram.js.map +7 -0
- package/dist-cjs/stateDiagram.js +373 -0
- package/dist-cjs/stateDiagram.js.map +7 -0
- package/dist-cjs/svgParsing.js +187 -0
- package/dist-cjs/svgParsing.js.map +7 -0
- package/dist-cjs/utils.js +75 -0
- package/dist-cjs/utils.js.map +7 -0
- package/dist-esm/blueprint.mjs +1 -0
- package/dist-esm/blueprint.mjs.map +7 -0
- package/dist-esm/colors.mjs +153 -0
- package/dist-esm/colors.mjs.map +7 -0
- package/dist-esm/createMermaidDiagram.mjs +127 -0
- package/dist-esm/createMermaidDiagram.mjs.map +7 -0
- package/dist-esm/flowchartDiagram.mjs +188 -0
- package/dist-esm/flowchartDiagram.mjs.map +7 -0
- package/dist-esm/index.d.mts +114 -0
- package/dist-esm/index.mjs +14 -0
- package/dist-esm/index.mjs.map +7 -0
- package/dist-esm/mindmapDiagram.mjs +119 -0
- package/dist-esm/mindmapDiagram.mjs.map +7 -0
- package/dist-esm/renderBlueprint.mjs +298 -0
- package/dist-esm/renderBlueprint.mjs.map +7 -0
- package/dist-esm/sequenceDiagram.mjs +666 -0
- package/dist-esm/sequenceDiagram.mjs.map +7 -0
- package/dist-esm/stateDiagram.mjs +359 -0
- package/dist-esm/stateDiagram.mjs.map +7 -0
- package/dist-esm/svgParsing.mjs +167 -0
- package/dist-esm/svgParsing.mjs.map +7 -0
- package/dist-esm/utils.mjs +55 -0
- package/dist-esm/utils.mjs.map +7 -0
- package/package.json +64 -0
- package/src/blueprint.ts +75 -0
- package/src/colors.ts +215 -0
- package/src/createMermaidDiagram.test.ts +31 -0
- package/src/createMermaidDiagram.ts +169 -0
- package/src/flowchartDiagram.ts +232 -0
- package/src/index.ts +18 -0
- package/src/mermaidDiagrams.test.ts +1157 -0
- package/src/mindmapDiagram.ts +169 -0
- package/src/renderBlueprint.ts +373 -0
- package/src/sequenceDiagram.ts +851 -0
- package/src/stateDiagram.ts +477 -0
- package/src/svgParsing.ts +240 -0
- package/src/utils.ts +73 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { buildClassDefColorMap, parseCssStyles, parseNodeInlineColor } from "./colors.mjs";
|
|
2
|
+
import {
|
|
3
|
+
buildNodeCentersFromSvg,
|
|
4
|
+
parseAllEdgePointsFromSvg,
|
|
5
|
+
parseClustersFromSvg,
|
|
6
|
+
parseNodesFromSvg,
|
|
7
|
+
scaleLayout
|
|
8
|
+
} from "./svgParsing.mjs";
|
|
9
|
+
import { getArrowBend, LAYOUT_SCALE, orderTopDown } from "./utils.mjs";
|
|
10
|
+
function mapEdgeTypeToArrowhead(type) {
|
|
11
|
+
if (!type) return "arrow";
|
|
12
|
+
if (type.includes("point")) return "arrow";
|
|
13
|
+
if (type.includes("circle")) return "dot";
|
|
14
|
+
if (type.includes("cross")) return "bar";
|
|
15
|
+
if (type.includes("open")) return "none";
|
|
16
|
+
return "arrow";
|
|
17
|
+
}
|
|
18
|
+
function mapFlowShapeTypeToGeo(type) {
|
|
19
|
+
switch (type) {
|
|
20
|
+
case "diamond":
|
|
21
|
+
return "diamond";
|
|
22
|
+
case "ellipse":
|
|
23
|
+
case "circle":
|
|
24
|
+
case "doublecircle":
|
|
25
|
+
case "stadium":
|
|
26
|
+
case "cylinder":
|
|
27
|
+
return "ellipse";
|
|
28
|
+
case "hexagon":
|
|
29
|
+
return "hexagon";
|
|
30
|
+
case "trapezoid":
|
|
31
|
+
// TODO: implement inv_trapezoid in SDK
|
|
32
|
+
case "inv_trapezoid":
|
|
33
|
+
return "trapezoid";
|
|
34
|
+
case "lean_right":
|
|
35
|
+
return "rhombus";
|
|
36
|
+
case "lean_left":
|
|
37
|
+
return "rhombus-2";
|
|
38
|
+
case "square":
|
|
39
|
+
case "rect":
|
|
40
|
+
case "round":
|
|
41
|
+
case "subroutine":
|
|
42
|
+
default:
|
|
43
|
+
return "rectangle";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function mapEdgeStrokeToDash(stroke) {
|
|
47
|
+
if (!stroke) return "solid";
|
|
48
|
+
if (stroke === "dotted") return "dotted";
|
|
49
|
+
return "solid";
|
|
50
|
+
}
|
|
51
|
+
const FRAME_TOP_PAD = 14;
|
|
52
|
+
function buildHierarchy(subGraphs) {
|
|
53
|
+
const subGraphIds = new Set(subGraphs.map((subGraph) => subGraph.id));
|
|
54
|
+
const nodeToSubGraph = /* @__PURE__ */ new Map();
|
|
55
|
+
const subGraphParent = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const subGraph of subGraphs) {
|
|
57
|
+
for (const nodeId of subGraph.nodes) {
|
|
58
|
+
if (subGraphIds.has(nodeId)) {
|
|
59
|
+
subGraphParent.set(nodeId, subGraph.id);
|
|
60
|
+
} else if (!nodeToSubGraph.has(nodeId)) {
|
|
61
|
+
nodeToSubGraph.set(nodeId, subGraph.id);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { nodeToSubGraph, subGraphParent };
|
|
66
|
+
}
|
|
67
|
+
function parseFlowchartLayout(root) {
|
|
68
|
+
const nodes = parseNodesFromSvg(root, ".node", (domId) => {
|
|
69
|
+
const match = domId.match(/^flowchart-(.+)-\d+$/);
|
|
70
|
+
return match ? match[1] : domId;
|
|
71
|
+
});
|
|
72
|
+
const clusters = parseClustersFromSvg(root, ".cluster");
|
|
73
|
+
const edges = parseAllEdgePointsFromSvg(root, (dataId) => {
|
|
74
|
+
const match = dataId.match(/^L_(.+)_([^_]+)_\d+$/);
|
|
75
|
+
return match ? { start: match[1], end: match[2] } : null;
|
|
76
|
+
});
|
|
77
|
+
scaleLayout(nodes, clusters, edges, LAYOUT_SCALE);
|
|
78
|
+
return { nodes, clusters, edges };
|
|
79
|
+
}
|
|
80
|
+
function flowchartToBlueprint(layout, vertices, edges, subGraphs, classDefs) {
|
|
81
|
+
const nodeColorMap = classDefs ? buildClassDefColorMap(classDefs, vertices) : /* @__PURE__ */ new Map();
|
|
82
|
+
const { nodes: svgNodes, clusters: svgClusters, edges: svgEdges } = layout;
|
|
83
|
+
const nodeCenters = buildNodeCentersFromSvg(svgNodes, svgClusters);
|
|
84
|
+
const allSubGraphs = subGraphs || [];
|
|
85
|
+
const { nodeToSubGraph, subGraphParent } = buildHierarchy(allSubGraphs);
|
|
86
|
+
const nodes = [];
|
|
87
|
+
const blueprintEdges = [];
|
|
88
|
+
for (const subGraph of orderTopDown(
|
|
89
|
+
allSubGraphs,
|
|
90
|
+
(subGraph2) => subGraph2.id,
|
|
91
|
+
(subGraph2) => subGraphParent.get(subGraph2.id)
|
|
92
|
+
)) {
|
|
93
|
+
const cluster = svgClusters.get(subGraph.id);
|
|
94
|
+
if (!cluster) continue;
|
|
95
|
+
nodes.push({
|
|
96
|
+
id: subGraph.id,
|
|
97
|
+
x: cluster.topLeft.x,
|
|
98
|
+
y: cluster.topLeft.y - FRAME_TOP_PAD,
|
|
99
|
+
w: cluster.width,
|
|
100
|
+
h: cluster.height + FRAME_TOP_PAD,
|
|
101
|
+
geo: "rectangle",
|
|
102
|
+
parentId: subGraphParent.get(subGraph.id),
|
|
103
|
+
label: subGraph.title || subGraph.id,
|
|
104
|
+
fill: "semi",
|
|
105
|
+
color: "black",
|
|
106
|
+
dash: "draw",
|
|
107
|
+
size: "s",
|
|
108
|
+
align: "middle",
|
|
109
|
+
verticalAlign: "start"
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
for (const [id, vertex] of vertices) {
|
|
113
|
+
const svgNode = svgNodes.get(id);
|
|
114
|
+
if (!svgNode) continue;
|
|
115
|
+
const geo = mapFlowShapeTypeToGeo(vertex.type);
|
|
116
|
+
const colors = nodeColorMap.get(id) ?? parseNodeInlineColor(vertex.styles);
|
|
117
|
+
let { width: w, height: h } = svgNode;
|
|
118
|
+
if (vertex.type === "circle" || vertex.type === "doublecircle") {
|
|
119
|
+
w = h = Math.max(w, h);
|
|
120
|
+
}
|
|
121
|
+
nodes.push({
|
|
122
|
+
id,
|
|
123
|
+
x: svgNode.center.x - w / 2,
|
|
124
|
+
y: svgNode.center.y - h / 2,
|
|
125
|
+
w,
|
|
126
|
+
h,
|
|
127
|
+
geo,
|
|
128
|
+
parentId: nodeToSubGraph.get(id),
|
|
129
|
+
label: vertex.text || void 0,
|
|
130
|
+
...(colors?.fillColor && { fill: "solid" }),
|
|
131
|
+
...(colors && { color: colors.strokeColor ?? colors.fillColor }),
|
|
132
|
+
align: "middle",
|
|
133
|
+
verticalAlign: "middle",
|
|
134
|
+
size: "m"
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
138
|
+
for (const edge of edges) {
|
|
139
|
+
const startCenter = nodeCenters.get(edge.start);
|
|
140
|
+
const endCenter = nodeCenters.get(edge.end);
|
|
141
|
+
let bend = 0;
|
|
142
|
+
if (startCenter && endCenter) {
|
|
143
|
+
let bestIndex = -1;
|
|
144
|
+
let bestDist = Infinity;
|
|
145
|
+
for (let i = 0; i < svgEdges.length; i++) {
|
|
146
|
+
if (claimed.has(i) || svgEdges[i].points.length < 2) continue;
|
|
147
|
+
const points = svgEdges[i].points;
|
|
148
|
+
const distance = Math.hypot(points[0].x - startCenter.x, points[0].y - startCenter.y) + Math.hypot(
|
|
149
|
+
points[points.length - 1].x - endCenter.x,
|
|
150
|
+
points[points.length - 1].y - endCenter.y
|
|
151
|
+
);
|
|
152
|
+
if (distance < bestDist) {
|
|
153
|
+
bestDist = distance;
|
|
154
|
+
bestIndex = i;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (bestIndex >= 0) {
|
|
158
|
+
claimed.add(bestIndex);
|
|
159
|
+
bend = getArrowBend(svgEdges[bestIndex]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const cssOverrides = edge.style ? parseCssStyles(edge.style) : void 0;
|
|
163
|
+
const arrowheadEnd = mapEdgeTypeToArrowhead(edge.type);
|
|
164
|
+
const dash = cssOverrides?.dashOverride ?? mapEdgeStrokeToDash(edge.stroke);
|
|
165
|
+
const size = cssOverrides?.sizeOverride ?? (edge.stroke === "thick" ? "l" : "s");
|
|
166
|
+
blueprintEdges.push({
|
|
167
|
+
startNodeId: edge.start,
|
|
168
|
+
endNodeId: edge.end,
|
|
169
|
+
label: edge.text,
|
|
170
|
+
bend,
|
|
171
|
+
arrowheadEnd,
|
|
172
|
+
arrowheadStart: edge.type?.includes("double_arrow") ? arrowheadEnd : void 0,
|
|
173
|
+
dash,
|
|
174
|
+
size,
|
|
175
|
+
color: cssOverrides?.color
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
179
|
+
const validEdges = blueprintEdges.filter(
|
|
180
|
+
(e) => nodeIds.has(e.startNodeId) && nodeIds.has(e.endNodeId)
|
|
181
|
+
);
|
|
182
|
+
return { nodes, edges: validEdges };
|
|
183
|
+
}
|
|
184
|
+
export {
|
|
185
|
+
flowchartToBlueprint,
|
|
186
|
+
parseFlowchartLayout
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=flowchartDiagram.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/flowchartDiagram.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n\tFlowClass,\n\tFlowEdge,\n\tFlowSubGraph,\n\tFlowVertex,\n} from 'mermaid/dist/diagrams/flowchart/types.js'\nimport { TLArrowShapeArrowheadStyle, TLDefaultDashStyle, TLGeoShape } from 'tldraw'\nimport type {\n\tDiagramMermaidBlueprint,\n\tMermaidBlueprintEdge,\n\tMermaidBlueprintGeoNode,\n} from './blueprint'\nimport { buildClassDefColorMap, parseCssStyles, parseNodeInlineColor } from './colors'\nimport {\n\tbuildNodeCentersFromSvg,\n\tparseAllEdgePointsFromSvg,\n\tparseClustersFromSvg,\n\ttype ParsedDiagramLayout,\n\tparseNodesFromSvg,\n\tscaleLayout,\n} from './svgParsing'\nimport { getArrowBend, LAYOUT_SCALE, orderTopDown } from './utils'\n\nfunction mapEdgeTypeToArrowhead(type: string | undefined): TLArrowShapeArrowheadStyle {\n\tif (!type) return 'arrow'\n\n\tif (type.includes('point')) return 'arrow'\n\tif (type.includes('circle')) return 'dot'\n\tif (type.includes('cross')) return 'bar'\n\tif (type.includes('open')) return 'none'\n\n\treturn 'arrow'\n}\n\nfunction mapFlowShapeTypeToGeo(type: string | undefined): TLGeoShape['props']['geo'] {\n\tswitch (type) {\n\t\tcase 'diamond':\n\t\t\treturn 'diamond'\n\t\tcase 'ellipse':\n\t\tcase 'circle':\n\t\tcase 'doublecircle':\n\t\tcase 'stadium':\n\t\tcase 'cylinder':\n\t\t\treturn 'ellipse'\n\t\tcase 'hexagon':\n\t\t\treturn 'hexagon'\n\t\tcase 'trapezoid':\n\t\t// TODO: implement inv_trapezoid in SDK\n\t\tcase 'inv_trapezoid':\n\t\t\treturn 'trapezoid'\n\t\tcase 'lean_right':\n\t\t\treturn 'rhombus'\n\t\tcase 'lean_left':\n\t\t\treturn 'rhombus-2'\n\t\tcase 'square':\n\t\tcase 'rect':\n\t\tcase 'round':\n\t\tcase 'subroutine':\n\t\tdefault:\n\t\t\treturn 'rectangle'\n\t}\n}\n\nfunction mapEdgeStrokeToDash(stroke: string | undefined): TLDefaultDashStyle {\n\tif (!stroke) return 'solid'\n\tif (stroke === 'dotted') return 'dotted'\n\treturn 'solid'\n}\n\nconst FRAME_TOP_PAD = 14\n\nfunction buildHierarchy(subGraphs: FlowSubGraph[]) {\n\tconst subGraphIds = new Set(subGraphs.map((subGraph) => subGraph.id))\n\tconst nodeToSubGraph = new Map<string, string>()\n\tconst subGraphParent = new Map<string, string>()\n\tfor (const subGraph of subGraphs) {\n\t\tfor (const nodeId of subGraph.nodes) {\n\t\t\tif (subGraphIds.has(nodeId)) {\n\t\t\t\tsubGraphParent.set(nodeId, subGraph.id)\n\t\t\t} else if (!nodeToSubGraph.has(nodeId)) {\n\t\t\t\tnodeToSubGraph.set(nodeId, subGraph.id)\n\t\t\t}\n\t\t}\n\t}\n\treturn { nodeToSubGraph, subGraphParent }\n}\n\n/** Parse flowchart-specific SVG layout data for use by {@link flowchartToBlueprint}. */\nexport function parseFlowchartLayout(root: Element): ParsedDiagramLayout {\n\tconst nodes = parseNodesFromSvg(root, '.node', (domId) => {\n\t\tconst match = domId.match(/^flowchart-(.+)-\\d+$/)\n\t\treturn match ? match[1] : domId\n\t})\n\tconst clusters = parseClustersFromSvg(root, '.cluster')\n\tconst edges = parseAllEdgePointsFromSvg(root, (dataId) => {\n\t\tconst match = dataId.match(/^L_(.+)_([^_]+)_\\d+$/)\n\t\treturn match ? { start: match[1], end: match[2] } : null\n\t})\n\tscaleLayout(nodes, clusters, edges, LAYOUT_SCALE)\n\treturn { nodes, clusters, edges }\n}\n\n/** Convert a parsed Mermaid flowchart into a tldraw blueprint of nodes and edges. */\nexport function flowchartToBlueprint(\n\tlayout: ParsedDiagramLayout,\n\tvertices: Map<string, FlowVertex>,\n\tedges: FlowEdge[],\n\tsubGraphs?: FlowSubGraph[],\n\tclassDefs?: Map<string, FlowClass>\n): DiagramMermaidBlueprint {\n\tconst nodeColorMap = classDefs ? buildClassDefColorMap(classDefs, vertices) : new Map()\n\tconst { nodes: svgNodes, clusters: svgClusters, edges: svgEdges } = layout\n\tconst nodeCenters = buildNodeCentersFromSvg(svgNodes, svgClusters)\n\n\tconst allSubGraphs = subGraphs || []\n\tconst { nodeToSubGraph, subGraphParent } = buildHierarchy(allSubGraphs)\n\n\tconst nodes: MermaidBlueprintGeoNode[] = []\n\tconst blueprintEdges: MermaidBlueprintEdge[] = []\n\n\t// Frames for subgraphs\n\tfor (const subGraph of orderTopDown(\n\t\tallSubGraphs,\n\t\t(subGraph) => subGraph.id,\n\t\t(subGraph) => subGraphParent.get(subGraph.id)\n\t)) {\n\t\tconst cluster = svgClusters.get(subGraph.id)\n\t\tif (!cluster) continue\n\n\t\tnodes.push({\n\t\t\tid: subGraph.id,\n\t\t\tx: cluster.topLeft.x,\n\t\t\ty: cluster.topLeft.y - FRAME_TOP_PAD,\n\t\t\tw: cluster.width,\n\t\t\th: cluster.height + FRAME_TOP_PAD,\n\t\t\tgeo: 'rectangle',\n\t\t\tparentId: subGraphParent.get(subGraph.id),\n\t\t\tlabel: subGraph.title || subGraph.id,\n\t\t\tfill: 'semi',\n\t\t\tcolor: 'black',\n\t\t\tdash: 'draw',\n\t\t\tsize: 's',\n\t\t\talign: 'middle',\n\t\t\tverticalAlign: 'start',\n\t\t})\n\t}\n\n\t// Node shapes\n\tfor (const [id, vertex] of vertices) {\n\t\tconst svgNode = svgNodes.get(id)\n\t\tif (!svgNode) continue\n\n\t\tconst geo = mapFlowShapeTypeToGeo(vertex.type)\n\t\tconst colors = nodeColorMap.get(id) ?? parseNodeInlineColor(vertex.styles)\n\n\t\tlet { width: w, height: h } = svgNode\n\t\tif (vertex.type === 'circle' || vertex.type === 'doublecircle') {\n\t\t\tw = h = Math.max(w, h)\n\t\t}\n\n\t\tnodes.push({\n\t\t\tid,\n\t\t\tx: svgNode.center.x - w / 2,\n\t\t\ty: svgNode.center.y - h / 2,\n\t\t\tw,\n\t\t\th,\n\t\t\tgeo,\n\t\t\tparentId: nodeToSubGraph.get(id),\n\t\t\tlabel: vertex.text || undefined,\n\t\t\t...(colors?.fillColor && { fill: 'solid' as const }),\n\t\t\t...(colors && { color: colors.strokeColor ?? colors.fillColor }),\n\t\t\talign: 'middle',\n\t\t\tverticalAlign: 'middle',\n\t\t\tsize: 'm',\n\t\t} satisfies MermaidBlueprintGeoNode)\n\t}\n\n\t// Edges: match DB edges to SVG edges by proximity, compute bends\n\tconst claimed = new Set<number>()\n\tfor (const edge of edges) {\n\t\tconst startCenter = nodeCenters.get(edge.start)\n\t\tconst endCenter = nodeCenters.get(edge.end)\n\n\t\tlet bend = 0\n\t\tif (startCenter && endCenter) {\n\t\t\tlet bestIndex = -1\n\t\t\tlet bestDist = Infinity\n\t\t\tfor (let i = 0; i < svgEdges.length; i++) {\n\t\t\t\tif (claimed.has(i) || svgEdges[i].points.length < 2) continue\n\n\t\t\t\tconst points = svgEdges[i].points\n\t\t\t\tconst distance =\n\t\t\t\t\tMath.hypot(points[0].x - startCenter.x, points[0].y - startCenter.y) +\n\t\t\t\t\tMath.hypot(\n\t\t\t\t\t\tpoints[points.length - 1].x - endCenter.x,\n\t\t\t\t\t\tpoints[points.length - 1].y - endCenter.y\n\t\t\t\t\t)\n\t\t\t\tif (distance < bestDist) {\n\t\t\t\t\tbestDist = distance\n\t\t\t\t\tbestIndex = i\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bestIndex >= 0) {\n\t\t\t\tclaimed.add(bestIndex)\n\t\t\t\tbend = getArrowBend(svgEdges[bestIndex])\n\t\t\t}\n\t\t}\n\n\t\tconst cssOverrides = edge.style ? parseCssStyles(edge.style) : undefined\n\t\tconst arrowheadEnd = mapEdgeTypeToArrowhead(edge.type)\n\t\tconst dash = cssOverrides?.dashOverride ?? mapEdgeStrokeToDash(edge.stroke)\n\t\tconst size = cssOverrides?.sizeOverride ?? (edge.stroke === 'thick' ? 'l' : 's')\n\n\t\tblueprintEdges.push({\n\t\t\tstartNodeId: edge.start,\n\t\t\tendNodeId: edge.end,\n\t\t\tlabel: edge.text,\n\t\t\tbend,\n\t\t\tarrowheadEnd,\n\t\t\tarrowheadStart: edge.type?.includes('double_arrow') ? arrowheadEnd : undefined,\n\t\t\tdash,\n\t\t\tsize,\n\t\t\tcolor: cssOverrides?.color,\n\t\t})\n\t}\n\n\tconst nodeIds = new Set(nodes.map((n) => n.id))\n\tconst validEdges = blueprintEdges.filter(\n\t\t(e) => nodeIds.has(e.startNodeId) && nodeIds.has(e.endNodeId)\n\t)\n\treturn { nodes, edges: validEdges }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["subGraph"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Editor } from 'tldraw';
|
|
2
|
+
import type { TLArrowShapeArrowheadStyle } from 'tldraw';
|
|
3
|
+
import type { TLDefaultColorStyle } from 'tldraw';
|
|
4
|
+
import type { TLDefaultDashStyle } from 'tldraw';
|
|
5
|
+
import type { TLDefaultFillStyle } from 'tldraw';
|
|
6
|
+
import type { TLDefaultHorizontalAlignStyle } from 'tldraw';
|
|
7
|
+
import type { TLDefaultSizeStyle } from 'tldraw';
|
|
8
|
+
import type { TLDefaultVerticalAlignStyle } from 'tldraw';
|
|
9
|
+
import type { TLGeoShapeGeoStyle } from 'tldraw';
|
|
10
|
+
|
|
11
|
+
/** @public */
|
|
12
|
+
export declare interface BlueprintRenderingOptions {
|
|
13
|
+
centerOnPosition?: boolean;
|
|
14
|
+
position?: {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse mermaid text and create tldraw shapes for supported diagram types.
|
|
22
|
+
* Returns the SVG string for supported diagrams, or `null` when the diagram type
|
|
23
|
+
* is unsupported (after calling `onUnsupportedDiagram` if provided).
|
|
24
|
+
* Throws {@link MermaidDiagramError} if parsing fails.
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export declare function createMermaidDiagram(editor: Editor, text: string, options?: MermaidDiagramOptions): Promise<void>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* An intermediate representation of a parsed mermaid diagram as abstract nodes,
|
|
31
|
+
* edges, and lines with layout positions and tldraw style props. Produced by
|
|
32
|
+
* the diagram-specific converters and consumed by `renderBlueprint` to create
|
|
33
|
+
* actual tldraw shapes on the canvas.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export declare interface DiagramMermaidBlueprint {
|
|
38
|
+
nodes: MermaidBlueprintGeoNode[];
|
|
39
|
+
edges: MermaidBlueprintEdge[];
|
|
40
|
+
lines?: MermaidBlueprintLineNode[];
|
|
41
|
+
groups?: string[][];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @public */
|
|
45
|
+
export declare interface MermaidBlueprintEdge {
|
|
46
|
+
startNodeId: string;
|
|
47
|
+
endNodeId: string;
|
|
48
|
+
label?: string;
|
|
49
|
+
bend: number;
|
|
50
|
+
arrowheadEnd?: TLArrowShapeArrowheadStyle;
|
|
51
|
+
arrowheadStart?: TLArrowShapeArrowheadStyle;
|
|
52
|
+
dash?: TLDefaultDashStyle;
|
|
53
|
+
size?: TLDefaultSizeStyle;
|
|
54
|
+
color?: TLDefaultColorStyle;
|
|
55
|
+
anchorStartY?: number;
|
|
56
|
+
anchorEndY?: number;
|
|
57
|
+
isExact?: boolean;
|
|
58
|
+
isPrecise?: boolean;
|
|
59
|
+
isExactEnd?: boolean;
|
|
60
|
+
isPreciseEnd?: boolean;
|
|
61
|
+
decoration?: {
|
|
62
|
+
type: 'autonumber';
|
|
63
|
+
value: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** @public */
|
|
68
|
+
export declare interface MermaidBlueprintGeoNode {
|
|
69
|
+
id: string;
|
|
70
|
+
x: number;
|
|
71
|
+
y: number;
|
|
72
|
+
w: number;
|
|
73
|
+
h: number;
|
|
74
|
+
geo: TLGeoShapeGeoStyle;
|
|
75
|
+
parentId?: string;
|
|
76
|
+
label?: string;
|
|
77
|
+
fill?: TLDefaultFillStyle;
|
|
78
|
+
color?: TLDefaultColorStyle;
|
|
79
|
+
dash?: TLDefaultDashStyle;
|
|
80
|
+
size?: TLDefaultSizeStyle;
|
|
81
|
+
align?: TLDefaultHorizontalAlignStyle;
|
|
82
|
+
verticalAlign?: TLDefaultVerticalAlignStyle;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @public */
|
|
86
|
+
export declare interface MermaidBlueprintLineNode {
|
|
87
|
+
id: string;
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
endX?: number;
|
|
91
|
+
endY: number;
|
|
92
|
+
dash?: TLDefaultDashStyle;
|
|
93
|
+
size?: TLDefaultSizeStyle;
|
|
94
|
+
color?: TLDefaultColorStyle;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @public */
|
|
98
|
+
export declare class MermaidDiagramError extends Error {
|
|
99
|
+
diagramType: string;
|
|
100
|
+
type: 'parse' | 'unsupported';
|
|
101
|
+
constructor(diagramType: string, type: 'parse' | 'unsupported');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** @public */
|
|
105
|
+
export declare interface MermaidDiagramOptions {
|
|
106
|
+
mermaidConfig?: Record<string, any>;
|
|
107
|
+
blueprintRender?: BlueprintRenderingOptions;
|
|
108
|
+
onUnsupportedDiagram?(svg: string): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** @public */
|
|
112
|
+
export declare function renderBlueprint(editor: Editor, blueprint: DiagramMermaidBlueprint, opts?: BlueprintRenderingOptions): void;
|
|
113
|
+
|
|
114
|
+
export { }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { registerTldrawLibraryVersion } from "@tldraw/utils";
|
|
2
|
+
import { createMermaidDiagram, MermaidDiagramError } from "./createMermaidDiagram.mjs";
|
|
3
|
+
import { renderBlueprint } from "./renderBlueprint.mjs";
|
|
4
|
+
registerTldrawLibraryVersion(
|
|
5
|
+
"@tldraw/mermaid",
|
|
6
|
+
"4.6.0-canary.00a8c03b5687",
|
|
7
|
+
"esm"
|
|
8
|
+
);
|
|
9
|
+
export {
|
|
10
|
+
MermaidDiagramError,
|
|
11
|
+
createMermaidDiagram,
|
|
12
|
+
renderBlueprint
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { registerTldrawLibraryVersion } from '@tldraw/utils'\n\nexport type {\n\tDiagramMermaidBlueprint,\n\tMermaidBlueprintEdge,\n\tMermaidBlueprintGeoNode,\n\tMermaidBlueprintLineNode,\n} from './blueprint'\nexport { createMermaidDiagram, MermaidDiagramError } from './createMermaidDiagram'\nexport type { MermaidDiagramOptions } from './createMermaidDiagram'\nexport { renderBlueprint } from './renderBlueprint'\nexport type { BlueprintRenderingOptions } from './renderBlueprint'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oCAAoC;AAQ7C,SAAS,sBAAsB,2BAA2B;AAE1D,SAAS,uBAAuB;AAGhC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { parseRgbToTldrawColor } from "./colors.mjs";
|
|
2
|
+
import { parseNodesFromSvg, scaleLayout } from "./svgParsing.mjs";
|
|
3
|
+
import { LAYOUT_SCALE } from "./utils.mjs";
|
|
4
|
+
const MINDMAP_NODE_TYPE = {
|
|
5
|
+
DEFAULT: 0,
|
|
6
|
+
ROUNDED_RECT: 1,
|
|
7
|
+
RECT: 2,
|
|
8
|
+
CIRCLE: 3,
|
|
9
|
+
CLOUD: 4,
|
|
10
|
+
BANG: 5,
|
|
11
|
+
HEXAGON: 6
|
|
12
|
+
};
|
|
13
|
+
function mapMindmapTypeToGeo(type) {
|
|
14
|
+
switch (type) {
|
|
15
|
+
case MINDMAP_NODE_TYPE.CIRCLE:
|
|
16
|
+
return "ellipse";
|
|
17
|
+
case MINDMAP_NODE_TYPE.CLOUD:
|
|
18
|
+
return "cloud";
|
|
19
|
+
case MINDMAP_NODE_TYPE.HEXAGON:
|
|
20
|
+
return "hexagon";
|
|
21
|
+
case MINDMAP_NODE_TYPE.BANG:
|
|
22
|
+
return "star";
|
|
23
|
+
case MINDMAP_NODE_TYPE.RECT:
|
|
24
|
+
case MINDMAP_NODE_TYPE.ROUNDED_RECT:
|
|
25
|
+
case MINDMAP_NODE_TYPE.DEFAULT:
|
|
26
|
+
default:
|
|
27
|
+
return "rectangle";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getEdgeSizeForLevel(parentLevel) {
|
|
31
|
+
if (parentLevel <= 0) return "l";
|
|
32
|
+
if (parentLevel === 1) return "m";
|
|
33
|
+
return "s";
|
|
34
|
+
}
|
|
35
|
+
function flattenMindmapTree(node, parentId, out) {
|
|
36
|
+
out.push({
|
|
37
|
+
id: String(node.id),
|
|
38
|
+
label: node.descr,
|
|
39
|
+
type: node.type,
|
|
40
|
+
level: node.level,
|
|
41
|
+
parentId,
|
|
42
|
+
section: node.section,
|
|
43
|
+
isRoot: !!node.isRoot
|
|
44
|
+
});
|
|
45
|
+
for (const child of node.children) {
|
|
46
|
+
flattenMindmapTree(child, String(node.id), out);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function parseMindmapNodeId(domId) {
|
|
50
|
+
const match = domId.match(/^node_(\d+)$/);
|
|
51
|
+
return match ? match[1] : domId;
|
|
52
|
+
}
|
|
53
|
+
function parseMindmapLayout(root) {
|
|
54
|
+
const nodes = parseNodesFromSvg(root, ".node", parseMindmapNodeId);
|
|
55
|
+
scaleLayout(nodes, /* @__PURE__ */ new Map(), [], LAYOUT_SCALE);
|
|
56
|
+
return { nodes };
|
|
57
|
+
}
|
|
58
|
+
function mindmapToBlueprint(layout, mindmapTree, svgRoot) {
|
|
59
|
+
const flatNodes = [];
|
|
60
|
+
flattenMindmapTree(mindmapTree, void 0, flatNodes);
|
|
61
|
+
const { nodes: svgNodes } = layout;
|
|
62
|
+
const nodeColors = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const el of svgRoot.querySelectorAll(".node")) {
|
|
64
|
+
const rawId = el.getAttribute("id") || "";
|
|
65
|
+
const id = parseMindmapNodeId(rawId);
|
|
66
|
+
const shape = el.querySelector("rect, circle, ellipse, polygon, path") ?? el.querySelector(".label-container");
|
|
67
|
+
if (shape) {
|
|
68
|
+
const parsed = parseRgbToTldrawColor(getComputedStyle(shape).fill);
|
|
69
|
+
if (parsed) nodeColors.set(id, parsed.color);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const nodes = [];
|
|
73
|
+
const edges = [];
|
|
74
|
+
const levelById = new Map(flatNodes.map((n) => [n.id, n.level]));
|
|
75
|
+
for (const flatNode of flatNodes) {
|
|
76
|
+
const svgNode = svgNodes.get(flatNode.id);
|
|
77
|
+
if (!svgNode) continue;
|
|
78
|
+
const geo = mapMindmapTypeToGeo(flatNode.type);
|
|
79
|
+
const color = nodeColors.get(flatNode.id) ?? "black";
|
|
80
|
+
let { width: w, height: h } = svgNode;
|
|
81
|
+
if (flatNode.type === MINDMAP_NODE_TYPE.CIRCLE) {
|
|
82
|
+
w = h = Math.max(w, h);
|
|
83
|
+
}
|
|
84
|
+
nodes.push({
|
|
85
|
+
id: flatNode.id,
|
|
86
|
+
x: svgNode.center.x - w / 2,
|
|
87
|
+
y: svgNode.center.y - h / 2,
|
|
88
|
+
w,
|
|
89
|
+
h,
|
|
90
|
+
geo,
|
|
91
|
+
label: flatNode.label || void 0,
|
|
92
|
+
fill: "solid",
|
|
93
|
+
color,
|
|
94
|
+
size: flatNode.isRoot ? "l" : "m",
|
|
95
|
+
align: "middle",
|
|
96
|
+
verticalAlign: "middle"
|
|
97
|
+
});
|
|
98
|
+
if (flatNode.parentId) {
|
|
99
|
+
const parentLevel = levelById.get(flatNode.parentId) ?? 0;
|
|
100
|
+
edges.push({
|
|
101
|
+
startNodeId: flatNode.parentId,
|
|
102
|
+
endNodeId: flatNode.id,
|
|
103
|
+
bend: 0,
|
|
104
|
+
arrowheadEnd: "none",
|
|
105
|
+
arrowheadStart: "none",
|
|
106
|
+
size: getEdgeSizeForLevel(parentLevel),
|
|
107
|
+
color
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
112
|
+
const validEdges = edges.filter((e) => nodeIds.has(e.startNodeId) && nodeIds.has(e.endNodeId));
|
|
113
|
+
return { nodes, edges: validEdges };
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
mindmapToBlueprint,
|
|
117
|
+
parseMindmapLayout
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=mindmapDiagram.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/mindmapDiagram.ts"],
|
|
4
|
+
"sourcesContent": ["import type { MindmapNode } from 'mermaid/dist/diagrams/mindmap/mindmapTypes.js'\nimport type { TLDefaultColorStyle, TLDefaultSizeStyle, TLGeoShapeGeoStyle } from 'tldraw'\nimport type {\n\tDiagramMermaidBlueprint,\n\tMermaidBlueprintEdge,\n\tMermaidBlueprintGeoNode,\n} from './blueprint'\nimport { parseRgbToTldrawColor } from './colors'\nimport type { ParsedNode } from './svgParsing'\nimport { parseNodesFromSvg, scaleLayout } from './svgParsing'\nimport { LAYOUT_SCALE } from './utils'\n\nconst MINDMAP_NODE_TYPE = {\n\tDEFAULT: 0,\n\tROUNDED_RECT: 1,\n\tRECT: 2,\n\tCIRCLE: 3,\n\tCLOUD: 4,\n\tBANG: 5,\n\tHEXAGON: 6,\n} as const\n\nfunction mapMindmapTypeToGeo(type: number): TLGeoShapeGeoStyle {\n\tswitch (type) {\n\t\tcase MINDMAP_NODE_TYPE.CIRCLE:\n\t\t\treturn 'ellipse'\n\t\tcase MINDMAP_NODE_TYPE.CLOUD:\n\t\t\treturn 'cloud'\n\t\tcase MINDMAP_NODE_TYPE.HEXAGON:\n\t\t\treturn 'hexagon'\n\t\tcase MINDMAP_NODE_TYPE.BANG:\n\t\t\treturn 'star'\n\t\tcase MINDMAP_NODE_TYPE.RECT:\n\t\tcase MINDMAP_NODE_TYPE.ROUNDED_RECT:\n\t\tcase MINDMAP_NODE_TYPE.DEFAULT:\n\t\tdefault:\n\t\t\treturn 'rectangle'\n\t}\n}\n\nfunction getEdgeSizeForLevel(parentLevel: number): TLDefaultSizeStyle {\n\tif (parentLevel <= 0) return 'l'\n\tif (parentLevel === 1) return 'm'\n\treturn 's'\n}\n\ninterface FlatNode {\n\tid: string\n\tlabel: string\n\ttype: number\n\tlevel: number\n\tparentId: string | undefined\n\tsection: number | undefined\n\tisRoot: boolean\n}\n\nfunction flattenMindmapTree(\n\tnode: MindmapNode,\n\tparentId: string | undefined,\n\tout: FlatNode[]\n): void {\n\tout.push({\n\t\tid: String(node.id),\n\t\tlabel: node.descr,\n\t\ttype: node.type,\n\t\tlevel: node.level,\n\t\tparentId,\n\t\tsection: node.section,\n\t\tisRoot: !!node.isRoot,\n\t})\n\tfor (const child of node.children) {\n\t\tflattenMindmapTree(child, String(node.id), out)\n\t}\n}\n\n/**\n * Pre-parsed SVG layout for mindmap diagram converters.\n * Contains already-scaled node positions extracted from the SVG.\n */\nexport interface ParsedMindmapLayout {\n\tnodes: Map<string, ParsedNode>\n}\n\nfunction parseMindmapNodeId(domId: string): string {\n\tconst match = domId.match(/^node_(\\d+)$/)\n\treturn match ? match[1] : domId\n}\n\n/** Parse mindmap-specific SVG layout data for use by {@link mindmapToBlueprint}. */\nexport function parseMindmapLayout(root: Element): ParsedMindmapLayout {\n\tconst nodes = parseNodesFromSvg(root, '.node', parseMindmapNodeId)\n\tscaleLayout(nodes, new Map(), [], LAYOUT_SCALE)\n\treturn { nodes }\n}\n\n/** Convert a parsed Mermaid mindmap into a tldraw blueprint of nodes and edges. */\nexport function mindmapToBlueprint(\n\tlayout: ParsedMindmapLayout,\n\tmindmapTree: MindmapNode,\n\tsvgRoot: Element\n): DiagramMermaidBlueprint {\n\tconst flatNodes: FlatNode[] = []\n\tflattenMindmapTree(mindmapTree, undefined, flatNodes)\n\n\tconst { nodes: svgNodes } = layout\n\n\tconst nodeColors = new Map<string, TLDefaultColorStyle>()\n\tfor (const el of svgRoot.querySelectorAll('.node')) {\n\t\tconst rawId = el.getAttribute('id') || ''\n\t\tconst id = parseMindmapNodeId(rawId)\n\t\tconst shape =\n\t\t\tel.querySelector('rect, circle, ellipse, polygon, path') ??\n\t\t\tel.querySelector('.label-container')\n\t\tif (shape) {\n\t\t\tconst parsed = parseRgbToTldrawColor(getComputedStyle(shape as Element).fill)\n\t\t\tif (parsed) nodeColors.set(id, parsed.color)\n\t\t}\n\t}\n\n\tconst nodes: MermaidBlueprintGeoNode[] = []\n\tconst edges: MermaidBlueprintEdge[] = []\n\tconst levelById = new Map(flatNodes.map((n) => [n.id, n.level]))\n\n\tfor (const flatNode of flatNodes) {\n\t\tconst svgNode = svgNodes.get(flatNode.id)\n\t\tif (!svgNode) continue\n\n\t\tconst geo = mapMindmapTypeToGeo(flatNode.type)\n\t\tconst color = nodeColors.get(flatNode.id) ?? 'black'\n\n\t\tlet { width: w, height: h } = svgNode\n\t\tif (flatNode.type === MINDMAP_NODE_TYPE.CIRCLE) {\n\t\t\tw = h = Math.max(w, h)\n\t\t}\n\n\t\tnodes.push({\n\t\t\tid: flatNode.id,\n\t\t\tx: svgNode.center.x - w / 2,\n\t\t\ty: svgNode.center.y - h / 2,\n\t\t\tw,\n\t\t\th,\n\t\t\tgeo,\n\t\t\tlabel: flatNode.label || undefined,\n\t\t\tfill: 'solid',\n\t\t\tcolor,\n\t\t\tsize: flatNode.isRoot ? 'l' : 'm',\n\t\t\talign: 'middle',\n\t\t\tverticalAlign: 'middle',\n\t\t})\n\n\t\t// Edge from parent to this node\n\t\tif (flatNode.parentId) {\n\t\t\tconst parentLevel = levelById.get(flatNode.parentId) ?? 0\n\t\t\tedges.push({\n\t\t\t\tstartNodeId: flatNode.parentId,\n\t\t\t\tendNodeId: flatNode.id,\n\t\t\t\tbend: 0,\n\t\t\t\tarrowheadEnd: 'none',\n\t\t\t\tarrowheadStart: 'none',\n\t\t\t\tsize: getEdgeSizeForLevel(parentLevel),\n\t\t\t\tcolor,\n\t\t\t})\n\t\t}\n\t}\n\n\tconst nodeIds = new Set(nodes.map((n) => n.id))\n\tconst validEdges = edges.filter((e) => nodeIds.has(e.startNodeId) && nodeIds.has(e.endNodeId))\n\treturn { nodes, edges: validEdges }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|