@tldraw/mermaid 4.6.0-internal.c7df3c92455a
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-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 +144 -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/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 +114 -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/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 +62 -0
- package/src/blueprint.ts +75 -0
- package/src/colors.ts +215 -0
- package/src/createMermaidDiagram.test.ts +31 -0
- package/src/createMermaidDiagram.ts +155 -0
- package/src/flowchartDiagram.ts +232 -0
- package/src/index.ts +18 -0
- package/src/mermaidDiagrams.test.ts +880 -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-internal.c7df3c92455a",
|
|
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,298 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createShapeId,
|
|
3
|
+
toRichText,
|
|
4
|
+
Vec
|
|
5
|
+
} from "tldraw";
|
|
6
|
+
import { orderTopDown, sanitizeDiagramText } from "./utils.mjs";
|
|
7
|
+
const defaultBlueprintRenderingOptions = {
|
|
8
|
+
centerOnPosition: true
|
|
9
|
+
};
|
|
10
|
+
function renderBlueprint(editor, blueprint, opts) {
|
|
11
|
+
const options = { ...defaultBlueprintRenderingOptions, ...(opts || {}) };
|
|
12
|
+
const { nodes, edges, lines } = blueprint;
|
|
13
|
+
const bounds = computeBlueprintBounds(nodes, lines);
|
|
14
|
+
const center = options.position ? options.position : editor.user.getIsPasteAtCursorMode() ? editor.inputs.getCurrentPagePoint() : editor.getViewportPageBounds().center;
|
|
15
|
+
const offsetX = options.centerOnPosition ? center.x - (bounds.maxX + bounds.minX) / 2 : center.x - bounds.minX;
|
|
16
|
+
const offsetY = options.centerOnPosition ? center.y - (bounds.maxY + bounds.minY) / 2 : center.y - bounds.minY;
|
|
17
|
+
const ordered = orderTopDown(
|
|
18
|
+
nodes,
|
|
19
|
+
(n) => n.id,
|
|
20
|
+
(n) => n.parentId
|
|
21
|
+
);
|
|
22
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
23
|
+
const shapeIds = /* @__PURE__ */ new Map();
|
|
24
|
+
if (lines) {
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
const lineId = createShapeId();
|
|
27
|
+
shapeIds.set(line.id, lineId);
|
|
28
|
+
editor.createShape({
|
|
29
|
+
id: lineId,
|
|
30
|
+
type: "line",
|
|
31
|
+
x: offsetX + line.x,
|
|
32
|
+
y: offsetY + line.y,
|
|
33
|
+
props: {
|
|
34
|
+
dash: line.dash ?? "solid",
|
|
35
|
+
size: line.size ?? "s",
|
|
36
|
+
color: line.color ?? "black",
|
|
37
|
+
spline: "line",
|
|
38
|
+
points: {
|
|
39
|
+
a1: { id: "a1", index: "a1", x: 0, y: 0 },
|
|
40
|
+
a2: { id: "a2", index: "a2", x: line.endX ?? 0, y: line.endY }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const node of ordered) {
|
|
47
|
+
const shapeId = createShapeId();
|
|
48
|
+
shapeIds.set(node.id, shapeId);
|
|
49
|
+
const parent = node.parentId ? nodeById.get(node.parentId) : void 0;
|
|
50
|
+
const parentShapeId = node.parentId ? shapeIds.get(node.parentId) : void 0;
|
|
51
|
+
const absoluteX = offsetX + node.x;
|
|
52
|
+
const absoluteY = offsetY + node.y;
|
|
53
|
+
const x = parent ? absoluteX - (offsetX + parent.x) : absoluteX;
|
|
54
|
+
const y = parent ? absoluteY - (offsetY + parent.y) : absoluteY;
|
|
55
|
+
editor.createShape({
|
|
56
|
+
id: shapeId,
|
|
57
|
+
type: "geo",
|
|
58
|
+
x,
|
|
59
|
+
y,
|
|
60
|
+
parentId: parentShapeId,
|
|
61
|
+
props: {
|
|
62
|
+
geo: node.geo,
|
|
63
|
+
w: node.w,
|
|
64
|
+
h: node.h,
|
|
65
|
+
fill: node.fill ?? "none",
|
|
66
|
+
color: node.color ?? "black",
|
|
67
|
+
dash: node.dash ?? "draw",
|
|
68
|
+
size: node.size ?? "m",
|
|
69
|
+
...(node.label && { richText: toRichText(sanitizeDiagramText(node.label)) }),
|
|
70
|
+
...(node.align && { align: node.align }),
|
|
71
|
+
...(node.verticalAlign && { verticalAlign: node.verticalAlign })
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const arrowIds = [];
|
|
76
|
+
for (const edge of edges) {
|
|
77
|
+
const arrowId = createArrowFromEdge(editor, edge, shapeIds);
|
|
78
|
+
if (arrowId) arrowIds.push(arrowId);
|
|
79
|
+
}
|
|
80
|
+
const groupedIds = /* @__PURE__ */ new Set();
|
|
81
|
+
const subGroupIds = [];
|
|
82
|
+
if (blueprint.groups) {
|
|
83
|
+
for (const group of blueprint.groups) {
|
|
84
|
+
const members = [];
|
|
85
|
+
for (const blueprintId of group) {
|
|
86
|
+
const memberShapeId = shapeIds.get(blueprintId);
|
|
87
|
+
if (memberShapeId) {
|
|
88
|
+
members.push(memberShapeId);
|
|
89
|
+
groupedIds.add(memberShapeId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (members.length > 1) {
|
|
93
|
+
editor.groupShapes(members);
|
|
94
|
+
const first = editor.getShape(members[0]);
|
|
95
|
+
if (first && first.parentId !== editor.getCurrentPageId()) {
|
|
96
|
+
subGroupIds.push(first.parentId);
|
|
97
|
+
} else {
|
|
98
|
+
subGroupIds.push(members[0]);
|
|
99
|
+
}
|
|
100
|
+
} else if (members.length === 1) {
|
|
101
|
+
subGroupIds.push(members[0]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const topLevelIds = [...subGroupIds];
|
|
106
|
+
for (const node of nodes) {
|
|
107
|
+
if (!node.parentId) {
|
|
108
|
+
const nodeShapeId = shapeIds.get(node.id);
|
|
109
|
+
if (nodeShapeId && !groupedIds.has(nodeShapeId)) topLevelIds.push(nodeShapeId);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (lines) {
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
const lineShapeId = shapeIds.get(line.id);
|
|
115
|
+
if (lineShapeId && !groupedIds.has(lineShapeId)) topLevelIds.push(lineShapeId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
topLevelIds.push(...arrowIds);
|
|
119
|
+
let rootShapeId;
|
|
120
|
+
if (topLevelIds.length > 1) {
|
|
121
|
+
editor.groupShapes(topLevelIds);
|
|
122
|
+
const first = editor.getShape(topLevelIds[0]);
|
|
123
|
+
if (first && first.parentId !== editor.getCurrentPageId()) {
|
|
124
|
+
rootShapeId = first.parentId;
|
|
125
|
+
}
|
|
126
|
+
} else if (topLevelIds.length === 1) {
|
|
127
|
+
rootShapeId = topLevelIds[0];
|
|
128
|
+
}
|
|
129
|
+
if (rootShapeId) {
|
|
130
|
+
const actualBounds = editor.getShapePageBounds(rootShapeId);
|
|
131
|
+
if (actualBounds) {
|
|
132
|
+
const desiredX = options.centerOnPosition ? center.x - actualBounds.w / 2 : center.x;
|
|
133
|
+
const desiredY = options.centerOnPosition ? center.y - actualBounds.h / 2 : center.y;
|
|
134
|
+
const dx = desiredX - actualBounds.x;
|
|
135
|
+
const dy = desiredY - actualBounds.y;
|
|
136
|
+
if (Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5) {
|
|
137
|
+
const shape = editor.getShape(rootShapeId);
|
|
138
|
+
editor.updateShape({
|
|
139
|
+
id: rootShapeId,
|
|
140
|
+
type: shape.type,
|
|
141
|
+
x: shape.x + dx,
|
|
142
|
+
y: shape.y + dy
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function makeArrowBinding(arrowId, targetId, terminal, anchor, isExact, isPrecise) {
|
|
149
|
+
return {
|
|
150
|
+
fromId: arrowId,
|
|
151
|
+
toId: targetId,
|
|
152
|
+
type: "arrow",
|
|
153
|
+
props: { terminal, normalizedAnchor: anchor, isExact, isPrecise }
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function createArrowFromEdge(editor, edge, shapeIds) {
|
|
157
|
+
const startShapeId = shapeIds.get(edge.startNodeId);
|
|
158
|
+
const endShapeId = shapeIds.get(edge.endNodeId);
|
|
159
|
+
if (!startShapeId || !endShapeId) return void 0;
|
|
160
|
+
const startBounds = editor.getShapePageBounds(startShapeId);
|
|
161
|
+
const endBounds = editor.getShapePageBounds(endShapeId);
|
|
162
|
+
if (!startBounds || !endBounds) return void 0;
|
|
163
|
+
const arrowId = createShapeId();
|
|
164
|
+
const isSelfLoop = startShapeId === endShapeId;
|
|
165
|
+
const hasPreciseAnchors = edge.anchorStartY !== void 0 || edge.anchorEndY !== void 0;
|
|
166
|
+
let labelText = edge.label;
|
|
167
|
+
if (edge.decoration?.type === "autonumber") {
|
|
168
|
+
const num = edge.decoration.value;
|
|
169
|
+
labelText = labelText ? `${num} ${labelText}` : num;
|
|
170
|
+
}
|
|
171
|
+
const baseProps = {
|
|
172
|
+
dash: edge.dash ?? "solid",
|
|
173
|
+
size: edge.size ?? "s",
|
|
174
|
+
arrowheadEnd: edge.arrowheadEnd ?? "arrow",
|
|
175
|
+
...(edge.arrowheadStart && { arrowheadStart: edge.arrowheadStart }),
|
|
176
|
+
color: edge.color ?? "black",
|
|
177
|
+
...(labelText && { richText: toRichText(sanitizeDiagramText(labelText)) })
|
|
178
|
+
};
|
|
179
|
+
if (hasPreciseAnchors) {
|
|
180
|
+
const startAnchorY = edge.anchorStartY ?? 0.5;
|
|
181
|
+
const endAnchorY = edge.anchorEndY ?? 0.5;
|
|
182
|
+
const exactStart = edge.isExact ?? true;
|
|
183
|
+
const preciseStart = edge.isPrecise ?? true;
|
|
184
|
+
const exactEnd = edge.isExactEnd ?? exactStart;
|
|
185
|
+
const preciseEnd = edge.isPreciseEnd ?? preciseStart;
|
|
186
|
+
const startPoint = {
|
|
187
|
+
x: startBounds.x + startBounds.w * 0.5,
|
|
188
|
+
y: startBounds.y + startBounds.h * startAnchorY
|
|
189
|
+
};
|
|
190
|
+
const endPoint = {
|
|
191
|
+
x: endBounds.x + endBounds.w * 0.5,
|
|
192
|
+
y: endBounds.y + endBounds.h * endAnchorY
|
|
193
|
+
};
|
|
194
|
+
const origin = {
|
|
195
|
+
x: Math.min(startPoint.x, endPoint.x),
|
|
196
|
+
y: Math.min(startPoint.y, endPoint.y)
|
|
197
|
+
};
|
|
198
|
+
editor.run(() => {
|
|
199
|
+
editor.createShape({
|
|
200
|
+
id: arrowId,
|
|
201
|
+
type: "arrow",
|
|
202
|
+
x: origin.x,
|
|
203
|
+
y: origin.y,
|
|
204
|
+
props: {
|
|
205
|
+
...baseProps,
|
|
206
|
+
start: { x: startPoint.x - origin.x, y: startPoint.y - origin.y },
|
|
207
|
+
end: { x: endPoint.x - origin.x, y: endPoint.y - origin.y },
|
|
208
|
+
bend: edge.bend
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
editor.createBindings([
|
|
212
|
+
makeArrowBinding(
|
|
213
|
+
arrowId,
|
|
214
|
+
startShapeId,
|
|
215
|
+
"start",
|
|
216
|
+
{ x: 0.5, y: startAnchorY },
|
|
217
|
+
exactStart,
|
|
218
|
+
preciseStart
|
|
219
|
+
),
|
|
220
|
+
makeArrowBinding(
|
|
221
|
+
arrowId,
|
|
222
|
+
endShapeId,
|
|
223
|
+
"end",
|
|
224
|
+
{ x: 0.5, y: endAnchorY },
|
|
225
|
+
exactEnd,
|
|
226
|
+
preciseEnd
|
|
227
|
+
)
|
|
228
|
+
]);
|
|
229
|
+
});
|
|
230
|
+
return arrowId;
|
|
231
|
+
}
|
|
232
|
+
if (isSelfLoop) {
|
|
233
|
+
editor.run(() => {
|
|
234
|
+
editor.createShape({
|
|
235
|
+
id: arrowId,
|
|
236
|
+
type: "arrow",
|
|
237
|
+
x: startBounds.x,
|
|
238
|
+
y: startBounds.y,
|
|
239
|
+
props: {
|
|
240
|
+
...baseProps,
|
|
241
|
+
start: { x: startBounds.w / 2, y: 0 },
|
|
242
|
+
end: { x: startBounds.w, y: startBounds.h / 2 },
|
|
243
|
+
bend: -80
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
editor.createBindings([
|
|
247
|
+
makeArrowBinding(arrowId, startShapeId, "start", { x: 0.9, y: 0.5 }, false, false),
|
|
248
|
+
makeArrowBinding(arrowId, endShapeId, "end", { x: 0.85, y: 0.8 }, false, false)
|
|
249
|
+
]);
|
|
250
|
+
});
|
|
251
|
+
return arrowId;
|
|
252
|
+
}
|
|
253
|
+
const startCenter = startBounds.center;
|
|
254
|
+
const endCenter = endBounds.center;
|
|
255
|
+
const arrowOrigin = Vec.Min(startCenter, endCenter);
|
|
256
|
+
editor.run(() => {
|
|
257
|
+
editor.createShape({
|
|
258
|
+
id: arrowId,
|
|
259
|
+
type: "arrow",
|
|
260
|
+
x: arrowOrigin.x,
|
|
261
|
+
y: arrowOrigin.y,
|
|
262
|
+
props: {
|
|
263
|
+
...baseProps,
|
|
264
|
+
start: { x: startCenter.x - arrowOrigin.x, y: startCenter.y - arrowOrigin.y },
|
|
265
|
+
end: { x: endCenter.x - arrowOrigin.x, y: endCenter.y - arrowOrigin.y },
|
|
266
|
+
bend: edge.bend
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
editor.createBindings([
|
|
270
|
+
makeArrowBinding(arrowId, startShapeId, "start", { x: 0.5, y: 0.5 }, false, false),
|
|
271
|
+
makeArrowBinding(arrowId, endShapeId, "end", { x: 0.5, y: 0.5 }, false, false)
|
|
272
|
+
]);
|
|
273
|
+
});
|
|
274
|
+
return arrowId;
|
|
275
|
+
}
|
|
276
|
+
function computeBlueprintBounds(nodes, lines) {
|
|
277
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
278
|
+
for (const node of nodes) {
|
|
279
|
+
if (node.parentId) continue;
|
|
280
|
+
minX = Math.min(minX, node.x);
|
|
281
|
+
minY = Math.min(minY, node.y);
|
|
282
|
+
maxX = Math.max(maxX, node.x + node.w);
|
|
283
|
+
maxY = Math.max(maxY, node.y + node.h);
|
|
284
|
+
}
|
|
285
|
+
if (lines) {
|
|
286
|
+
for (const line of lines) {
|
|
287
|
+
minX = Math.min(minX, line.x);
|
|
288
|
+
minY = Math.min(minY, line.y);
|
|
289
|
+
maxX = Math.max(maxX, line.x);
|
|
290
|
+
maxY = Math.max(maxY, line.y + line.endY);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return { minX, minY, maxX, maxY };
|
|
294
|
+
}
|
|
295
|
+
export {
|
|
296
|
+
renderBlueprint
|
|
297
|
+
};
|
|
298
|
+
//# sourceMappingURL=renderBlueprint.mjs.map
|