@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
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# @tldraw/mermaid
|
|
2
|
+
|
|
3
|
+
Convert [Mermaid](https://mermaid.js.org/) diagram syntax into native, editable tldraw shapes.
|
|
4
|
+
|
|
5
|
+
Instead of rendering a static SVG, `@tldraw/mermaid` parses Mermaid text and creates real geo shapes, arrows, frames, and groups on the tldraw canvas — so users can move, resize, restyle, and connect them like any other shape.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @tldraw/mermaid
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Peer dependencies:** your app must also include compatible versions of:
|
|
14
|
+
|
|
15
|
+
- **`tldraw`** — the SDK this package plugs into
|
|
16
|
+
- **`react`** — `^18.2.0` or `^19.2.1`
|
|
17
|
+
- **`react-dom`** — `^18.2.0` or `^19.2.1`
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { createMermaidDiagram } from '@tldraw/mermaid'
|
|
23
|
+
|
|
24
|
+
// Inside a component or callback with access to the editor:
|
|
25
|
+
await createMermaidDiagram(
|
|
26
|
+
editor,
|
|
27
|
+
`
|
|
28
|
+
flowchart TD
|
|
29
|
+
A[Start] --> B{Decision}
|
|
30
|
+
B -->|Yes| C[Do something]
|
|
31
|
+
B -->|No| D[Do something else]
|
|
32
|
+
`
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
By default, shapes are centered on the viewport. You can control placement with `blueprintRender`:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// Center the diagram on a specific point (default behavior)
|
|
40
|
+
await createMermaidDiagram(editor, diagramText, {
|
|
41
|
+
blueprintRender: {
|
|
42
|
+
position: { x: 500, y: 300 },
|
|
43
|
+
centerOnPosition: true,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Place the diagram's top-left corner at the given point
|
|
48
|
+
await createMermaidDiagram(editor, diagramText, {
|
|
49
|
+
blueprintRender: {
|
|
50
|
+
position: { x: 0, y: 0 },
|
|
51
|
+
centerOnPosition: false,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
When `centerOnPosition` is `true` (the default), the diagram's center aligns with the given position. When `false`, the diagram's top-left corner aligns with it instead. If no `position` is provided, the diagram uses the viewport center — or the cursor position if the user has "paste at cursor" mode enabled.
|
|
57
|
+
|
|
58
|
+
## Supported diagram types
|
|
59
|
+
|
|
60
|
+
| Diagram type | Mermaid keyword | What you get |
|
|
61
|
+
| ---------------- | -------------------- | ------------------------------------------------------------------- |
|
|
62
|
+
| Flowchart | `flowchart`, `graph` | Geo shapes, arrows, subgraph frames |
|
|
63
|
+
| Sequence diagram | `sequenceDiagram` | Actor shapes, lifelines, signal arrows, fragment frames |
|
|
64
|
+
| State diagram | `stateDiagram-v2` | State shapes, transitions, compound state frames, fork/join, choice |
|
|
65
|
+
| Mindmap | `mindmap` | Colored geo shapes, parent-child edges, tree hierarchy |
|
|
66
|
+
|
|
67
|
+
Unsupported diagram types (pie, gantt, class, ER, etc.) can be handled with the `onUnsupportedDiagram` callback — for example, to fall back to SVG import:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
await createMermaidDiagram(editor, text, {
|
|
71
|
+
onUnsupportedDiagram(svgString) {
|
|
72
|
+
editor.putExternalContent({ type: 'svg-text', text: svgString })
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## API
|
|
78
|
+
|
|
79
|
+
### `createMermaidDiagram(editor, text, options?)`
|
|
80
|
+
|
|
81
|
+
Parses Mermaid text, extracts layout from the rendered SVG, builds a blueprint, and creates tldraw shapes.
|
|
82
|
+
|
|
83
|
+
- **`editor`** — a tldraw `Editor` instance.
|
|
84
|
+
- **`text`** — Mermaid diagram source text.
|
|
85
|
+
- **`options`** — optional `MermaidDiagramOptions`:
|
|
86
|
+
- `mermaidConfig` — Mermaid configuration overrides (theme, spacing, etc.).
|
|
87
|
+
- `blueprintRender` — positioning options (`position`, `centerOnPosition`).
|
|
88
|
+
- `onUnsupportedDiagram(svg)` — callback when the diagram type isn't natively supported.
|
|
89
|
+
|
|
90
|
+
Throws `MermaidDiagramError` on parse failure or unsupported diagram type (if no callback is provided).
|
|
91
|
+
|
|
92
|
+
### `renderBlueprint(editor, blueprint, opts?)`
|
|
93
|
+
|
|
94
|
+
Renders a pre-built `DiagramMermaidBlueprint` into the editor. Useful if you want to construct or modify a blueprint programmatically before rendering.
|
|
95
|
+
|
|
96
|
+
### `MermaidDiagramError`
|
|
97
|
+
|
|
98
|
+
Error class with `type: 'parse' | 'unsupported'` and `diagramType` properties.
|
|
99
|
+
|
|
100
|
+
### Types
|
|
101
|
+
|
|
102
|
+
- **`DiagramMermaidBlueprint`** — intermediate representation: `{ nodes, edges, lines?, groups? }`.
|
|
103
|
+
- **`MermaidBlueprintGeoNode`** — a shape node with position, size, geo type, label, colors, and optional `parentId`.
|
|
104
|
+
- **`MermaidBlueprintEdge`** — an arrow with start/end node IDs, bend, arrowheads, dash style, and anchor positions.
|
|
105
|
+
- **`MermaidBlueprintLineNode`** — a vertical or horizontal line (used for sequence diagram lifelines).
|
|
106
|
+
- **`BlueprintRenderingOptions`** — position and centering options for `renderBlueprint`.
|
|
107
|
+
|
|
108
|
+
## How it works
|
|
109
|
+
|
|
110
|
+
The conversion happens in three stages:
|
|
111
|
+
|
|
112
|
+
```mermaid
|
|
113
|
+
flowchart LR
|
|
114
|
+
MermaidText["Mermaid text"] --> ParseRender["Parse and render SVG"]
|
|
115
|
+
ParseRender --> ExtractBlueprint["Extract blueprint"]
|
|
116
|
+
ExtractBlueprint --> CreateShapes["Create tldraw shapes"]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
1. **Parse and render** — Mermaid parses the text and renders an SVG into an offscreen DOM element, giving us accurate layout positions via `getBBox()`.
|
|
120
|
+
2. **Extract blueprint** — a diagram-specific converter reads positions from the SVG and semantic data from Mermaid's internal database, producing a `DiagramMermaidBlueprint`.
|
|
121
|
+
3. **Create shapes** — `renderBlueprint` converts the blueprint into tldraw geo shapes, arrows, lines, frames, and groups.
|
|
122
|
+
|
|
123
|
+
## Example: paste handler
|
|
124
|
+
|
|
125
|
+
A common integration is converting Mermaid text on paste. Here's a React component that registers itself as a text content handler — based on the pattern used on [tldraw.com](https://github.com/tldraw/tldraw/blob/main/apps/dotcom/client/src/components/SneakyMermaidHandler/SneakyMermaidHandler.tsx):
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { useEffect } from 'react'
|
|
129
|
+
import { defaultHandleExternalTextContent, useEditor } from 'tldraw'
|
|
130
|
+
|
|
131
|
+
const MERMAID_KEYWORD =
|
|
132
|
+
/^\s*(flowchart|graph|sequenceDiagram|stateDiagram|classDiagram|erDiagram|gantt|pie|gitGraph|mindmap)/
|
|
133
|
+
|
|
134
|
+
export function MermaidPasteHandler() {
|
|
135
|
+
const editor = useEditor()
|
|
136
|
+
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
editor.registerExternalContentHandler('text', async (content) => {
|
|
139
|
+
if (!MERMAID_KEYWORD.test(content.text)) {
|
|
140
|
+
await defaultHandleExternalTextContent(editor, content)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const { createMermaidDiagram } = await import('@tldraw/mermaid')
|
|
146
|
+
await createMermaidDiagram(editor, content.text, {
|
|
147
|
+
async onUnsupportedDiagram(svgString) {
|
|
148
|
+
await editor.putExternalContent({ type: 'svg-text', text: svgString })
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
} catch {
|
|
152
|
+
await defaultHandleExternalTextContent(editor, content)
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
}, [editor])
|
|
156
|
+
|
|
157
|
+
return null
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Drop `<MermaidPasteHandler />` inside your `<Tldraw>` component and users can paste Mermaid text directly onto the canvas.
|
|
162
|
+
|
|
163
|
+
The regex above is a simplified check. On tldraw.com the detection is handled by [`simpleMermaidStringTest`](https://github.com/tldraw/tldraw/blob/main/apps/dotcom/client/src/components/SneakyMermaidHandler/simpleMermaidStringTest.ts), which also strips YAML frontmatter (`---...---`), `%%{...}%%` directives, and `%%` comments before testing for the diagram keyword — making it more robust for real-world pasted content.
|
|
164
|
+
|
|
165
|
+
## Lazy loading
|
|
166
|
+
|
|
167
|
+
The `mermaid` dependency is roughly 2 MB. The paste handler above already lazy-loads with `await import('@tldraw/mermaid')` — the package is only fetched when the pasted text matches a Mermaid keyword. For your own integration, the same pattern applies: pre-screen with a lightweight regex, then dynamic-import the package only when needed.
|
|
168
|
+
|
|
169
|
+
## Examples
|
|
170
|
+
|
|
171
|
+
See the [Mermaid diagrams example](https://github.com/tldraw/tldraw/tree/main/apps/examples/src/examples/use-cases/hundred-mermaids) in the examples app for a runnable demo that renders many diagram types at once. Run it locally with `yarn dev` from the repo root and visit `localhost:5420`.
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
This project is part of the tldraw SDK. It is provided under the [tldraw SDK license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md).
|
|
176
|
+
|
|
177
|
+
## Trademarks
|
|
178
|
+
|
|
179
|
+
Copyright (c) 2024-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our [trademark guidelines](https://github.com/tldraw/tldraw/blob/main/TRADEMARKS.md) for info on acceptable usage.
|
|
180
|
+
|
|
181
|
+
## Distributions
|
|
182
|
+
|
|
183
|
+
You can find tldraw on npm [here](https://www.npmjs.com/package/@tldraw/tldraw?activeTab=versions).
|
|
184
|
+
|
|
185
|
+
## Contribution
|
|
186
|
+
|
|
187
|
+
Please see our [contributing guide](https://github.com/tldraw/tldraw/blob/main/CONTRIBUTING.md). Found a bug? Please [submit an issue](https://github.com/tldraw/tldraw/issues/new).
|
|
188
|
+
|
|
189
|
+
## Community
|
|
190
|
+
|
|
191
|
+
Have questions, comments or feedback? [Join our discord](https://discord.tldraw.com/?utm_source=github&utm_medium=readme&utm_campaign=sociallink). For the latest news and release notes, visit [tldraw.dev](https://tldraw.dev).
|
|
192
|
+
|
|
193
|
+
## Contact
|
|
194
|
+
|
|
195
|
+
Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var blueprint_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(blueprint_exports);
|
|
17
|
+
//# sourceMappingURL=blueprint.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/blueprint.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n\tTLArrowShapeArrowheadStyle,\n\tTLDefaultColorStyle,\n\tTLDefaultDashStyle,\n\tTLDefaultFillStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultSizeStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLGeoShapeGeoStyle,\n} from 'tldraw'\n\n/**\n * An intermediate representation of a parsed mermaid diagram as abstract nodes,\n * edges, and lines with layout positions and tldraw style props. Produced by\n * the diagram-specific converters and consumed by `renderBlueprint` to create\n * actual tldraw shapes on the canvas.\n *\n * @public\n */\nexport interface DiagramMermaidBlueprint {\n\tnodes: MermaidBlueprintGeoNode[]\n\tedges: MermaidBlueprintEdge[]\n\tlines?: MermaidBlueprintLineNode[]\n\tgroups?: string[][]\n}\n\n/** @public */\nexport interface MermaidBlueprintGeoNode {\n\tid: string\n\tx: number\n\ty: number\n\tw: number\n\th: number\n\tgeo: TLGeoShapeGeoStyle\n\tparentId?: string\n\tlabel?: string\n\tfill?: TLDefaultFillStyle\n\tcolor?: TLDefaultColorStyle\n\tdash?: TLDefaultDashStyle\n\tsize?: TLDefaultSizeStyle\n\talign?: TLDefaultHorizontalAlignStyle\n\tverticalAlign?: TLDefaultVerticalAlignStyle\n}\n\n/** @public */\nexport interface MermaidBlueprintEdge {\n\tstartNodeId: string\n\tendNodeId: string\n\tlabel?: string\n\tbend: number\n\tarrowheadEnd?: TLArrowShapeArrowheadStyle\n\tarrowheadStart?: TLArrowShapeArrowheadStyle\n\tdash?: TLDefaultDashStyle\n\tsize?: TLDefaultSizeStyle\n\tcolor?: TLDefaultColorStyle\n\tanchorStartY?: number\n\tanchorEndY?: number\n\tisExact?: boolean\n\tisPrecise?: boolean\n\tisExactEnd?: boolean\n\tisPreciseEnd?: boolean\n\tdecoration?: { type: 'autonumber'; value: string }\n}\n\n/** @public */\nexport interface MermaidBlueprintLineNode {\n\tid: string\n\tx: number\n\ty: number\n\tendX?: number\n\tendY: number\n\tdash?: TLDefaultDashStyle\n\tsize?: TLDefaultSizeStyle\n\tcolor?: TLDefaultColorStyle\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var colors_exports = {};
|
|
20
|
+
__export(colors_exports, {
|
|
21
|
+
buildClassDefColorMap: () => buildClassDefColorMap,
|
|
22
|
+
parseCssStyles: () => parseCssStyles,
|
|
23
|
+
parseNodeInlineColor: () => parseNodeInlineColor,
|
|
24
|
+
parseRgbToTldrawColor: () => parseRgbToTldrawColor
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(colors_exports);
|
|
27
|
+
var import_tlschema = require("@tldraw/tlschema");
|
|
28
|
+
function buildClassDefColorMap(classDefs, items) {
|
|
29
|
+
const result = /* @__PURE__ */ new Map();
|
|
30
|
+
if (classDefs.size === 0) return result;
|
|
31
|
+
for (const [nodeId, item] of items) {
|
|
32
|
+
if (!item.classes || item.classes.length === 0) continue;
|
|
33
|
+
for (const className of item.classes) {
|
|
34
|
+
const classDef = classDefs.get(className);
|
|
35
|
+
if (!classDef || classDef.styles.length === 0) continue;
|
|
36
|
+
const props = parseCssProps(classDef.styles);
|
|
37
|
+
const fill = toColor(props.get("fill"));
|
|
38
|
+
const stroke = toColor(props.get("stroke"));
|
|
39
|
+
if (!fill && !stroke) continue;
|
|
40
|
+
const colors = {};
|
|
41
|
+
if (fill) colors.fillColor = nearestTldrawColor(fill);
|
|
42
|
+
if (stroke) colors.strokeColor = nearestTldrawColor(stroke);
|
|
43
|
+
result.set(nodeId, colors);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
function parseRgbToTldrawColor(text) {
|
|
50
|
+
const color = toColor(text);
|
|
51
|
+
if (!color) return null;
|
|
52
|
+
return { color: nearestTldrawColor(color), hasAlpha: color[3] < 255 };
|
|
53
|
+
}
|
|
54
|
+
function parseCssProps(styles) {
|
|
55
|
+
const props = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const entry of styles) {
|
|
57
|
+
for (const part of entry.split(";")) {
|
|
58
|
+
const colon = part.indexOf(":");
|
|
59
|
+
if (colon < 0) continue;
|
|
60
|
+
const key = part.slice(0, colon).trim().toLowerCase();
|
|
61
|
+
const value = part.slice(colon + 1).trim();
|
|
62
|
+
if (key && value) props.set(key, value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return props;
|
|
66
|
+
}
|
|
67
|
+
function parseCssStyles(styles) {
|
|
68
|
+
if (!styles || styles.length === 0) return {};
|
|
69
|
+
const props = parseCssProps(styles);
|
|
70
|
+
const result = {};
|
|
71
|
+
const stroke = toColor(props.get("stroke"));
|
|
72
|
+
if (stroke) {
|
|
73
|
+
result.color = nearestTldrawColor(stroke);
|
|
74
|
+
}
|
|
75
|
+
if (props.has("stroke-dasharray")) {
|
|
76
|
+
result.dashOverride = "dashed";
|
|
77
|
+
}
|
|
78
|
+
const strokeWidth = props.get("stroke-width");
|
|
79
|
+
if (strokeWidth) {
|
|
80
|
+
const pixels = parseFloat(strokeWidth);
|
|
81
|
+
if (Number.isFinite(pixels)) {
|
|
82
|
+
if (pixels <= 1) result.sizeOverride = "s";
|
|
83
|
+
else if (pixels <= 2) result.sizeOverride = "m";
|
|
84
|
+
else result.sizeOverride = "l";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
function parseNodeInlineColor(styles) {
|
|
90
|
+
if (!styles || styles.length === 0) return void 0;
|
|
91
|
+
const props = parseCssProps(styles);
|
|
92
|
+
const fill = toColor(props.get("fill"));
|
|
93
|
+
const stroke = toColor(props.get("stroke"));
|
|
94
|
+
if (!fill && !stroke) return void 0;
|
|
95
|
+
const colors = {};
|
|
96
|
+
if (fill) colors.fillColor = nearestTldrawColor(fill);
|
|
97
|
+
if (stroke) colors.strokeColor = nearestTldrawColor(stroke);
|
|
98
|
+
return colors;
|
|
99
|
+
}
|
|
100
|
+
function parseHexToRgb(hex) {
|
|
101
|
+
const stripped = hex.replace(/^#/, "");
|
|
102
|
+
if (stripped.length === 3 || stripped.length === 4) {
|
|
103
|
+
return [
|
|
104
|
+
parseInt(stripped[0] + stripped[0], 16),
|
|
105
|
+
parseInt(stripped[1] + stripped[1], 16),
|
|
106
|
+
parseInt(stripped[2] + stripped[2], 16)
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
if (stripped.length === 6 || stripped.length === 8) {
|
|
110
|
+
return [
|
|
111
|
+
parseInt(stripped.slice(0, 2), 16),
|
|
112
|
+
parseInt(stripped.slice(2, 4), 16),
|
|
113
|
+
parseInt(stripped.slice(4, 6), 16)
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const TLDRAW_PALETTE = import_tlschema.defaultColorNames.map(
|
|
119
|
+
(name) => {
|
|
120
|
+
const { solid } = import_tlschema.DefaultColorThemePalette.lightMode[name];
|
|
121
|
+
const rgb = parseHexToRgb(solid);
|
|
122
|
+
return [name, rgb[0], rgb[1], rgb[2]];
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
function nearestTldrawColor(rgb) {
|
|
126
|
+
let [r, g, b] = rgb;
|
|
127
|
+
const max = Math.max(r, g, b);
|
|
128
|
+
const min = Math.min(r, g, b);
|
|
129
|
+
const lightness = (max + min) / 2 / 255;
|
|
130
|
+
const chroma = max - min;
|
|
131
|
+
if (lightness > 0.75 && chroma > 5) {
|
|
132
|
+
const target = 200;
|
|
133
|
+
r = Math.round((r - min) / chroma * target);
|
|
134
|
+
g = Math.round((g - min) / chroma * target);
|
|
135
|
+
b = Math.round((b - min) / chroma * target);
|
|
136
|
+
}
|
|
137
|
+
let best = "black";
|
|
138
|
+
let bestDistance = Infinity;
|
|
139
|
+
for (const [name, red, green, blue] of TLDRAW_PALETTE) {
|
|
140
|
+
const rMean = (r + red) / 2;
|
|
141
|
+
const dR = r - red;
|
|
142
|
+
const dG = g - green;
|
|
143
|
+
const dB = b - blue;
|
|
144
|
+
const distance = (2 + rMean / 256) * dR * dR + 4 * dG * dG + (2 + (255 - rMean) / 256) * dB * dB;
|
|
145
|
+
if (distance < bestDistance) {
|
|
146
|
+
bestDistance = distance;
|
|
147
|
+
best = name;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return best;
|
|
151
|
+
}
|
|
152
|
+
function toColor(value) {
|
|
153
|
+
if (!value) return void 0;
|
|
154
|
+
const trimmed = value.trim();
|
|
155
|
+
if (!trimmed || trimmed === "none" || trimmed === "transparent") return void 0;
|
|
156
|
+
if (trimmed.startsWith("rgb")) {
|
|
157
|
+
const match = trimmed.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)/);
|
|
158
|
+
if (!match) return void 0;
|
|
159
|
+
return [
|
|
160
|
+
parseInt(match[1], 10),
|
|
161
|
+
parseInt(match[2], 10),
|
|
162
|
+
parseInt(match[3], 10),
|
|
163
|
+
match[4] !== void 0 ? Math.round(parseFloat(match[4]) * 255) : 255
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
if (trimmed.startsWith("#")) {
|
|
167
|
+
const rgb = parseHexToRgb(trimmed);
|
|
168
|
+
if (!rgb) return void 0;
|
|
169
|
+
return [rgb[0], rgb[1], rgb[2], 255];
|
|
170
|
+
}
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=colors.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/colors.ts"],
|
|
4
|
+
"sourcesContent": ["import { defaultColorNames, DefaultColorThemePalette } from '@tldraw/tlschema'\nimport { TLDefaultColorStyle, TLDefaultDashStyle, TLDefaultSizeStyle } from 'tldraw'\n\ntype Color = [number, number, number, number]\n\nexport interface ParsedNodeColors {\n\tfillColor?: TLDefaultColorStyle\n\tstrokeColor?: TLDefaultColorStyle\n}\n\n/**\n * Build a map of node id \u2192 parsed fill/stroke colors from Mermaid's classDef definitions.\n *\n * Uses the structured data from `db.getClasses()` and each node's `classes`\n * array. For each node, looks up its applied classDef styles and maps fill and\n * stroke independently to the nearest tldraw palette color.\n */\nexport function buildClassDefColorMap(\n\tclassDefs: Map<string, { styles: string[] }>,\n\titems: Iterable<[string, { classes?: string[] }]>\n): Map<string, ParsedNodeColors> {\n\tconst result = new Map<string, ParsedNodeColors>()\n\tif (classDefs.size === 0) return result\n\n\tfor (const [nodeId, item] of items) {\n\t\tif (!item.classes || item.classes.length === 0) continue\n\n\t\tfor (const className of item.classes) {\n\t\t\tconst classDef = classDefs.get(className)\n\t\t\tif (!classDef || classDef.styles.length === 0) continue\n\n\t\t\tconst props = parseCssProps(classDef.styles)\n\t\t\tconst fill = toColor(props.get('fill'))\n\t\t\tconst stroke = toColor(props.get('stroke'))\n\n\t\t\tif (!fill && !stroke) continue\n\n\t\t\tconst colors: ParsedNodeColors = {}\n\t\t\tif (fill) colors.fillColor = nearestTldrawColor(fill)\n\t\t\tif (stroke) colors.strokeColor = nearestTldrawColor(stroke)\n\t\t\tresult.set(nodeId, colors)\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn result\n}\n\nexport function parseRgbToTldrawColor(\n\ttext: string\n): { color: TLDefaultColorStyle; hasAlpha: boolean } | null {\n\tconst color = toColor(text)\n\tif (!color) return null\n\treturn { color: nearestTldrawColor(color), hasAlpha: color[3] < 255 }\n}\n\ninterface ParsedCssOverrides {\n\tcolor?: TLDefaultColorStyle\n\tdashOverride?: TLDefaultDashStyle\n\tsizeOverride?: TLDefaultSizeStyle\n}\n\nfunction parseCssProps(styles: string[]): Map<string, string> {\n\tconst props = new Map<string, string>()\n\tfor (const entry of styles) {\n\t\tfor (const part of entry.split(';')) {\n\t\t\tconst colon = part.indexOf(':')\n\t\t\tif (colon < 0) continue\n\t\t\tconst key = part.slice(0, colon).trim().toLowerCase()\n\t\t\tconst value = part.slice(colon + 1).trim()\n\t\t\tif (key && value) props.set(key, value)\n\t\t}\n\t}\n\treturn props\n}\n\n/**\n * Parse a Mermaid CSS style array from an edge (FlowEdge.style / linkStyle)\n * and return tldraw-compatible overrides.\n */\nexport function parseCssStyles(styles: string[] | undefined): ParsedCssOverrides {\n\tif (!styles || styles.length === 0) return {}\n\n\tconst props = parseCssProps(styles)\n\tconst result: ParsedCssOverrides = {}\n\n\tconst stroke = toColor(props.get('stroke'))\n\tif (stroke) {\n\t\tresult.color = nearestTldrawColor(stroke)\n\t}\n\n\tif (props.has('stroke-dasharray')) {\n\t\tresult.dashOverride = 'dashed'\n\t}\n\n\tconst strokeWidth = props.get('stroke-width')\n\tif (strokeWidth) {\n\t\tconst pixels = parseFloat(strokeWidth)\n\t\tif (Number.isFinite(pixels)) {\n\t\t\tif (pixels <= 1) result.sizeOverride = 's'\n\t\t\telse if (pixels <= 2) result.sizeOverride = 'm'\n\t\t\telse result.sizeOverride = 'l'\n\t\t}\n\t}\n\n\treturn result\n}\n\n/**\n * Parse inline `style nodeId fill:\u2026,stroke:\u2026` directives from a FlowVertex.styles\n * array and return fill and stroke as independent tldraw colors.\n */\nexport function parseNodeInlineColor(styles: string[] | undefined): ParsedNodeColors | undefined {\n\tif (!styles || styles.length === 0) return undefined\n\n\tconst props = parseCssProps(styles)\n\tconst fill = toColor(props.get('fill'))\n\tconst stroke = toColor(props.get('stroke'))\n\n\tif (!fill && !stroke) return undefined\n\n\tconst colors: ParsedNodeColors = {}\n\tif (fill) colors.fillColor = nearestTldrawColor(fill)\n\tif (stroke) colors.strokeColor = nearestTldrawColor(stroke)\n\treturn colors\n}\n\nfunction parseHexToRgb(hex: string): [number, number, number] | null {\n\tconst stripped = hex.replace(/^#/, '')\n\tif (stripped.length === 3 || stripped.length === 4) {\n\t\treturn [\n\t\t\tparseInt(stripped[0] + stripped[0], 16),\n\t\t\tparseInt(stripped[1] + stripped[1], 16),\n\t\t\tparseInt(stripped[2] + stripped[2], 16),\n\t\t]\n\t}\n\tif (stripped.length === 6 || stripped.length === 8) {\n\t\treturn [\n\t\t\tparseInt(stripped.slice(0, 2), 16),\n\t\t\tparseInt(stripped.slice(2, 4), 16),\n\t\t\tparseInt(stripped.slice(4, 6), 16),\n\t\t]\n\t}\n\treturn null\n}\n\nconst TLDRAW_PALETTE: [TLDefaultColorStyle, number, number, number][] = defaultColorNames.map(\n\t(name) => {\n\t\tconst { solid } = DefaultColorThemePalette.lightMode[name]\n\t\tconst rgb = parseHexToRgb(solid)!\n\t\treturn [name, rgb[0], rgb[1], rgb[2]]\n\t}\n)\n\n/** Map an arbitrary Color tuple to the nearest tldraw named color (best-effort). */\nfunction nearestTldrawColor(rgb: Color): TLDefaultColorStyle {\n\tlet [r, g, b] = rgb\n\n\tconst max = Math.max(r, g, b)\n\tconst min = Math.min(r, g, b)\n\tconst lightness = (max + min) / 2 / 255\n\tconst chroma = max - min\n\n\t// For very light pastels, strip the white base and amplify the\n\t// chromatic signal so the distance metric can see the hue.\n\tif (lightness > 0.75 && chroma > 5) {\n\t\tconst target = 200\n\t\tr = Math.round(((r - min) / chroma) * target)\n\t\tg = Math.round(((g - min) / chroma) * target)\n\t\tb = Math.round(((b - min) / chroma) * target)\n\t}\n\n\tlet best: TLDefaultColorStyle = 'black'\n\tlet bestDistance = Infinity\n\tfor (const [name, red, green, blue] of TLDRAW_PALETTE) {\n\t\t// \"Redmean\" weighted Euclidean distance (Compuphase approximation).\n\t\t// Weights RGB channels by the average red value of the two colors to\n\t\t// approximate human perception, which is more sensitive to green and\n\t\t// varies in red/blue sensitivity depending on the color's warmth.\n\t\tconst rMean = (r + red) / 2\n\t\tconst dR = r - red\n\t\tconst dG = g - green\n\t\tconst dB = b - blue\n\t\tconst distance = (2 + rMean / 256) * dR * dR + 4 * dG * dG + (2 + (255 - rMean) / 256) * dB * dB\n\t\tif (distance < bestDistance) {\n\t\t\tbestDistance = distance\n\t\t\tbest = name\n\t\t}\n\t}\n\treturn best\n}\n\nfunction toColor(value: string | undefined): Color | undefined {\n\tif (!value) return undefined\n\n\tconst trimmed = value.trim()\n\tif (!trimmed || trimmed === 'none' || trimmed === 'transparent') return undefined\n\n\tif (trimmed.startsWith('rgb')) {\n\t\tconst match = trimmed.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*([\\d.]+)\\s*)?\\)/)\n\t\tif (!match) return undefined\n\t\treturn [\n\t\t\tparseInt(match[1], 10),\n\t\t\tparseInt(match[2], 10),\n\t\t\tparseInt(match[3], 10),\n\t\t\tmatch[4] !== undefined ? Math.round(parseFloat(match[4]) * 255) : 255,\n\t\t]\n\t}\n\tif (trimmed.startsWith('#')) {\n\t\tconst rgb = parseHexToRgb(trimmed)\n\t\tif (!rgb) return undefined\n\t\treturn [rgb[0], rgb[1], rgb[2], 255]\n\t}\n\treturn undefined\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var createMermaidDiagram_exports = {};
|
|
30
|
+
__export(createMermaidDiagram_exports, {
|
|
31
|
+
MermaidDiagramError: () => MermaidDiagramError,
|
|
32
|
+
createMermaidDiagram: () => createMermaidDiagram
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(createMermaidDiagram_exports);
|
|
35
|
+
var import_mermaid = __toESM(require("mermaid"), 1);
|
|
36
|
+
var import_flowchartDiagram = require("./flowchartDiagram");
|
|
37
|
+
var import_mindmapDiagram = require("./mindmapDiagram");
|
|
38
|
+
var import_renderBlueprint = require("./renderBlueprint");
|
|
39
|
+
var import_sequenceDiagram = require("./sequenceDiagram");
|
|
40
|
+
var import_stateDiagram = require("./stateDiagram");
|
|
41
|
+
let nextMermaidId = 0;
|
|
42
|
+
class MermaidDiagramError extends Error {
|
|
43
|
+
constructor(diagramType, type) {
|
|
44
|
+
super(`mermaid diagram error: ${diagramType}`);
|
|
45
|
+
this.diagramType = diagramType;
|
|
46
|
+
this.type = type;
|
|
47
|
+
this.name = "MermaidDiagramError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const FONT_INFLATE = 1.4;
|
|
51
|
+
const MERMAID_CONFIG = {
|
|
52
|
+
startOnLoad: false,
|
|
53
|
+
flowchart: { nodeSpacing: 80, rankSpacing: 80, padding: 20 },
|
|
54
|
+
state: { nodeSpacing: 80, rankSpacing: 80, padding: 20 },
|
|
55
|
+
mindmap: { padding: 20 },
|
|
56
|
+
sequence: { actorMargin: 50, noteMargin: 20 },
|
|
57
|
+
themeVariables: { fontSize: `${18 * FONT_INFLATE}px` }
|
|
58
|
+
};
|
|
59
|
+
async function createMermaidDiagram(editor, text, options = {}) {
|
|
60
|
+
import_mermaid.default.initialize({
|
|
61
|
+
...MERMAID_CONFIG,
|
|
62
|
+
...options.mermaidConfig ?? {},
|
|
63
|
+
flowchart: { ...MERMAID_CONFIG.flowchart, ...options.mermaidConfig?.flowchart },
|
|
64
|
+
state: { ...MERMAID_CONFIG.state, ...options.mermaidConfig?.state },
|
|
65
|
+
mindmap: { ...MERMAID_CONFIG.mindmap, ...options.mermaidConfig?.mindmap },
|
|
66
|
+
sequence: { ...MERMAID_CONFIG.sequence, ...options.mermaidConfig?.sequence },
|
|
67
|
+
themeVariables: { ...MERMAID_CONFIG.themeVariables, ...options.mermaidConfig?.themeVariables }
|
|
68
|
+
});
|
|
69
|
+
const parsedResult = await import_mermaid.default.parse(text, { suppressErrors: true });
|
|
70
|
+
if (!parsedResult) {
|
|
71
|
+
throw new MermaidDiagramError("not a mermaid diagram", "parse");
|
|
72
|
+
}
|
|
73
|
+
const offscreen = document.createElement("div");
|
|
74
|
+
offscreen.style.position = "absolute";
|
|
75
|
+
offscreen.style.left = "-9999px";
|
|
76
|
+
offscreen.style.top = "-9999px";
|
|
77
|
+
offscreen.style.overflow = "hidden";
|
|
78
|
+
document.body.appendChild(offscreen);
|
|
79
|
+
try {
|
|
80
|
+
const parsedSvg = (await import_mermaid.default.render(`mermaid-${nextMermaidId++}`, text, offscreen)).svg;
|
|
81
|
+
let liveSvg = offscreen.querySelector("svg");
|
|
82
|
+
if (!liveSvg) {
|
|
83
|
+
offscreen.innerHTML = parsedSvg;
|
|
84
|
+
liveSvg = offscreen.querySelector("svg");
|
|
85
|
+
if (!liveSvg) {
|
|
86
|
+
throw new MermaidDiagramError(parsedResult.diagramType, "parse");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const diagramResult = await import_mermaid.default.mermaidAPI.getDiagramFromText(text);
|
|
90
|
+
let blueprint;
|
|
91
|
+
switch (parsedResult.diagramType) {
|
|
92
|
+
case "flowchart-v2": {
|
|
93
|
+
const db = diagramResult.db;
|
|
94
|
+
const vertices = db.getVertices();
|
|
95
|
+
const edges = db.getEdges();
|
|
96
|
+
const subGraphs = db.getSubGraphs();
|
|
97
|
+
const classes = db.getClasses();
|
|
98
|
+
const layout = (0, import_flowchartDiagram.parseFlowchartLayout)(liveSvg);
|
|
99
|
+
blueprint = (0, import_flowchartDiagram.flowchartToBlueprint)(layout, vertices, edges, subGraphs, classes);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "sequence": {
|
|
103
|
+
const db = diagramResult.db;
|
|
104
|
+
const actors = db.getActors();
|
|
105
|
+
const actorKeys = db.getActorKeys();
|
|
106
|
+
const messages = db.getMessages();
|
|
107
|
+
const layout = (0, import_sequenceDiagram.parseSequenceLayout)(liveSvg, actorKeys.length, (0, import_sequenceDiagram.countSequenceEvents)(messages));
|
|
108
|
+
blueprint = (0, import_sequenceDiagram.sequenceToBlueprint)(
|
|
109
|
+
layout,
|
|
110
|
+
actors,
|
|
111
|
+
actorKeys,
|
|
112
|
+
messages,
|
|
113
|
+
db.getCreatedActors(),
|
|
114
|
+
db.getDestroyedActors()
|
|
115
|
+
);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "state":
|
|
119
|
+
case "stateDiagram": {
|
|
120
|
+
const db = diagramResult.db;
|
|
121
|
+
const states = db.getStates();
|
|
122
|
+
const relations = db.getRelations();
|
|
123
|
+
const classes = db.getClasses();
|
|
124
|
+
const layout = (0, import_stateDiagram.parseStateDiagramLayout)(liveSvg);
|
|
125
|
+
blueprint = (0, import_stateDiagram.stateToBlueprint)(layout, states, relations, classes);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case "mindmap": {
|
|
129
|
+
const db = diagramResult.db;
|
|
130
|
+
const tree = db.getMindmap();
|
|
131
|
+
if (tree) {
|
|
132
|
+
db.assignSections(tree);
|
|
133
|
+
const layout = (0, import_mindmapDiagram.parseMindmapLayout)(liveSvg);
|
|
134
|
+
blueprint = (0, import_mindmapDiagram.mindmapToBlueprint)(layout, tree, liveSvg);
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
if (options.onUnsupportedDiagram) {
|
|
140
|
+
await options.onUnsupportedDiagram(parsedSvg);
|
|
141
|
+
} else {
|
|
142
|
+
throw new MermaidDiagramError(parsedResult.diagramType, "unsupported");
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
if (blueprint) {
|
|
147
|
+
(0, import_renderBlueprint.renderBlueprint)(editor, blueprint, options.blueprintRender);
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (e instanceof MermaidDiagramError) throw e;
|
|
151
|
+
console.error(e);
|
|
152
|
+
throw new MermaidDiagramError(parsedResult.diagramType, "parse");
|
|
153
|
+
} finally {
|
|
154
|
+
offscreen.remove();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=createMermaidDiagram.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/createMermaidDiagram.ts"],
|
|
4
|
+
"sourcesContent": ["let nextMermaidId = 0\n\nimport mermaid from 'mermaid'\nimport type { FlowDB } from 'mermaid/dist/diagrams/flowchart/flowDb.d.ts'\nimport type { FlowEdge, FlowSubGraph, FlowVertex } from 'mermaid/dist/diagrams/flowchart/types.js'\nimport type { MindmapDB } from 'mermaid/dist/diagrams/mindmap/mindmapDb.d.ts'\nimport type { SequenceDB } from 'mermaid/dist/diagrams/sequence/sequenceDb.d.ts'\nimport type { StateDB } from 'mermaid/dist/diagrams/state/stateDb.d.ts'\nimport { Editor } from 'tldraw'\nimport { flowchartToBlueprint, parseFlowchartLayout } from './flowchartDiagram'\nimport { mindmapToBlueprint, parseMindmapLayout } from './mindmapDiagram'\nimport { BlueprintRenderingOptions, renderBlueprint } from './renderBlueprint'\nimport { countSequenceEvents, parseSequenceLayout, sequenceToBlueprint } from './sequenceDiagram'\nimport { parseStateDiagramLayout, stateToBlueprint } from './stateDiagram'\n\n/** @public */\nexport class MermaidDiagramError extends Error {\n\tconstructor(\n\t\tpublic diagramType: string,\n\t\tpublic type: 'parse' | 'unsupported'\n\t) {\n\t\tsuper(`mermaid diagram error: ${diagramType}`)\n\t\tthis.name = 'MermaidDiagramError'\n\t}\n}\n\n// Inflate the font size so Mermaid's layout engine allocates larger nodes,\n// compensating for tldraw's hand-drawn font being wider than Mermaid's default.\nconst FONT_INFLATE = 1.4\n\nconst MERMAID_CONFIG = {\n\tstartOnLoad: false,\n\tflowchart: { nodeSpacing: 80, rankSpacing: 80, padding: 20 },\n\tstate: { nodeSpacing: 80, rankSpacing: 80, padding: 20 },\n\tmindmap: { padding: 20 },\n\tsequence: { actorMargin: 50, noteMargin: 20 },\n\tthemeVariables: { fontSize: `${18 * FONT_INFLATE}px` },\n}\n\n/** @public */\nexport interface MermaidDiagramOptions {\n\tmermaidConfig?: Record<string, any>\n\tblueprintRender?: BlueprintRenderingOptions\n\tonUnsupportedDiagram?(svg: string): Promise<void>\n}\n\n/**\n * Parse mermaid text and create tldraw shapes for supported diagram types.\n * Returns the SVG string for supported diagrams, or `null` when the diagram type\n * is unsupported (after calling `onUnsupportedDiagram` if provided).\n * Throws {@link MermaidDiagramError} if parsing fails.\n * @public\n */\nexport async function createMermaidDiagram(\n\teditor: Editor,\n\ttext: string,\n\toptions: MermaidDiagramOptions = {}\n): Promise<void> {\n\tmermaid.initialize({\n\t\t...MERMAID_CONFIG,\n\t\t...(options.mermaidConfig ?? {}),\n\t\tflowchart: { ...MERMAID_CONFIG.flowchart, ...options.mermaidConfig?.flowchart },\n\t\tstate: { ...MERMAID_CONFIG.state, ...options.mermaidConfig?.state },\n\t\tmindmap: { ...MERMAID_CONFIG.mindmap, ...options.mermaidConfig?.mindmap },\n\t\tsequence: { ...MERMAID_CONFIG.sequence, ...options.mermaidConfig?.sequence },\n\t\tthemeVariables: { ...MERMAID_CONFIG.themeVariables, ...options.mermaidConfig?.themeVariables },\n\t})\n\n\tconst parsedResult = await mermaid.parse(text, { suppressErrors: true })\n\n\tif (!parsedResult) {\n\t\tthrow new MermaidDiagramError('not a mermaid diagram', 'parse')\n\t}\n\n\tconst offscreen = document.createElement('div')\n\toffscreen.style.position = 'absolute'\n\toffscreen.style.left = '-9999px'\n\toffscreen.style.top = '-9999px'\n\toffscreen.style.overflow = 'hidden'\n\tdocument.body.appendChild(offscreen)\n\n\ttry {\n\t\tconst parsedSvg = (await mermaid.render(`mermaid-${nextMermaidId++}`, text, offscreen)).svg\n\n\t\t// Reuse the live SVG that mermaid.render() already mounted into the\n\t\t// offscreen container. This avoids a second DOM mount and ensures\n\t\t// getBBox() works for every diagram type (state diagrams in particular\n\t\t// lack explicit dimension attributes and rely on live layout).\n\t\tlet liveSvg = offscreen.querySelector('svg')\n\n\t\tif (!liveSvg) {\n\t\t\toffscreen.innerHTML = parsedSvg\n\t\t\tliveSvg = offscreen.querySelector('svg')\n\t\t\tif (!liveSvg) {\n\t\t\t\tthrow new MermaidDiagramError(parsedResult.diagramType, 'parse')\n\t\t\t}\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tconst diagramResult = await mermaid.mermaidAPI.getDiagramFromText(text)\n\n\t\tlet blueprint\n\t\tswitch (parsedResult.diagramType) {\n\t\t\tcase 'flowchart-v2': {\n\t\t\t\tconst db = diagramResult.db as FlowDB\n\t\t\t\tconst vertices = db.getVertices() as Map<string, FlowVertex>\n\t\t\t\tconst edges = db.getEdges() as FlowEdge[]\n\t\t\t\tconst subGraphs = db.getSubGraphs() as FlowSubGraph[]\n\t\t\t\tconst classes = db.getClasses()\n\t\t\t\tconst layout = parseFlowchartLayout(liveSvg)\n\t\t\t\tblueprint = flowchartToBlueprint(layout, vertices, edges, subGraphs, classes)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'sequence': {\n\t\t\t\tconst db = diagramResult.db as SequenceDB\n\t\t\t\tconst actors = db.getActors()\n\t\t\t\tconst actorKeys = db.getActorKeys()\n\t\t\t\tconst messages = db.getMessages()\n\t\t\t\tconst layout = parseSequenceLayout(liveSvg, actorKeys.length, countSequenceEvents(messages))\n\t\t\t\tblueprint = sequenceToBlueprint(\n\t\t\t\t\tlayout,\n\t\t\t\t\tactors,\n\t\t\t\t\tactorKeys,\n\t\t\t\t\tmessages,\n\t\t\t\t\tdb.getCreatedActors(),\n\t\t\t\t\tdb.getDestroyedActors()\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'state':\n\t\t\tcase 'stateDiagram': {\n\t\t\t\tconst db = diagramResult.db as StateDB\n\t\t\t\tconst states = db.getStates()\n\t\t\t\tconst relations = db.getRelations()\n\t\t\t\tconst classes = db.getClasses()\n\t\t\t\tconst layout = parseStateDiagramLayout(liveSvg)\n\t\t\t\tblueprint = stateToBlueprint(layout, states, relations, classes)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'mindmap': {\n\t\t\t\tconst db = diagramResult.db as MindmapDB\n\t\t\t\tconst tree = db.getMindmap()\n\t\t\t\tif (tree) {\n\t\t\t\t\tdb.assignSections(tree)\n\t\t\t\t\tconst layout = parseMindmapLayout(liveSvg)\n\t\t\t\t\tblueprint = mindmapToBlueprint(layout, tree, liveSvg)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tif (options.onUnsupportedDiagram) {\n\t\t\t\t\tawait options.onUnsupportedDiagram(parsedSvg)\n\t\t\t\t} else {\n\t\t\t\t\tthrow new MermaidDiagramError(parsedResult.diagramType, 'unsupported')\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\n\t\tif (blueprint) {\n\t\t\trenderBlueprint(editor, blueprint, options.blueprintRender)\n\t\t}\n\t} catch (e) {\n\t\tif (e instanceof MermaidDiagramError) throw e\n\t\tconsole.error(e)\n\t\tthrow new MermaidDiagramError(parsedResult.diagramType, 'parse')\n\t} finally {\n\t\toffscreen.remove()\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["mermaid"]
|
|
7
|
+
}
|