mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-22 01:36:43 +02:00
Compare commits
75 Commits
@mermaid-j
...
knsv/mindm
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a700e8bf97 | ||
![]() |
61d9143acb | ||
![]() |
c88f74a6ee | ||
![]() |
6377d6f64d | ||
![]() |
1b0bc05fc2 | ||
![]() |
45edeb9307 | ||
![]() |
211974b2b7 | ||
![]() |
1f5ad3e315 | ||
![]() |
d7848e8a3d | ||
![]() |
89b9f0df70 | ||
![]() |
e9011567bd | ||
![]() |
0429970d58 | ||
![]() |
ecad9cee6c | ||
![]() |
1e8a9f76a9 | ||
![]() |
e42fdf1c54 | ||
![]() |
c75566ddc3 | ||
![]() |
7e9577dffd | ||
![]() |
180dc7bdff | ||
![]() |
cc9581842d | ||
![]() |
d782e4bb17 | ||
![]() |
ba9ad9385b | ||
![]() |
91edfa40f7 | ||
![]() |
c8b00bb929 | ||
![]() |
57eadbf6af | ||
![]() |
a906adce26 | ||
![]() |
a6d26ef6c3 | ||
![]() |
2b3f94eb7d | ||
![]() |
dd36046e23 | ||
![]() |
1507435e15 | ||
![]() |
68c01b76bf | ||
![]() |
28717e108d | ||
![]() |
688d9b383d | ||
![]() |
e68424d748 | ||
![]() |
204a9a338f | ||
![]() |
6a6a39ff33 | ||
![]() |
b296db9a33 | ||
![]() |
01ce84d8ee | ||
![]() |
f48e663d4c | ||
![]() |
a4aa2bd355 | ||
![]() |
b51b9d50c2 | ||
![]() |
b61780f735 | ||
![]() |
074f18dfb8 | ||
![]() |
d7308b0f43 | ||
![]() |
2f1860386a | ||
![]() |
f0bca7da55 | ||
![]() |
6fcdf5bfcc | ||
![]() |
e2ce0450c1 | ||
![]() |
c95c64139d | ||
![]() |
a7f12f1baa | ||
![]() |
2a8653de2b | ||
![]() |
a92c3bb251 | ||
![]() |
3677abe9e5 | ||
![]() |
95847ad236 | ||
![]() |
e0152fb873 | ||
![]() |
2298b96d8e | ||
![]() |
5db83365b6 | ||
![]() |
341a81a113 | ||
![]() |
8a62b4cace | ||
![]() |
ccafc20917 | ||
![]() |
d5cb4eaa59 | ||
![]() |
425fb7ee33 | ||
![]() |
cd6f8e5a24 | ||
![]() |
f0445b74d1 | ||
![]() |
ba52eef257 | ||
![]() |
c13ce2a5c0 | ||
![]() |
d2463f41b5 | ||
![]() |
eadb343292 | ||
![]() |
e7208622f7 | ||
![]() |
fbae611406 | ||
![]() |
34027bc589 | ||
![]() |
f2eef37599 | ||
![]() |
1e3ea13323 | ||
![]() |
4c8c48cde9 | ||
![]() |
c8e50276e8 | ||
![]() |
1e6419a63f |
@@ -33,6 +33,11 @@ export const packageOptions = {
|
||||
packageName: 'mermaid-layout-elk',
|
||||
file: 'layouts.ts',
|
||||
},
|
||||
'mermaid-layout-tidy-tree': {
|
||||
name: 'mermaid-layout-tidy-tree',
|
||||
packageName: 'mermaid-layout-tidy-tree',
|
||||
file: 'index.ts',
|
||||
},
|
||||
examples: {
|
||||
name: 'mermaid-examples',
|
||||
packageName: 'examples',
|
||||
|
5
.changeset/beige-peas-shave.md
Normal file
5
.changeset/beige-peas-shave.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@mermaid-js/mermaid-zenuml': patch
|
||||
---
|
||||
|
||||
Fixed a critical bug that the ZenUML diagram is not rendered.
|
5
.changeset/chatty-lemons-stick.md
Normal file
5
.changeset/chatty-lemons-stick.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
chore: Remove the "-beta" suffix from the XYChart, Block, Sankey diagrams to reflect their stable status
|
5
.changeset/cold-sites-accept.md
Normal file
5
.changeset/cold-sites-accept.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Position the edge label in state diagram correctly relative to the edge
|
5
.changeset/empty-clouds-cry.md
Normal file
5
.changeset/empty-clouds-cry.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Apply correct dateFormat in Gantt chart to show only day when specified
|
5
.changeset/fuzzy-pears-cough.md
Normal file
5
.changeset/fuzzy-pears-cough.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: handle exclude dates properly in Gantt charts when using dateFormat: 'YYYY-MM-DD HH:mm:ss'
|
5
.changeset/gold-spiders-join.md
Normal file
5
.changeset/gold-spiders-join.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: fixed connection gaps in flowchart for roundedRect, stadium and diamond shape
|
7
.changeset/hungry-guests-drive.md
Normal file
7
.changeset/hungry-guests-drive.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
'@mermaid-js/layout-tidy-tree': minor
|
||||
'@mermaid-js/layout-elk': minor
|
||||
---
|
||||
|
||||
feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
|
5
.changeset/large-mirrors-cheer.md
Normal file
5
.changeset/large-mirrors-cheer.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Update casing of ID in requirement diagram
|
5
.changeset/lemon-masks-unite.md
Normal file
5
.changeset/lemon-masks-unite.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Added support for per link curve styling in flowchart diagram using edge ids
|
5
.changeset/light-flowers-judge.md
Normal file
5
.changeset/light-flowers-judge.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Make flowchart elk detector regex match less greedy
|
8
.changeset/lovely-queens-own.md
Normal file
8
.changeset/lovely-queens-own.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(block): overflowing blocks no longer affect later lines
|
||||
|
||||
This may change the layout of block diagrams that have overflowing lines
|
||||
(i.e. block diagrams that use up more columns that the `columns` specifier).
|
7
.changeset/ninety-roses-turn.md
Normal file
7
.changeset/ninety-roses-turn.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: log warning for blocks exceeding column width
|
||||
|
||||
This update adds a validation check that logs a warning message when a block's width exceeds the defined column layout.
|
5
.changeset/rare-women-fly.md
Normal file
5
.changeset/rare-women-fly.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Add escaped class literal name on namespace
|
5
.changeset/red-zebras-happen.md
Normal file
5
.changeset/red-zebras-happen.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Allow equals sign in sequenceDiagram labels
|
9
.changeset/seven-papayas-film.md
Normal file
9
.changeset/seven-papayas-film.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
Add validation for negative values in pie charts:
|
||||
|
||||
Prevents crashes during parsing by validating values post-parsing.
|
||||
|
||||
Provides clearer, user-friendly error messages for invalid negative inputs.
|
5
.changeset/silver-eyes-build.md
Normal file
5
.changeset/silver-eyes-build.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
chore: migrate to class-based ArchitectureDB implementation
|
5
.changeset/six-planets-rescue.md
Normal file
5
.changeset/six-planets-rescue.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: node border style for handdrawn shapes
|
5
.changeset/smart-humans-cover.md
Normal file
5
.changeset/smart-humans-cover.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Update flowchart direction TD's behavior to be the same as TB
|
5
.changeset/strong-dryers-pay.md
Normal file
5
.changeset/strong-dryers-pay.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: correctly render non-directional lines for '---' in block diagrams
|
5
.changeset/vast-buses-see.md
Normal file
5
.changeset/vast-buses-see.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
chore: Update packet diagram to use new class-based database structure
|
@@ -2,7 +2,6 @@
|
||||
Ashish Jain
|
||||
cpettitt
|
||||
Dong Cai
|
||||
fourcube
|
||||
knsv
|
||||
Knut Sveidqvist
|
||||
Nikolay Rozhkov
|
||||
|
1
.github/lychee.toml
vendored
1
.github/lychee.toml
vendored
@@ -59,7 +59,6 @@ exclude = [
|
||||
"https://huehive.co",
|
||||
"https://foswiki.org",
|
||||
"https://www.gnu.org",
|
||||
"https://redmine.org",
|
||||
"https://mermaid-preview.com"
|
||||
]
|
||||
|
||||
|
2
.github/workflows/validate-lockfile.yml
vendored
2
.github/workflows/validate-lockfile.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
# 2) No unwanted vitepress paths
|
||||
if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then
|
||||
issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run `rm -rf packages/mermaid/src/vitepress && pnpm install` to regenerate.")
|
||||
issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run \`rm -rf packages/mermaid/src/vitepress && pnpm install\` to regenerate.")
|
||||
fi
|
||||
|
||||
# 3) Lockfile only changes when package.json changes
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ node_modules/
|
||||
coverage/
|
||||
.idea/
|
||||
.pnpm-store/
|
||||
.instructions/
|
||||
|
||||
dist
|
||||
v8-compile-cache-0
|
||||
|
@@ -14,7 +14,7 @@ interface CodeObject {
|
||||
mermaid: CypressMermaidConfig;
|
||||
}
|
||||
|
||||
export const utf8ToB64 = (str: string): string => {
|
||||
const utf8ToB64 = (str: string): string => {
|
||||
return Buffer.from(decodeURIComponent(encodeURIComponent(str))).toString('base64');
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ const batchId: string =
|
||||
'mermaid-batch-' +
|
||||
(Cypress.env('useAppli')
|
||||
? Date.now().toString()
|
||||
: (Cypress.env('CYPRESS_COMMIT') ?? Date.now().toString()));
|
||||
: Cypress.env('CYPRESS_COMMIT') || Date.now().toString());
|
||||
|
||||
export const mermaidUrl = (
|
||||
graphStr: string | string[],
|
||||
@@ -61,7 +61,9 @@ export const imgSnapshotTest = (
|
||||
sequence: {
|
||||
...(_options.sequence ?? {}),
|
||||
actorFontFamily: 'courier',
|
||||
noteFontFamily: _options.sequence?.noteFontFamily ?? 'courier',
|
||||
noteFontFamily: _options.sequence?.noteFontFamily
|
||||
? _options.sequence.noteFontFamily
|
||||
: 'courier',
|
||||
messageFontFamily: 'courier',
|
||||
},
|
||||
};
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { imgSnapshotTest, mermaidUrl, utf8ToB64 } from '../../helpers/util.ts';
|
||||
import { mermaidUrl } from '../../helpers/util.ts';
|
||||
describe('XSS', () => {
|
||||
it('should handle xss in tags', () => {
|
||||
const str =
|
||||
@@ -141,37 +141,4 @@ describe('XSS', () => {
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sanitize icon labels in architecture diagrams', () => {
|
||||
const str = JSON.stringify({
|
||||
code: `architecture-beta
|
||||
group api(cloud)[API]
|
||||
service db "<img src=x onerror=\\"xssAttack()\\">" [Database] in api`,
|
||||
});
|
||||
imgSnapshotTest(utf8ToB64(str), {}, true);
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sanitize katex blocks', () => {
|
||||
const str = JSON.stringify({
|
||||
code: `sequenceDiagram
|
||||
participant A as Alice<img src="x" onerror="xssAttack()">$$\\text{Alice}$$
|
||||
A->>John: Hello John, how are you?`,
|
||||
});
|
||||
imgSnapshotTest(utf8ToB64(str), {}, true);
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sanitize labels', () => {
|
||||
const str = JSON.stringify({
|
||||
code: `erDiagram
|
||||
"<img src=x onerror=xssAttack()>" ||--|| ENTITY2 : "<img src=x onerror=xssAttack()>"
|
||||
`,
|
||||
});
|
||||
imgSnapshotTest(utf8ToB64(str), {}, true);
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
@@ -1053,21 +1053,6 @@ flowchart LR
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('6647-elk: should keep node order when using elk layout unless it would add crossings', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
a --> a1 & a2 & a3 & a4
|
||||
b --> b1 & b2
|
||||
b2 --> b3
|
||||
b1 --> b4
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Title and arrow styling #4813', () => {
|
||||
|
@@ -1118,7 +1118,7 @@ end
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
A(["Start"]) --> n1["Untitled Node"]
|
||||
A --> n2["Untitled Node"]
|
||||
A --> n2["Untitled Node"]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -1127,7 +1127,7 @@ end
|
||||
imgSnapshotTest(
|
||||
`flowchart BT
|
||||
n2["Untitled Node"] --> n1["Diamond"]
|
||||
n1@{ shape: diam}
|
||||
n1@{ shape: diam}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -1138,7 +1138,7 @@ end
|
||||
n2["Untitled Node"] --> n1["Rounded Rectangle"]
|
||||
n3["Untitled Node"] --> n1
|
||||
n1@{ shape: rounded}
|
||||
n3@{ shape: rect}
|
||||
n3@{ shape: rect}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -1164,26 +1164,4 @@ end
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
describe('when rendering unsuported markdown', () => {
|
||||
const graph = `flowchart TB
|
||||
mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"]
|
||||
mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"]
|
||||
subgraph subgraph1["\`How to fix **fix**\`"]
|
||||
broken --> B["B"]
|
||||
end
|
||||
githost["Github, Gitlab, BitBucket, etc."]
|
||||
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
|
||||
a["1."]
|
||||
b["- x"]
|
||||
`;
|
||||
|
||||
it('should render raw strings', () => {
|
||||
imgSnapshotTest(graph);
|
||||
});
|
||||
|
||||
it('should render raw strings with htmlLabels: false', () => {
|
||||
imgSnapshotTest(graph, { htmlLabels: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
79
cypress/integration/rendering/mindmap-tidy-tree.spec.js
Normal file
79
cypress/integration/rendering/mindmap-tidy-tree.spec.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Mindmap Tidy Tree', () => {
|
||||
it('1-tidy-tree: should render a simple mindmap without children', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
`
|
||||
);
|
||||
});
|
||||
it('2-tidy-tree: should render a simple mindmap', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
`
|
||||
);
|
||||
});
|
||||
it('3-tidy-tree: should render a mindmap with different shapes', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
id)I am a cloud(
|
||||
id))I am a bang((
|
||||
Tools
|
||||
`
|
||||
);
|
||||
});
|
||||
it('4-tidy-tree: should render a mindmap with children', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
((This is a mindmap))
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
@@ -159,12 +159,10 @@ root
|
||||
});
|
||||
it('square shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
root[
|
||||
The root
|
||||
]
|
||||
`,
|
||||
]`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -172,12 +170,10 @@ mindmap
|
||||
});
|
||||
it('rounded rect shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
root((
|
||||
The root
|
||||
))
|
||||
`,
|
||||
))`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -185,12 +181,10 @@ mindmap
|
||||
});
|
||||
it('circle shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
root(
|
||||
The root
|
||||
)
|
||||
`,
|
||||
)`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -198,10 +192,8 @@ mindmap
|
||||
});
|
||||
it('default shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
`,
|
||||
`mindmap
|
||||
The root`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -209,12 +201,10 @@ mindmap
|
||||
});
|
||||
it('adding children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
`,
|
||||
child2`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -222,13 +212,11 @@ mindmap
|
||||
});
|
||||
it('adding grand children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
child3
|
||||
`,
|
||||
child3`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
|
@@ -130,23 +130,77 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
elk:
|
||||
mergeEdges: false
|
||||
forceNodeModelOrder: false
|
||||
considerModelOrder: NONE
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
|
||||
---
|
||||
flowchart TB
|
||||
a --> a1 & a2 & a3 & a4
|
||||
b --> b1 & b2
|
||||
b2 --> b3
|
||||
b1 --> b4</pre
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
a
|
||||
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
b
|
||||
c
|
||||
d
|
||||
B
|
||||
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
D
|
||||
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
|
||||
</pre>
|
||||
|
||||
<pre id="diagram4" class="mermaid">
|
||||
treemap
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
@@ -191,8 +245,145 @@ treemap
|
||||
"Item B2": 25
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
a
|
||||
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
b
|
||||
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
flowchart TB
|
||||
A --> n0["1"]
|
||||
A --> n1["2"]
|
||||
A --> n2["3"]
|
||||
A --> n3["4"] --> Q & R & S & T
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
A --> n0["1"]
|
||||
A --> n1["2"]
|
||||
A --> n2["3"]
|
||||
A --> n3["4"] --> Q & R & S & T
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
flowchart LR
|
||||
AB["apa@apa@"] --> B(("`apa@apa`"))
|
||||
root{mindmap} --- Origins --- Europe
|
||||
Origins --> Asia
|
||||
root --- Background --- Rich
|
||||
Background --- Poor
|
||||
subgraph apa
|
||||
Background
|
||||
Poor
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
root{mindmap} --- Origins --- Europe
|
||||
Origins --> Asia
|
||||
root --- Background --- Rich
|
||||
Background --- Poor
|
||||
|
||||
|
||||
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart
|
||||
@@ -274,6 +465,44 @@ config:
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
a
|
||||
subgraph s0["APA"]
|
||||
subgraph s8["APA"]
|
||||
subgraph s1["APA"]
|
||||
D{"X"}
|
||||
E[Q]
|
||||
end
|
||||
subgraph s3["BAPA"]
|
||||
F[Q]
|
||||
I
|
||||
end
|
||||
D --> I
|
||||
D --> I
|
||||
D --> I
|
||||
|
||||
I{"X"}
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
a
|
||||
D{"Use the editor"}
|
||||
|
||||
D -- Mermaid js --> I{"fa:fa-code Text"}
|
||||
D-->I
|
||||
D-->I
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
|
376
cypress/platform/mindmap-layouts.html
Normal file
376
cypress/platform/mindmap-layouts.html
Normal file
@@ -0,0 +1,376 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>Mermaid Quick Test Page</title>
|
||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
|
||||
<style>
|
||||
div.mermaid {
|
||||
font-family: 'Courier New', Courier, monospace !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
</pre>
|
||||
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
id)I am a cloud(
|
||||
id))I am a bang((
|
||||
Tools
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
id)I am a cloud(
|
||||
id))I am a bang((
|
||||
Tools
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
id)I am a cloud(
|
||||
id))I am a bang((
|
||||
Tools
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
id)I am a cloud(
|
||||
id))I am a bang((
|
||||
Tools
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
a
|
||||
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
b
|
||||
c
|
||||
d
|
||||
B
|
||||
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
D
|
||||
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
a
|
||||
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
b
|
||||
c
|
||||
d
|
||||
B
|
||||
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
D
|
||||
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
a
|
||||
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
b
|
||||
c
|
||||
d
|
||||
B
|
||||
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
D
|
||||
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
a
|
||||
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
b
|
||||
c
|
||||
d
|
||||
B
|
||||
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
D
|
||||
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||
|
||||
</pre>
|
||||
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
((This is a mindmap))
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
|
||||
</pre>
|
||||
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: dagre
|
||||
---
|
||||
mindmap
|
||||
((This is a mindmap))
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
|
||||
</pre>
|
||||
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
mindmap
|
||||
((This is a mindmap))
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
|
||||
</pre>
|
||||
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: cose-bilkent
|
||||
---
|
||||
mindmap
|
||||
((This is a mindmap))
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
<script type="module">
|
||||
import mermaid from '/mermaid.esm.mjs';
|
||||
import tidytree from '/mermaid-layout-tidy-tree.esm.mjs';
|
||||
import layouts from './mermaid-layout-elk.esm.mjs';
|
||||
mermaid.registerLayoutLoaders(layouts);
|
||||
mermaid.registerLayoutLoaders(tidytree);
|
||||
mermaid.initialize({
|
||||
theme: 'default',
|
||||
logLevel: 3,
|
||||
securityLevel: 'loose',
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -41,6 +41,10 @@ graph TB
|
||||
const { svg } = await mermaid.render('d22', value);
|
||||
console.log(svg);
|
||||
el.innerHTML = svg;
|
||||
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
|
||||
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
|
||||
// mermaid.test1('third_fast', 200).then((r) => console.info(r));
|
||||
// mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import externalExample from './mermaid-example-diagram.esm.mjs';
|
||||
import layouts from './mermaid-layout-elk.esm.mjs';
|
||||
import tidyTree from './mermaid-layout-tidy-tree.esm.mjs';
|
||||
import zenUml from './mermaid-zenuml.esm.mjs';
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
|
||||
@@ -65,6 +66,7 @@ const contentLoaded = async function () {
|
||||
await mermaid.registerExternalDiagrams([externalExample, zenUml]);
|
||||
|
||||
mermaid.registerLayoutLoaders(layouts);
|
||||
mermaid.registerLayoutLoaders(tidyTree);
|
||||
mermaid.initialize(graphObj.mermaid);
|
||||
/**
|
||||
* CC-BY-4.0
|
||||
@@ -182,7 +184,7 @@ const contentLoadedApi = async function () {
|
||||
for (let i = 0; i < numCodes; i++) {
|
||||
const { svg, bindFunctions } = await mermaid.render('newid' + i, graphObj.code[i], divs[i]);
|
||||
div.innerHTML = svg;
|
||||
bindFunctions?.(div);
|
||||
bindFunctions(div);
|
||||
}
|
||||
} else {
|
||||
const div = document.createElement('div');
|
||||
@@ -194,7 +196,7 @@ const contentLoadedApi = async function () {
|
||||
const { svg, bindFunctions } = await mermaid.render('newid', graphObj.code, div);
|
||||
div.innerHTML = svg;
|
||||
console.log(div.innerHTML);
|
||||
bindFunctions?.(div);
|
||||
bindFunctions(div);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -2,219 +2,219 @@
|
||||
"durations": [
|
||||
{
|
||||
"spec": "cypress/integration/other/configuration.spec.js",
|
||||
"duration": 6297
|
||||
"duration": 5672
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/external-diagrams.spec.js",
|
||||
"duration": 2187
|
||||
"duration": 1990
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/ghsa.spec.js",
|
||||
"duration": 3509
|
||||
"duration": 3186
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/iife.spec.js",
|
||||
"duration": 2218
|
||||
"duration": 1948
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/interaction.spec.js",
|
||||
"duration": 12104
|
||||
"duration": 11938
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/rerender.spec.js",
|
||||
"duration": 2151
|
||||
"duration": 1932
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/xss.spec.js",
|
||||
"duration": 33064
|
||||
"duration": 27237
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/appli.spec.js",
|
||||
"duration": 3488
|
||||
"duration": 3170
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/architecture.spec.ts",
|
||||
"duration": 106
|
||||
"duration": 104
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/block.spec.js",
|
||||
"duration": 18317
|
||||
"duration": 17390
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/c4.spec.js",
|
||||
"duration": 5592
|
||||
"duration": 5296
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
|
||||
"duration": 39358
|
||||
"duration": 39004
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
|
||||
"duration": 37160
|
||||
"duration": 37653
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
|
||||
"duration": 23660
|
||||
"duration": 23278
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
|
||||
"duration": 36866
|
||||
"duration": 36645
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram.spec.js",
|
||||
"duration": 17334
|
||||
"duration": 15418
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
|
||||
"duration": 9871
|
||||
"duration": 9684
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/current.spec.js",
|
||||
"duration": 2833
|
||||
"duration": 2570
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
|
||||
"duration": 85321
|
||||
"duration": 84687
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/erDiagram.spec.js",
|
||||
"duration": 15673
|
||||
"duration": 14819
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
|
||||
"duration": 3724
|
||||
"duration": 3371
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
|
||||
"duration": 41178
|
||||
"duration": 39925
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
|
||||
"duration": 29966
|
||||
"duration": 34694
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
|
||||
"duration": 7689
|
||||
"duration": 7137
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
|
||||
"duration": 24709
|
||||
"duration": 24740
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
|
||||
"duration": 45565
|
||||
"duration": 42077
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart.spec.js",
|
||||
"duration": 31144
|
||||
"duration": 30642
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/gantt.spec.js",
|
||||
"duration": 20808
|
||||
"duration": 18085
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/gitGraph.spec.js",
|
||||
"duration": 49985
|
||||
"duration": 50107
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/iconShape.spec.ts",
|
||||
"duration": 273272
|
||||
"duration": 276279
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/imageShape.spec.ts",
|
||||
"duration": 55880
|
||||
"duration": 56505
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/info.spec.ts",
|
||||
"duration": 3271
|
||||
"duration": 3036
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/journey.spec.js",
|
||||
"duration": 7293
|
||||
"duration": 6889
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/kanban.spec.ts",
|
||||
"duration": 7861
|
||||
"duration": 7353
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/katex.spec.js",
|
||||
"duration": 3922
|
||||
"duration": 3580
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
|
||||
"duration": 2726
|
||||
"duration": 2508
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/mindmap.spec.ts",
|
||||
"duration": 11670
|
||||
"duration": 10939
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/newShapes.spec.ts",
|
||||
"duration": 146020
|
||||
"duration": 149102
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
|
||||
"duration": 114244
|
||||
"duration": 113987
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/packet.spec.ts",
|
||||
"duration": 5036
|
||||
"duration": 4060
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/pie.spec.ts",
|
||||
"duration": 6545
|
||||
"duration": 5715
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
|
||||
"duration": 9097
|
||||
"duration": 8945
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/radar.spec.js",
|
||||
"duration": 5676
|
||||
"duration": 5337
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/requirement.spec.js",
|
||||
"duration": 2795
|
||||
"duration": 2643
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
|
||||
"duration": 51660
|
||||
"duration": 52072
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/sankey.spec.ts",
|
||||
"duration": 6957
|
||||
"duration": 6692
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
|
||||
"duration": 36026
|
||||
"duration": 35721
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
|
||||
"duration": 29551
|
||||
"duration": 26030
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
|
||||
"duration": 17364
|
||||
"duration": 16333
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/theme.spec.js",
|
||||
"duration": 30209
|
||||
"duration": 29287
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/timeline.spec.ts",
|
||||
"duration": 8699
|
||||
"duration": 8491
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/treemap.spec.ts",
|
||||
"duration": 12168
|
||||
"duration": 12291
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/xyChart.spec.js",
|
||||
"duration": 21453
|
||||
"duration": 20651
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/zenuml.spec.js",
|
||||
"duration": 3577
|
||||
"duration": 3218
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -20,14 +20,14 @@
|
||||
width: 800
|
||||
nodeAlignment: left
|
||||
---
|
||||
sankey
|
||||
sankey
|
||||
a,b,8
|
||||
b,c,8
|
||||
c,d,8
|
||||
d,e,8
|
||||
|
||||
|
||||
x,c,4
|
||||
c,y,4
|
||||
c,y,4
|
||||
</pre>
|
||||
|
||||
<h2>Energy flow</h2>
|
||||
|
@@ -29,7 +29,8 @@ In GitHub, you first [**fork a mermaid repository**](https://github.com/mermaid-
|
||||
|
||||
Then you **clone** a copy to your local development machine (e.g. where you code) to make a copy with all the files to work with.
|
||||
|
||||
> **💡 Tip** > [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
||||
> **💡 Tip**
|
||||
> [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
||||
|
||||
```bash
|
||||
git clone git@github.com/your-fork/mermaid
|
||||
|
@@ -33,7 +33,8 @@ mindmap
|
||||
|
||||
## Join the Development
|
||||
|
||||
> **💡 Tip** > **Check out our** [**detailed contribution guide**](./contributing.md).
|
||||
> **💡 Tip**
|
||||
> **Check out our** [**detailed contribution guide**](./contributing.md).
|
||||
|
||||
Where to start:
|
||||
|
||||
@@ -47,7 +48,8 @@ Where to start:
|
||||
|
||||
## A Question Or a Suggestion?
|
||||
|
||||
> **💡 Tip** > **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
|
||||
> **💡 Tip**
|
||||
> **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
|
||||
|
||||
If you have faced a vulnerability [report it to us](./security.md).
|
||||
|
||||
|
@@ -22,7 +22,6 @@ While directives allow you to change most of the default configuration settings,
|
||||
Mermaid basically supports two types of configuration options to be overridden by directives.
|
||||
|
||||
1. _General/Top Level configurations_ : These are the configurations that are available and applied to all the diagram. **Some of the most important top-level** configurations are:
|
||||
|
||||
- theme
|
||||
- fontFamily
|
||||
- logLevel
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
> `const` **configKeys**: `Set`<`string`>
|
||||
|
||||
Defined in: [packages/mermaid/src/defaultConfig.ts:292](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L292)
|
||||
Defined in: [packages/mermaid/src/defaultConfig.ts:290](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L290)
|
||||
|
@@ -10,10 +10,6 @@
|
||||
|
||||
# mermaid
|
||||
|
||||
## Classes
|
||||
|
||||
- [UnknownDiagramError](classes/UnknownDiagramError.md)
|
||||
|
||||
## Interfaces
|
||||
|
||||
- [DetailedError](interfaces/DetailedError.md)
|
||||
@@ -27,6 +23,7 @@
|
||||
- [RenderOptions](interfaces/RenderOptions.md)
|
||||
- [RenderResult](interfaces/RenderResult.md)
|
||||
- [RunOptions](interfaces/RunOptions.md)
|
||||
- [UnknownDiagramError](interfaces/UnknownDiagramError.md)
|
||||
|
||||
## Type Aliases
|
||||
|
||||
|
@@ -1,159 +0,0 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/mermaid/classes/UnknownDiagramError.md](../../../../../packages/mermaid/src/docs/config/setup/mermaid/classes/UnknownDiagramError.md).
|
||||
|
||||
[**mermaid**](../../README.md)
|
||||
|
||||
---
|
||||
|
||||
# Class: UnknownDiagramError
|
||||
|
||||
Defined in: [packages/mermaid/src/errors.ts:1](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/errors.ts#L1)
|
||||
|
||||
## Extends
|
||||
|
||||
- `Error`
|
||||
|
||||
## Constructors
|
||||
|
||||
### new UnknownDiagramError()
|
||||
|
||||
> **new UnknownDiagramError**(`message`): [`UnknownDiagramError`](UnknownDiagramError.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/errors.ts:2](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/errors.ts#L2)
|
||||
|
||||
#### Parameters
|
||||
|
||||
##### message
|
||||
|
||||
`string`
|
||||
|
||||
#### Returns
|
||||
|
||||
[`UnknownDiagramError`](UnknownDiagramError.md)
|
||||
|
||||
#### Overrides
|
||||
|
||||
`Error.constructor`
|
||||
|
||||
## Properties
|
||||
|
||||
### cause?
|
||||
|
||||
> `optional` **cause**: `unknown`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es2022.error.d.ts:26
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.cause`
|
||||
|
||||
---
|
||||
|
||||
### message
|
||||
|
||||
> **message**: `string`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1077
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.message`
|
||||
|
||||
---
|
||||
|
||||
### name
|
||||
|
||||
> **name**: `string`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1076
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.name`
|
||||
|
||||
---
|
||||
|
||||
### stack?
|
||||
|
||||
> `optional` **stack**: `string`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1078
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.stack`
|
||||
|
||||
---
|
||||
|
||||
### prepareStackTrace()?
|
||||
|
||||
> `static` `optional` **prepareStackTrace**: (`err`, `stackTraces`) => `any`
|
||||
|
||||
Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:143
|
||||
|
||||
Optional override for formatting stack traces
|
||||
|
||||
#### Parameters
|
||||
|
||||
##### err
|
||||
|
||||
`Error`
|
||||
|
||||
##### stackTraces
|
||||
|
||||
`CallSite`\[]
|
||||
|
||||
#### Returns
|
||||
|
||||
`any`
|
||||
|
||||
#### See
|
||||
|
||||
<https://v8.dev/docs/stack-trace-api#customizing-stack-traces>
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.prepareStackTrace`
|
||||
|
||||
---
|
||||
|
||||
### stackTraceLimit
|
||||
|
||||
> `static` **stackTraceLimit**: `number`
|
||||
|
||||
Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:145
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.stackTraceLimit`
|
||||
|
||||
## Methods
|
||||
|
||||
### captureStackTrace()
|
||||
|
||||
> `static` **captureStackTrace**(`targetObject`, `constructorOpt`?): `void`
|
||||
|
||||
Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:136
|
||||
|
||||
Create .stack property on a target object
|
||||
|
||||
#### Parameters
|
||||
|
||||
##### targetObject
|
||||
|
||||
`object`
|
||||
|
||||
##### constructorOpt?
|
||||
|
||||
`Function`
|
||||
|
||||
#### Returns
|
||||
|
||||
`void`
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.captureStackTrace`
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: LayoutData
|
||||
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L145)
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:178](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L178)
|
||||
|
||||
## Indexable
|
||||
|
||||
@@ -22,7 +22,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:145](https://github.co
|
||||
|
||||
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L148)
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L181)
|
||||
|
||||
---
|
||||
|
||||
@@ -30,7 +30,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:148](https://github.co
|
||||
|
||||
> **edges**: `Edge`\[]
|
||||
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:147](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L147)
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L180)
|
||||
|
||||
---
|
||||
|
||||
@@ -38,4 +38,4 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:147](https://github.co
|
||||
|
||||
> **nodes**: `Node`\[]
|
||||
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L146)
|
||||
Defined in: [packages/mermaid/src/rendering-util/types.ts:179](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L179)
|
||||
|
@@ -32,7 +32,7 @@ page.
|
||||
|
||||
### detectType()
|
||||
|
||||
> **detectType**: (`text`, `config`?) => `string`
|
||||
> **detectType**: (`text`, `config?`) => `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:449](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L449)
|
||||
|
||||
@@ -105,7 +105,7 @@ An array of objects with the id of the diagram.
|
||||
|
||||
### ~~init()~~
|
||||
|
||||
> **init**: (`config`?, `nodes`?, `callback`?) => `Promise`<`void`>
|
||||
> **init**: (`config?`, `nodes?`, `callback?`) => `Promise`<`void`>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442)
|
||||
|
||||
@@ -117,7 +117,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/
|
||||
|
||||
[`MermaidConfig`](MermaidConfig.md)
|
||||
|
||||
**Deprecated**, please set configuration in [initialize](Mermaid.md#initialize).
|
||||
**Deprecated**, please set configuration in [initialize](#initialize).
|
||||
|
||||
##### nodes?
|
||||
|
||||
@@ -141,13 +141,13 @@ Called once for each rendered diagram's id.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#run) instead.
|
||||
Use [initialize](#initialize) and [run](#run) instead.
|
||||
|
||||
Renders the mermaid diagrams
|
||||
|
||||
#### Deprecated
|
||||
|
||||
Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#run) instead.
|
||||
Use [initialize](#initialize) and [run](#run) instead.
|
||||
|
||||
---
|
||||
|
||||
@@ -176,7 +176,7 @@ Configuration object for mermaid.
|
||||
|
||||
### ~~mermaidAPI~~
|
||||
|
||||
> **mermaidAPI**: `Readonly`<{ `defaultConfig`: [`MermaidConfig`](MermaidConfig.md); `getConfig`: () => [`MermaidConfig`](MermaidConfig.md); `getDiagramFromText`: (`text`, `metadata`) => `Promise`<`Diagram`>; `getSiteConfig`: () => [`MermaidConfig`](MermaidConfig.md); `globalReset`: () => `void`; `initialize`: (`userOptions`) => `void`; `parse`: (`text`, `parseOptions`) => `Promise`<`false` | [`ParseResult`](ParseResult.md)>(`text`, `parseOptions`?) => `Promise`<[`ParseResult`](ParseResult.md)>; `render`: (`id`, `text`, `svgContainingElement`?) => `Promise`<[`RenderResult`](RenderResult.md)>; `reset`: () => `void`; `setConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); `updateSiteConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); }>
|
||||
> **mermaidAPI**: `Readonly`<{ `defaultConfig`: [`MermaidConfig`](MermaidConfig.md); `getConfig`: () => [`MermaidConfig`](MermaidConfig.md); `getDiagramFromText`: (`text`, `metadata`) => `Promise`<`Diagram`>; `getSiteConfig`: () => [`MermaidConfig`](MermaidConfig.md); `globalReset`: () => `void`; `initialize`: (`userOptions`) => `void`; `parse`: {(`text`, `parseOptions`): `Promise`<`false` | [`ParseResult`](ParseResult.md)>; (`text`, `parseOptions?`): `Promise`<[`ParseResult`](ParseResult.md)>; }; `render`: (`id`, `text`, `svgContainingElement?`) => `Promise`<[`RenderResult`](RenderResult.md)>; `reset`: () => `void`; `setConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); `updateSiteConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); }>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
|
||||
|
||||
@@ -184,73 +184,81 @@ Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/
|
||||
|
||||
#### Deprecated
|
||||
|
||||
Use [parse](Mermaid.md#parse) and [render](Mermaid.md#render) instead. Please [open a discussion](https://github.com/mermaid-js/mermaid/discussions) if your use case does not fit the new API.
|
||||
Use [parse](#parse) and [render](#render) instead. Please [open a discussion](https://github.com/mermaid-js/mermaid/discussions) if your use case does not fit the new API.
|
||||
|
||||
---
|
||||
|
||||
### parse()
|
||||
|
||||
> **parse**: (`text`, `parseOptions`) => `Promise`<`false` | [`ParseResult`](ParseResult.md)>(`text`, `parseOptions`?) => `Promise`<[`ParseResult`](ParseResult.md)>
|
||||
> **parse**: {(`text`, `parseOptions`): `Promise`<`false` | [`ParseResult`](ParseResult.md)>; (`text`, `parseOptions?`): `Promise`<[`ParseResult`](ParseResult.md)>; }
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
|
||||
|
||||
#### Call Signature
|
||||
|
||||
> (`text`, `parseOptions`): `Promise`<`false` | [`ParseResult`](ParseResult.md)>
|
||||
|
||||
Parse the text and validate the syntax.
|
||||
|
||||
#### Parameters
|
||||
##### Parameters
|
||||
|
||||
##### text
|
||||
###### text
|
||||
|
||||
`string`
|
||||
|
||||
The mermaid diagram definition.
|
||||
|
||||
##### parseOptions
|
||||
###### parseOptions
|
||||
|
||||
[`ParseOptions`](ParseOptions.md) & `object`
|
||||
|
||||
Options for parsing.
|
||||
|
||||
#### Returns
|
||||
##### Returns
|
||||
|
||||
`Promise`<`false` | [`ParseResult`](ParseResult.md)>
|
||||
|
||||
An object with the `diagramType` set to type of the diagram if valid. Otherwise `false` if parseOptions.suppressErrors is `true`.
|
||||
|
||||
#### See
|
||||
##### See
|
||||
|
||||
[ParseOptions](ParseOptions.md)
|
||||
|
||||
#### Throws
|
||||
##### Throws
|
||||
|
||||
Error if the diagram is invalid and parseOptions.suppressErrors is false or not set.
|
||||
|
||||
#### Call Signature
|
||||
|
||||
> (`text`, `parseOptions?`): `Promise`<[`ParseResult`](ParseResult.md)>
|
||||
|
||||
Parse the text and validate the syntax.
|
||||
|
||||
#### Parameters
|
||||
##### Parameters
|
||||
|
||||
##### text
|
||||
###### text
|
||||
|
||||
`string`
|
||||
|
||||
The mermaid diagram definition.
|
||||
|
||||
##### parseOptions?
|
||||
###### parseOptions?
|
||||
|
||||
[`ParseOptions`](ParseOptions.md)
|
||||
|
||||
Options for parsing.
|
||||
|
||||
#### Returns
|
||||
##### Returns
|
||||
|
||||
`Promise`<[`ParseResult`](ParseResult.md)>
|
||||
|
||||
An object with the `diagramType` set to type of the diagram if valid. Otherwise `false` if parseOptions.suppressErrors is `true`.
|
||||
|
||||
#### See
|
||||
##### See
|
||||
|
||||
[ParseOptions](ParseOptions.md)
|
||||
|
||||
#### Throws
|
||||
##### Throws
|
||||
|
||||
Error if the diagram is invalid and parseOptions.suppressErrors is false or not set.
|
||||
|
||||
@@ -332,7 +340,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:444](https://github.com/mermaid-js/
|
||||
|
||||
### render()
|
||||
|
||||
> **render**: (`id`, `text`, `svgContainingElement`?) => `Promise`<[`RenderResult`](RenderResult.md)>
|
||||
> **render**: (`id`, `text`, `svgContainingElement?`) => `Promise`<[`RenderResult`](RenderResult.md)>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438)
|
||||
|
||||
|
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/config.type.ts:58](https://github.com/mermaid-
|
||||
|
||||
> `optional` **altFontFamily**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L132)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L122)
|
||||
|
||||
---
|
||||
|
||||
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/config.type.ts:132](https://github.com/mermaid
|
||||
|
||||
> `optional` **architecture**: `ArchitectureDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
|
||||
|
||||
---
|
||||
|
||||
@@ -34,7 +34,7 @@ Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid
|
||||
|
||||
> `optional` **arrowMarkerAbsolute**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L151)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
|
||||
|
||||
Controls whether or arrow markers in html code are absolute paths or anchors.
|
||||
This matters if you are using base tag settings.
|
||||
@@ -45,7 +45,7 @@ This matters if you are using base tag settings.
|
||||
|
||||
> `optional` **block**: `BlockDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
|
||||
|
||||
---
|
||||
|
||||
@@ -53,7 +53,7 @@ Defined in: [packages/mermaid/src/config.type.ts:211](https://github.com/mermaid
|
||||
|
||||
> `optional` **c4**: `C4DiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:208](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L208)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
|
||||
|
||||
---
|
||||
|
||||
@@ -61,7 +61,7 @@ Defined in: [packages/mermaid/src/config.type.ts:208](https://github.com/mermaid
|
||||
|
||||
> `optional` **class**: `ClassDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
|
||||
|
||||
---
|
||||
|
||||
@@ -69,7 +69,7 @@ Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid
|
||||
|
||||
> `optional` **darkMode**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L123)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L113)
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ Defined in: [packages/mermaid/src/config.type.ts:123](https://github.com/mermaid
|
||||
|
||||
> `optional` **deterministicIds**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
|
||||
|
||||
This option controls if the generated ids of nodes in the SVG are
|
||||
generated randomly or based on a seed.
|
||||
@@ -93,7 +93,7 @@ should not change unless content is changed.
|
||||
|
||||
> `optional` **deterministicIDSeed**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
|
||||
|
||||
This option is the optional seed for deterministic ids.
|
||||
If set to `undefined` but deterministicIds is `true`, a simple number iterator is used.
|
||||
@@ -105,7 +105,7 @@ You can set this attribute to base the seed on a static string.
|
||||
|
||||
> `optional` **dompurifyConfig**: `Config`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:213](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L213)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
|
||||
|
||||
---
|
||||
|
||||
@@ -115,24 +115,12 @@ Defined in: [packages/mermaid/src/config.type.ts:213](https://github.com/mermaid
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L91)
|
||||
|
||||
#### considerModelOrder?
|
||||
|
||||
> `optional` **considerModelOrder**: `"NONE"` | `"NODES_AND_EDGES"` | `"PREFER_EDGES"` | `"PREFER_NODES"`
|
||||
|
||||
Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
|
||||
|
||||
#### cycleBreakingStrategy?
|
||||
|
||||
> `optional` **cycleBreakingStrategy**: `"GREEDY"` | `"DEPTH_FIRST"` | `"INTERACTIVE"` | `"MODEL_ORDER"` | `"GREEDY_MODEL_ORDER"`
|
||||
|
||||
This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops.
|
||||
|
||||
#### forceNodeModelOrder?
|
||||
|
||||
> `optional` **forceNodeModelOrder**: `boolean`
|
||||
|
||||
The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
|
||||
|
||||
#### mergeEdges?
|
||||
|
||||
> `optional` **mergeEdges**: `boolean`
|
||||
@@ -151,7 +139,7 @@ Elk specific option affecting how nodes are placed.
|
||||
|
||||
> `optional` **er**: `ErDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
|
||||
|
||||
---
|
||||
|
||||
@@ -159,7 +147,7 @@ Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid
|
||||
|
||||
> `optional` **flowchart**: `FlowchartDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
|
||||
|
||||
---
|
||||
|
||||
@@ -167,7 +155,7 @@ Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid
|
||||
|
||||
> `optional` **fontFamily**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
|
||||
|
||||
Specifies the font to be used in the rendered diagrams.
|
||||
Can be any possible CSS `font-family`.
|
||||
@@ -179,7 +167,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
|
||||
|
||||
> `optional` **fontSize**: `number`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:215](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L215)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
|
||||
|
||||
---
|
||||
|
||||
@@ -187,7 +175,7 @@ Defined in: [packages/mermaid/src/config.type.ts:215](https://github.com/mermaid
|
||||
|
||||
> `optional` **forceLegacyMathML**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163)
|
||||
|
||||
This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
|
||||
fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
|
||||
@@ -199,7 +187,7 @@ If set to true, ignores legacyMathML.
|
||||
|
||||
> `optional` **gantt**: `GanttDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
|
||||
|
||||
---
|
||||
|
||||
@@ -207,7 +195,7 @@ Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid
|
||||
|
||||
> `optional` **gitGraph**: `GitGraphDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:207](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L207)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
|
||||
|
||||
---
|
||||
|
||||
@@ -225,7 +213,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
|
||||
|
||||
> `optional` **htmlLabels**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L124)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L114)
|
||||
|
||||
---
|
||||
|
||||
@@ -233,7 +221,7 @@ Defined in: [packages/mermaid/src/config.type.ts:124](https://github.com/mermaid
|
||||
|
||||
> `optional` **journey**: `JourneyDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
|
||||
|
||||
---
|
||||
|
||||
@@ -241,7 +229,7 @@ Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid
|
||||
|
||||
> `optional` **kanban**: `KanbanDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
|
||||
|
||||
---
|
||||
|
||||
@@ -259,7 +247,7 @@ Defines which layout algorithm to use for rendering the diagram.
|
||||
|
||||
> `optional` **legacyMathML**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:166](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L166)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L156)
|
||||
|
||||
This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
|
||||
without their own MathML implementation. If this option is disabled and MathML is not supported, the math
|
||||
@@ -272,7 +260,7 @@ fall back to legacy rendering for KaTeX.
|
||||
|
||||
> `optional` **logLevel**: `0` | `2` | `1` | `"trace"` | `"debug"` | `"info"` | `"warn"` | `"error"` | `"fatal"` | `3` | `4` | `5`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L137)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L127)
|
||||
|
||||
This option decides the amount of logging to be used by mermaid.
|
||||
|
||||
@@ -292,7 +280,7 @@ Defines which main look to use for the diagram.
|
||||
|
||||
> `optional` **markdownAutoWrap**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:216](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L216)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
|
||||
|
||||
---
|
||||
|
||||
@@ -320,7 +308,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
> `optional` **mindmap**: `MindmapDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
|
||||
|
||||
---
|
||||
|
||||
@@ -328,7 +316,7 @@ Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid
|
||||
|
||||
> `optional` **packet**: `PacketDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
|
||||
|
||||
---
|
||||
|
||||
@@ -336,7 +324,7 @@ Defined in: [packages/mermaid/src/config.type.ts:210](https://github.com/mermaid
|
||||
|
||||
> `optional` **pie**: `PieDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
|
||||
|
||||
---
|
||||
|
||||
@@ -344,7 +332,7 @@ Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid
|
||||
|
||||
> `optional` **quadrantChart**: `QuadrantChartConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
|
||||
|
||||
---
|
||||
|
||||
@@ -352,7 +340,7 @@ Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid
|
||||
|
||||
> `optional` **radar**: `RadarDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
|
||||
|
||||
---
|
||||
|
||||
@@ -360,7 +348,7 @@ Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid
|
||||
|
||||
> `optional` **requirement**: `RequirementDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
|
||||
|
||||
---
|
||||
|
||||
@@ -368,7 +356,7 @@ Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid
|
||||
|
||||
> `optional` **sankey**: `SankeyDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
|
||||
|
||||
---
|
||||
|
||||
@@ -376,7 +364,7 @@ Defined in: [packages/mermaid/src/config.type.ts:209](https://github.com/mermaid
|
||||
|
||||
> `optional` **secure**: `string`\[]
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:158](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L158)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L148)
|
||||
|
||||
This option controls which `currentConfig` keys are considered secure and
|
||||
can only be changed via call to `mermaid.initialize`.
|
||||
@@ -388,7 +376,7 @@ This prevents malicious graph directives from overriding a site's default securi
|
||||
|
||||
> `optional` **securityLevel**: `"strict"` | `"loose"` | `"antiscript"` | `"sandbox"`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
|
||||
|
||||
Level of trust for parsed diagram
|
||||
|
||||
@@ -398,7 +386,7 @@ Level of trust for parsed diagram
|
||||
|
||||
> `optional` **sequence**: `SequenceDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
|
||||
|
||||
---
|
||||
|
||||
@@ -406,7 +394,7 @@ Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid
|
||||
|
||||
> `optional` **startOnLoad**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L145)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L135)
|
||||
|
||||
Dictates whether mermaid starts on Page load
|
||||
|
||||
@@ -416,7 +404,7 @@ Dictates whether mermaid starts on Page load
|
||||
|
||||
> `optional` **state**: `StateDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
|
||||
|
||||
---
|
||||
|
||||
@@ -424,7 +412,7 @@ Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid
|
||||
|
||||
> `optional` **suppressErrorRendering**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:222](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L222)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
|
||||
|
||||
Suppresses inserting 'Syntax error' diagram in the DOM.
|
||||
This is useful when you want to control how to handle syntax errors in your application.
|
||||
@@ -462,7 +450,7 @@ Defined in: [packages/mermaid/src/config.type.ts:65](https://github.com/mermaid-
|
||||
|
||||
> `optional` **timeline**: `TimelineDiagramConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
|
||||
|
||||
---
|
||||
|
||||
@@ -470,7 +458,7 @@ Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid
|
||||
|
||||
> `optional` **wrap**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:214](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L214)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
|
||||
|
||||
---
|
||||
|
||||
@@ -478,4 +466,4 @@ Defined in: [packages/mermaid/src/config.type.ts:214](https://github.com/mermaid
|
||||
|
||||
> `optional` **xyChart**: `XYChartConfig`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
|
||||
Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: ParseOptions
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L72)
|
||||
Defined in: [packages/mermaid/src/types.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L76)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mer
|
||||
|
||||
> `optional` **suppressErrors**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L77)
|
||||
Defined in: [packages/mermaid/src/types.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L81)
|
||||
|
||||
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
|
||||
The `parseError` function will not be called.
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: ParseResult
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
|
||||
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mer
|
||||
|
||||
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
|
||||
Defined in: [packages/mermaid/src/types.ts:92](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L92)
|
||||
|
||||
The config passed as YAML frontmatter or directives
|
||||
|
||||
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
|
||||
|
||||
> **diagramType**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
|
||||
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
|
||||
|
||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: RenderResult
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
|
||||
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mer
|
||||
|
||||
> `optional` **bindFunctions**: (`element`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116)
|
||||
Defined in: [packages/mermaid/src/types.ts:120](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L120)
|
||||
|
||||
Bind function to be called after the svg has been inserted into the DOM.
|
||||
This is necessary for adding event listeners to the elements in the svg.
|
||||
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
||||
|
||||
> **diagramType**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
|
||||
Defined in: [packages/mermaid/src/types.ts:110](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L110)
|
||||
|
||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
> **svg**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
|
||||
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
|
||||
|
||||
The svg code for the rendered graph.
|
||||
|
65
docs/config/setup/mermaid/interfaces/UnknownDiagramError.md
Normal file
65
docs/config/setup/mermaid/interfaces/UnknownDiagramError.md
Normal file
@@ -0,0 +1,65 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md](../../../../../packages/mermaid/src/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md).
|
||||
|
||||
[**mermaid**](../../README.md)
|
||||
|
||||
---
|
||||
|
||||
# Interface: UnknownDiagramError
|
||||
|
||||
Defined in: [packages/mermaid/src/errors.ts:1](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/errors.ts#L1)
|
||||
|
||||
## Extends
|
||||
|
||||
- `Error`
|
||||
|
||||
## Properties
|
||||
|
||||
### cause?
|
||||
|
||||
> `optional` **cause**: `unknown`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es2022.error.d.ts:26
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.cause`
|
||||
|
||||
---
|
||||
|
||||
### message
|
||||
|
||||
> **message**: `string`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1077
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.message`
|
||||
|
||||
---
|
||||
|
||||
### name
|
||||
|
||||
> **name**: `string`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1076
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.name`
|
||||
|
||||
---
|
||||
|
||||
### stack?
|
||||
|
||||
> `optional` **stack**: `string`
|
||||
|
||||
Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1078
|
||||
|
||||
#### Inherited from
|
||||
|
||||
`Error.stack`
|
@@ -10,6 +10,6 @@
|
||||
|
||||
# Type Alias: InternalHelpers
|
||||
|
||||
> **InternalHelpers**: _typeof_ `internalHelpers`
|
||||
> **InternalHelpers** = _typeof_ `internalHelpers`
|
||||
|
||||
Defined in: [packages/mermaid/src/internals.ts:33](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/internals.ts#L33)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Type Alias: ParseErrorFunction()
|
||||
|
||||
> **ParseErrorFunction**: (`err`, `hash`?) => `void`
|
||||
> **ParseErrorFunction** = (`err`, `hash?`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/Diagram.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/Diagram.ts#L10)
|
||||
|
||||
|
@@ -10,6 +10,6 @@
|
||||
|
||||
# Type Alias: SVG
|
||||
|
||||
> **SVG**: `d3.Selection`<`SVGSVGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
> **SVG** = `d3.Selection`<`SVGSVGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L126)
|
||||
|
@@ -10,6 +10,6 @@
|
||||
|
||||
# Type Alias: SVGGroup
|
||||
|
||||
> **SVGGroup**: `d3.Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
> **SVGGroup** = `d3.Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L128)
|
||||
|
63
docs/config/tidy-tree.md
Normal file
63
docs/config/tidy-tree.md
Normal file
@@ -0,0 +1,63 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/tidy-tree.md](../../packages/mermaid/src/docs/config/tidy-tree.md).
|
||||
|
||||
# Tidy-tree Layout Instructions
|
||||
|
||||
Instructions to use the Tidy-tree layout algorithm.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install non-layered-tidy-tree-layout
|
||||
# or
|
||||
yarn add non-layered-tidy-tree-layout
|
||||
|
||||
```
|
||||
|
||||
There's also a built version: `dist/non-layered-tidy-tree-layout.js` for use with browser `<script>` tag, or as a Javascript module.
|
||||
|
||||
## Tidy tree Layouts
|
||||
|
||||
Mermaid also supports a Tidy Tree layout for mindmaps.
|
||||
|
||||
```
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
```
|
||||
|
||||
### With bundlers
|
||||
|
||||
```sh
|
||||
npm install @mermaid-js/layout-tidy-tree
|
||||
```
|
||||
|
||||
```ts
|
||||
import mermaid from 'mermaid';
|
||||
import tidyTreeLayouts from '@mermaid-js/layout-tidy-tree';
|
||||
|
||||
mermaid.registerLayoutLoaders(tidyTreeLayouts);
|
||||
```
|
||||
|
||||
### With CDN
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
||||
import tidyTreeLayouts from 'https://cdn.jsdelivr.net/npm/@mermaid-js/layout-tidy-tree@0/dist/mermaid-layout-tidy-tree.esm.min.mjs';
|
||||
|
||||
mermaid.registerLayoutLoaders(tidyTreeLayouts);
|
||||
</script>
|
||||
```
|
@@ -73,7 +73,7 @@ To add an integration to this list, see the [Integrations - create page](./integ
|
||||
- [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) ✅
|
||||
- [Outline](https://docs.getoutline.com/s/guide/doc/diagrams-KQiKoT4wzK) ✅
|
||||
- [Redmine](https://redmine.org)
|
||||
- [Mermaid Macro](https://redmine.org/plugins/redmine_mermaid_macro)
|
||||
- [Mermaid Macro](https://www.redmine.org/plugins/redmine_mermaid_macro)
|
||||
- [Markdown for mermaid plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
|
||||
- [redmine-mermaid](https://github.com/styz/redmine_mermaid)
|
||||
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
|
||||
@@ -117,7 +117,7 @@ Content Management Systems/Enterprise Content Management
|
||||
- [Grav CMS](https://getgrav.org/)
|
||||
- [Mermaid Diagrams Plugin](https://github.com/DanielFlaum/grav-plugin-mermaid-diagrams)
|
||||
- [GitLab Markdown Adapter](https://github.com/Goutte/grav-plugin-gitlab-markdown-adapter)
|
||||
- [Tiki Wiki CMS Groupware](https://tiki.org)
|
||||
- [Tiki](https://tiki.org)
|
||||
- [Tracker Entity Relationship Diagram](https://doc.tiki.org/Tracker-Entity-Relationship-Diagram)
|
||||
- [VitePress](https://vitepress.vuejs.org/)
|
||||
- [Plugin for Mermaid.js](https://emersonbottero.github.io/vitepress-plugin-mermaid/)
|
||||
|
@@ -29,7 +29,6 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
||||
- **Plugins** - A plugin system for extending the functionality of Mermaid.
|
||||
|
||||
Official Mermaid Chart plugins:
|
||||
|
||||
- [Mermaid Chart GPT](https://chatgpt.com/g/g-684cc36f30208191b21383b88650a45d-mermaid-chart-diagrams-and-charts)
|
||||
- [Confluence](https://marketplace.atlassian.com/apps/1234056/mermaid-chart-for-confluence?hosting=cloud&tab=overview)
|
||||
- [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud)
|
||||
|
@@ -35,13 +35,11 @@ The Mermaid Chart team is excited to introduce a new Visual Editor for Flowchart
|
||||
Learn more:
|
||||
|
||||
- Visual Editor For Flowcharts
|
||||
|
||||
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts)
|
||||
|
||||
- [Demo video](https://www.youtube.com/watch?v=5aja0gijoO0)
|
||||
|
||||
- Visual Editor For Sequence diagrams
|
||||
|
||||
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams)
|
||||
|
||||
- [Demo video](https://youtu.be/imc2u5_N6Dc)
|
||||
|
@@ -6,66 +6,6 @@
|
||||
|
||||
# Blog
|
||||
|
||||
## [Mermaid introduces the Visual Editor for Entity Relationship diagrams](https://docs.mermaidchart.com/blog/posts/mermaid-introduces-the-visual-editor-for-entity-relationship-diagrams)
|
||||
|
||||
7/15/2025 • 7 mins
|
||||
|
||||
Mermaid just introduced a Visual Editor for Entity Relationship diagrams, letting anyone map database structures through a simple point-and-click interface instead of code. This no-code ER builder now sits alongside Mermaid’s editors for flowcharts, sequence, and class diagrams, enabling teams to craft and share polished data models for apps, AI, and business processes.
|
||||
|
||||
## [Mermaid supports Treemap Diagrams now!!!](https://docs.mermaidchart.com/blog/posts/mermaid-have-treemap-diagrams-now)
|
||||
|
||||
7/3/2025 • 4 mins
|
||||
|
||||
Mermaid has introduced Treemap diagrams, currently in beta, enhancing hierarchical data visualization. Treemap diagrams use nested rectangles to represent data relationships, focusing on size and proportions. They offer various applications, including budget visualization and market analysis. With simple syntax and customization options, users can effectively present complex data hierarchies.
|
||||
|
||||
## [AI Diagram Generators and Data Visualization: Best Practices](https://docs.mermaidchart.com/blog/posts/ai-diagram-generators-and-data-visualization-best-practices)
|
||||
|
||||
7/2/2025 • 6 mins
|
||||
|
||||
AI diagram generators transform complex data into clear, interactive visuals – enabling faster analysis, better decisions, and stronger collaboration across teams. By combining automation with manual refinement, these tools empower anyone to communicate insights effectively, regardless of technical skill level.
|
||||
|
||||
## [How to Choose the Best AI Diagram Generator for Your Needs (2025)](https://docs.mermaidchart.com/blog/posts/how-to-choose-the-best-ai-diagram-generator-for-your-needs-2025)
|
||||
|
||||
6/26/2025 • 14 mins
|
||||
|
||||
AI diagram generators are transforming how developers visualize and communicate complex systems, reducing hours of manual work into minutes. With tools like Mermaid AI, users benefit from both code-based and visual editing, enabling seamless collaboration and precision. Whether you’re diagramming workflows, software architecture, or data relationships, the right AI tool can significantly boost productivity and streamline communication.
|
||||
|
||||
## [5 Time-Saving Tips for Using Mermaid’s AI Diagram Generator Effectively](https://docs.mermaidchart.com/blog/posts/5-time-saving-tips-for-using-mermaids-ai-diagram-generator-effectively)
|
||||
|
||||
6/11/2025 • 10 mins
|
||||
|
||||
See how developers can save time and boost productivity using Mermaid Chart’s AI diagram generator. Learn five practical tips that help turn plain language into powerful, professional diagrams.
|
||||
|
||||
## [Enhancing Team Collaboration with AI-Powered Diagrams](https://docs.mermaidchart.com/blog/posts/enhancing-team-collaboration-with-ai-powered-diagrams)
|
||||
|
||||
5/27/2025 • 6 mins
|
||||
|
||||
Software teams move fast, but old-school diagramming tools can’t keep up. Mermaid Chart replaces static slides and whiteboards with real-time, AI-generated visuals that evolve with your code and ideas. Just describe a process in plain English, and watch it come to life.
|
||||
|
||||
## [What is an AI Diagram Generator? Benefits and Use Cases](https://docs.mermaidchart.com/blog/posts/what-is-an-ai-diagram-generator-benefits-and-use-cases)
|
||||
|
||||
5/22/2025 • 6 mins
|
||||
|
||||
Discover how AI diagram generators like Mermaid Chart transform developer workflows. Instantly turn text into flowcharts, ERDs, and system diagrams, no manual drag-and-drop needed. Learn how it works, key benefits, and real-world use cases.
|
||||
|
||||
## [How to Use Mermaid Chart as an AI Diagram Generator for Developers](https://docs.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator)
|
||||
|
||||
5/21/2025 • 9 mins
|
||||
|
||||
Would an AI diagram generator make your life easier? We think it would!
|
||||
|
||||
## [Mermaid Chart VS Code Plugin: Create and Edit Mermaid.js Diagrams in Visual Studio Code](https://docs.mermaidchart.com/blog/posts/mermaid-chart-vs-code-plugin-create-and-edit-mermaid-js-diagrams-in-visual-studio-code)
|
||||
|
||||
3/21/2025 • 5 mins
|
||||
|
||||
The Mermaid Chart VS Code Plugin is a powerful developer diagramming tool that brings Mermaid.js diagramming directly into your Visual Studio Code environment. Whether you’re visualizing software architecture, documenting API flows, fixing bad documentation, or managing flowcharts and sequence diagrams, this plugin integrates seamlessly into your workflow. Key Features of the Mermaid Chart VS Code \[…]
|
||||
|
||||
## [Mermaid Chart: The Evolution of Mermaid](https://docs.mermaidchart.com/blog/posts/mermaid-chart-the-evolution-of-mermaid)
|
||||
|
||||
1/30/2025 • 3 mins
|
||||
|
||||
Mermaid revolutionized diagramming with its simple, markdown-style syntax, empowering millions of developers worldwide. Now, Mermaid Chart takes it further with AI-powered visuals, a GUI for seamless editing, real-time collaboration, and advanced design tools. Experience the next generation of diagramming—faster, smarter, and built for modern teams. Try Mermaid Chart today!
|
||||
|
||||
## [GUI for editing Mermaid Class Diagrams](https://docs.mermaidchart.com/blog/posts/gui-for-editing-mermaid-class-diagrams)
|
||||
|
||||
1/17/2025 • 5 mins
|
||||
|
@@ -139,7 +139,6 @@ The following unfinished features are not supported in the short term.
|
||||
- [ ] Legend
|
||||
|
||||
- [x] System Context
|
||||
|
||||
- [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
|
||||
- [x] Person_Ext
|
||||
- [x] System(alias, label, ?descr, ?sprite, ?tags, $link)
|
||||
@@ -153,7 +152,6 @@ The following unfinished features are not supported in the short term.
|
||||
- [x] System_Boundary
|
||||
|
||||
- [x] Container diagram
|
||||
|
||||
- [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||
- [x] ContainerDb
|
||||
- [x] ContainerQueue
|
||||
@@ -163,7 +161,6 @@ The following unfinished features are not supported in the short term.
|
||||
- [x] Container_Boundary(alias, label, ?tags, $link)
|
||||
|
||||
- [x] Component diagram
|
||||
|
||||
- [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||
- [x] ComponentDb
|
||||
- [x] ComponentQueue
|
||||
@@ -172,18 +169,15 @@ The following unfinished features are not supported in the short term.
|
||||
- [x] ComponentQueue_Ext
|
||||
|
||||
- [x] Dynamic diagram
|
||||
|
||||
- [x] RelIndex(index, from, to, label, ?tags, $link)
|
||||
|
||||
- [x] Deployment diagram
|
||||
|
||||
- [x] Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link)
|
||||
- [x] Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link): short name of Deployment_Node()
|
||||
- [x] Node_L(alias, label, ?type, ?descr, ?sprite, ?tags, $link): left aligned Node()
|
||||
- [x] Node_R(alias, label, ?type, ?descr, ?sprite, ?tags, $link): right aligned Node()
|
||||
|
||||
- [x] Relationship Types
|
||||
|
||||
- [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||
- [x] BiRel (bidirectional relationship)
|
||||
- [x] Rel_U, Rel_Up
|
||||
|
@@ -324,53 +324,56 @@ This syntax creates a node A as a rectangle. It renders in the same way as `A["A
|
||||
|
||||
Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases:
|
||||
|
||||
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
||||
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
|
||||
| Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
|
||||
| Collate | Hourglass | `hourglass` | Represents a collate operation | `collate`, `hourglass` |
|
||||
| Com Link | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
|
||||
| Comment | Curly Brace | `brace` | Adds a comment | `brace-l`, `comment` |
|
||||
| Comment Right | Curly Brace | `brace-r` | Adds a comment | |
|
||||
| Comment with braces on both sides | Curly Braces | `braces` | Adds a comment | |
|
||||
| Data Input/Output | Lean Right | `lean-r` | Represents input or output | `in-out`, `lean-right` |
|
||||
| Data Input/Output | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
|
||||
| Database | Cylinder | `cyl` | Database storage | `cylinder`, `database`, `db` |
|
||||
| Decision | Diamond | `diam` | Decision-making step | `decision`, `diamond`, `question` |
|
||||
| Delay | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
|
||||
| Direct Access Storage | Horizontal Cylinder | `h-cyl` | Direct access storage | `das`, `horizontal-cylinder` |
|
||||
| Disk Storage | Lined Cylinder | `lin-cyl` | Disk storage | `disk`, `lined-cylinder` |
|
||||
| Display | Curved Trapezoid | `curv-trap` | Represents a display | `curved-trapezoid`, `display` |
|
||||
| Divided Process | Divided Rectangle | `div-rect` | Divided process shape | `div-proc`, `divided-process`, `divided-rectangle` |
|
||||
| Document | Document | `doc` | Represents a document | `doc`, `document` |
|
||||
| Event | Rounded Rectangle | `rounded` | Represents an event | `event` |
|
||||
| Extract | Triangle | `tri` | Extraction process | `extract`, `triangle` |
|
||||
| Fork/Join | Filled Rectangle | `fork` | Fork or join in process flow | `join` |
|
||||
| Internal Storage | Window Pane | `win-pane` | Internal storage | `internal-storage`, `window-pane` |
|
||||
| Junction | Filled Circle | `f-circ` | Junction point | `filled-circle`, `junction` |
|
||||
| Lined Document | Lined Document | `lin-doc` | Lined document | `lined-document` |
|
||||
| Lined/Shaded Process | Lined Rectangle | `lin-rect` | Lined process shape | `lin-proc`, `lined-process`, `lined-rectangle`, `shaded-process` |
|
||||
| Loop Limit | Trapezoidal Pentagon | `notch-pent` | Loop limit step | `loop-limit`, `notched-pentagon` |
|
||||
| Manual File | Flipped Triangle | `flip-tri` | Manual file operation | `flipped-triangle`, `manual-file` |
|
||||
| Manual Input | Sloped Rectangle | `sl-rect` | Manual input step | `manual-input`, `sloped-rectangle` |
|
||||
| Manual Operation | Trapezoid Base Top | `trap-t` | Represents a manual task | `inv-trapezoid`, `manual`, `trapezoid-top` |
|
||||
| Multi-Document | Stacked Document | `docs` | Multiple documents | `documents`, `st-doc`, `stacked-document` |
|
||||
| Multi-Process | Stacked Rectangle | `st-rect` | Multiple processes | `processes`, `procs`, `stacked-rectangle` |
|
||||
| Odd | Odd | `odd` | Odd shape | |
|
||||
| Paper Tape | Flag | `flag` | Paper tape | `paper-tape` |
|
||||
| Prepare Conditional | Hexagon | `hex` | Preparation or condition step | `hexagon`, `prepare` |
|
||||
| Priority Action | Trapezoid Base Bottom | `trap-b` | Priority action | `priority`, `trapezoid`, `trapezoid-bottom` |
|
||||
| Process | Rectangle | `rect` | Standard process shape | `proc`, `process`, `rectangle` |
|
||||
| Start | Circle | `circle` | Starting point | `circ` |
|
||||
| Start | Small Circle | `sm-circ` | Small starting point | `small-circle`, `start` |
|
||||
| Stop | Double Circle | `dbl-circ` | Represents a stop point | `double-circle` |
|
||||
| Stop | Framed Circle | `fr-circ` | Stop point | `framed-circle`, `stop` |
|
||||
| Stored Data | Bow Tie Rectangle | `bow-rect` | Stored data | `bow-tie-rectangle`, `stored-data` |
|
||||
| Subprocess | Framed Rectangle | `fr-rect` | Subprocess | `framed-rectangle`, `subproc`, `subprocess`, `subroutine` |
|
||||
| Summary | Crossed Circle | `cross-circ` | Summary | `crossed-circle`, `summary` |
|
||||
| Tagged Document | Tagged Document | `tag-doc` | Tagged document | `tag-doc`, `tagged-document` |
|
||||
| Tagged Process | Tagged Rectangle | `tag-rect` | Tagged process | `tag-proc`, `tagged-process`, `tagged-rectangle` |
|
||||
| Terminal Point | Stadium | `stadium` | Terminal point | `pill`, `terminal` |
|
||||
| Text Block | Text Block | `text` | Text block | |
|
||||
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
||||
| --------------------------------- | ---------------------- | ----------------- | ------------------------------ | ---------------------------------------------------------------- |
|
||||
| Bang | Bang | `bang` | Bang | `bang` |
|
||||
| Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
|
||||
| Cloud | Cloud | `cloud` | cloud | `cloud` |
|
||||
| Collate | Hourglass | `hourglass` | Represents a collate operation | `collate`, `hourglass` |
|
||||
| Com Link | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
|
||||
| Comment | Curly Brace | `brace` | Adds a comment | `brace-l`, `comment` |
|
||||
| Comment Right | Curly Brace | `brace-r` | Adds a comment | |
|
||||
| Comment with braces on both sides | Curly Braces | `braces` | Adds a comment | |
|
||||
| Data Input/Output | Lean Right | `lean-r` | Represents input or output | `in-out`, `lean-right` |
|
||||
| Data Input/Output | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
|
||||
| Database | Cylinder | `cyl` | Database storage | `cylinder`, `database`, `db` |
|
||||
| Decision | Diamond | `diam` | Decision-making step | `decision`, `diamond`, `question` |
|
||||
| Default Mindmap Node | defaultMindmapNode | `default-mindmap` | defaultMindmapNode | `default-mindmap`, `defaultMindmapNode` |
|
||||
| Delay | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
|
||||
| Direct Access Storage | Horizontal Cylinder | `h-cyl` | Direct access storage | `das`, `horizontal-cylinder` |
|
||||
| Disk Storage | Lined Cylinder | `lin-cyl` | Disk storage | `disk`, `lined-cylinder` |
|
||||
| Display | Curved Trapezoid | `curv-trap` | Represents a display | `curved-trapezoid`, `display` |
|
||||
| Divided Process | Divided Rectangle | `div-rect` | Divided process shape | `div-proc`, `divided-process`, `divided-rectangle` |
|
||||
| Document | Document | `doc` | Represents a document | `doc`, `document` |
|
||||
| Event | Rounded Rectangle | `rounded` | Represents an event | `event` |
|
||||
| Extract | Triangle | `tri` | Extraction process | `extract`, `triangle` |
|
||||
| Fork/Join | Filled Rectangle | `fork` | Fork or join in process flow | `join` |
|
||||
| Internal Storage | Window Pane | `win-pane` | Internal storage | `internal-storage`, `window-pane` |
|
||||
| Junction | Filled Circle | `f-circ` | Junction point | `filled-circle`, `junction` |
|
||||
| Lined Document | Lined Document | `lin-doc` | Lined document | `lined-document` |
|
||||
| Lined/Shaded Process | Lined Rectangle | `lin-rect` | Lined process shape | `lin-proc`, `lined-process`, `lined-rectangle`, `shaded-process` |
|
||||
| Loop Limit | Trapezoidal Pentagon | `notch-pent` | Loop limit step | `loop-limit`, `notched-pentagon` |
|
||||
| Manual File | Flipped Triangle | `flip-tri` | Manual file operation | `flipped-triangle`, `manual-file` |
|
||||
| Manual Input | Sloped Rectangle | `sl-rect` | Manual input step | `manual-input`, `sloped-rectangle` |
|
||||
| Manual Operation | Trapezoid Base Top | `trap-t` | Represents a manual task | `inv-trapezoid`, `manual`, `trapezoid-top` |
|
||||
| Multi-Document | Stacked Document | `docs` | Multiple documents | `documents`, `st-doc`, `stacked-document` |
|
||||
| Multi-Process | Stacked Rectangle | `st-rect` | Multiple processes | `processes`, `procs`, `stacked-rectangle` |
|
||||
| Odd | Odd | `odd` | Odd shape | |
|
||||
| Paper Tape | Flag | `flag` | Paper tape | `paper-tape` |
|
||||
| Prepare Conditional | Hexagon | `hex` | Preparation or condition step | `hexagon`, `prepare` |
|
||||
| Priority Action | Trapezoid Base Bottom | `trap-b` | Priority action | `priority`, `trapezoid`, `trapezoid-bottom` |
|
||||
| Process | Rectangle | `rect` | Standard process shape | `proc`, `process`, `rectangle` |
|
||||
| Start | Circle | `circle` | Starting point | `circ` |
|
||||
| Start | Small Circle | `sm-circ` | Small starting point | `small-circle`, `start` |
|
||||
| Stop | Double Circle | `dbl-circ` | Represents a stop point | `double-circle` |
|
||||
| Stop | Framed Circle | `fr-circ` | Stop point | `framed-circle`, `stop` |
|
||||
| Stored Data | Bow Tie Rectangle | `bow-rect` | Stored data | `bow-tie-rectangle`, `stored-data` |
|
||||
| Subprocess | Framed Rectangle | `fr-rect` | Subprocess | `framed-rectangle`, `subproc`, `subprocess`, `subroutine` |
|
||||
| Summary | Crossed Circle | `cross-circ` | Summary | `crossed-circle`, `summary` |
|
||||
| Tagged Document | Tagged Document | `tag-doc` | Tagged document | `tag-doc`, `tagged-document` |
|
||||
| Tagged Process | Tagged Rectangle | `tag-rect` | Tagged process | `tag-proc`, `tagged-process`, `tagged-rectangle` |
|
||||
| Terminal Point | Stadium | `stadium` | Terminal point | `pill`, `terminal` |
|
||||
| Text Block | Text Block | `text` | Text block | |
|
||||
|
||||
### Example Flowchart with New Shapes
|
||||
|
||||
@@ -1816,7 +1819,7 @@ config:
|
||||
graph LR
|
||||
```
|
||||
|
||||
#### Edge level curve style using Edge IDs (v11.10.0+)
|
||||
#### Edge level curve style using Edge IDs (v\<MERMAID_RELEASE_VERSION>+)
|
||||
|
||||
You can assign IDs to [edges](#attaching-an-id-to-edges). After assigning an ID you can modify the line style by modifying the edge's `curve` property using the following syntax:
|
||||
|
||||
|
@@ -360,7 +360,8 @@ gantt
|
||||
weekday monday
|
||||
```
|
||||
|
||||
> **Warning** > `millisecond` and `second` support was added in v10.3.0
|
||||
> **Warning**
|
||||
> `millisecond` and `second` support was added in v10.3.0
|
||||
|
||||
## Output in compact mode
|
||||
|
||||
|
@@ -314,3 +314,22 @@ You can also refer the [implementation in the live editor](https://github.com/me
|
||||
cspell:locale en,en-gb
|
||||
cspell:ignore Buzan
|
||||
--->
|
||||
|
||||
## Layouts
|
||||
|
||||
Mermaid also supports a Tidy Tree layout for mindmaps.
|
||||
|
||||
```
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
```
|
||||
|
||||
Instructions to add and register tidy-tree layout are present in [Tidy Tree Configuration](/config/tidy-tree)
|
||||
|
@@ -39,7 +39,7 @@ Drawing a pie chart is really simple in mermaid.
|
||||
|
||||
**Note:**
|
||||
|
||||
> Pie chart values must be **positive numbers greater than zero**.
|
||||
> Pie chart values must be **positive numbers greater than zero**.\
|
||||
> **Negative values are not allowed** and will result in an error.
|
||||
|
||||
\[pie] \[showData] (OPTIONAL)
|
||||
|
@@ -17,6 +17,7 @@ export default tseslint.config(
|
||||
...tseslint.configs.stylisticTypeChecked,
|
||||
{
|
||||
ignores: [
|
||||
'**/*.d.ts',
|
||||
'**/dist/',
|
||||
'**/node_modules/',
|
||||
'.git/',
|
||||
|
@@ -27,6 +27,9 @@
|
||||
"devDependencies": {
|
||||
"mermaid": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mermaid": "workspace:~"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
@@ -1,16 +1,5 @@
|
||||
# @mermaid-js/layout-elk
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6857](https://github.com/mermaid-js/mermaid/pull/6857) [`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6) Thanks [@knsv](https://github.com/knsv)! - feat: Exposing elk configuration forceNodeModelOrder and considerModelOrder to the mermaid configuration
|
||||
|
||||
- [#6849](https://github.com/mermaid-js/mermaid/pull/6849) [`2260948`](https://github.com/mermaid-js/mermaid/commit/2260948b7bda08f00616c2ce678bed1da69eb96c) Thanks [@anderium](https://github.com/anderium)! - Make elk not force node model order, but strongly consider it instead
|
||||
|
||||
- Updated dependencies [[`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6), [`2c0931d`](https://github.com/mermaid-js/mermaid/commit/2c0931da46794b49d2523211e25f782900c34e94), [`33e08da`](https://github.com/mermaid-js/mermaid/commit/33e08daf175125295a06b1b80279437004a4e865), [`814b68b`](https://github.com/mermaid-js/mermaid/commit/814b68b4a94813f7c6b3d7fb4559532a7bab2652), [`fce7cab`](https://github.com/mermaid-js/mermaid/commit/fce7cabb71d68a20a66246fe23d066512126a412), [`fc07f0d`](https://github.com/mermaid-js/mermaid/commit/fc07f0d8abca49e4f887d7457b7b94fb07d1e3da), [`12e01bd`](https://github.com/mermaid-js/mermaid/commit/12e01bdb5cacf3569133979a5a4f1d8973e9aec1), [`01aaef3`](https://github.com/mermaid-js/mermaid/commit/01aaef39b4a1ec8bc5a0c6bfa3a20b712d67f4dc), [`daf8d8d`](https://github.com/mermaid-js/mermaid/commit/daf8d8d3befcd600618a629977b76463b38d0ad9), [`c36cd05`](https://github.com/mermaid-js/mermaid/commit/c36cd05c45ac3090181152b4dae41f8d7b569bd6), [`8bb29fc`](https://github.com/mermaid-js/mermaid/commit/8bb29fc879329ad109898e4025b4f4eba2ab0649), [`71b04f9`](https://github.com/mermaid-js/mermaid/commit/71b04f93b07f876df2b30656ef36036c1d0e4e4f), [`c99bce6`](https://github.com/mermaid-js/mermaid/commit/c99bce6bab4c7ce0b81b66d44f44853ce4aeb1c3), [`6cc1926`](https://github.com/mermaid-js/mermaid/commit/6cc192680a2531cab28f87a8061a53b786e010f3), [`9da6fb3`](https://github.com/mermaid-js/mermaid/commit/9da6fb39ae278401771943ac85d6d1b875f78cf1), [`e48b0ba`](https://github.com/mermaid-js/mermaid/commit/e48b0ba61dab7f95aa02da603b5b7d383b894932), [`4d62d59`](https://github.com/mermaid-js/mermaid/commit/4d62d5963238400270e9314c6e4d506f48147074), [`e9ce8cf`](https://github.com/mermaid-js/mermaid/commit/e9ce8cf4da9062d85098042044822100889bb0dd), [`9258b29`](https://github.com/mermaid-js/mermaid/commit/9258b2933bbe1ef41087345ffea3731673671c49), [`da90f67`](https://github.com/mermaid-js/mermaid/commit/da90f6760b6efb0da998bcb63b75eecc29e06c08), [`0133f1c`](https://github.com/mermaid-js/mermaid/commit/0133f1c0c5cff4fc4c8e0b99e9cf0b3d49dcbe71), [`895f9d4`](https://github.com/mermaid-js/mermaid/commit/895f9d43ff98ca05ebfba530789f677f31a011ff)]:
|
||||
- mermaid@11.10.0
|
||||
|
||||
## 0.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
This package provides a layout engine for Mermaid based on the [ELK](https://www.eclipse.org/elk/) layout engine.
|
||||
|
||||
> [!NOTE]
|
||||
> [!NOTE]
|
||||
> The ELK Layout engine will not be available in all providers that support mermaid by default.
|
||||
> The websites will have to install the `@mermaid-js/layout-elk` package to use the ELK layout engine.
|
||||
|
||||
@@ -69,4 +69,4 @@ mermaid.registerLayoutLoaders(elkLayouts);
|
||||
- `elk.mrtree`: Multi-root tree layout
|
||||
- `elk.sporeOverlap`: Spore overlap layout
|
||||
|
||||
<!-- TODO: Add images for these layouts, as GitHub doesn't support natively -->
|
||||
<!-- TODO: Add images for these layouts, as GitHub doesn't support natively. -->
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mermaid-js/layout-elk",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.8",
|
||||
"description": "ELK layout engine for mermaid",
|
||||
"module": "dist/mermaid-layout-elk.core.mjs",
|
||||
"types": "dist/layouts.d.ts",
|
||||
|
9
packages/mermaid-layout-elk/src/find-common-ancestor.d.ts
vendored
Normal file
9
packages/mermaid-layout-elk/src/find-common-ancestor.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface TreeData {
|
||||
parentById: Record<string, string>;
|
||||
childrenById: Record<string, string[]>;
|
||||
}
|
||||
export declare const findCommonAncestor: (
|
||||
id1: string,
|
||||
id2: string,
|
||||
{ parentById }: TreeData
|
||||
) => string;
|
@@ -4,7 +4,8 @@ import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from '
|
||||
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
||||
|
||||
type Node = LayoutData['nodes'][number];
|
||||
|
||||
// Used to calculate distances in order to avoid floating number rounding issues when comparing floating numbers
|
||||
const epsilon = 0.0001;
|
||||
interface LabelData {
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -13,11 +14,20 @@ interface LabelData {
|
||||
}
|
||||
|
||||
interface NodeWithVertex extends Omit<Node, 'domId'> {
|
||||
children?: unknown[];
|
||||
children?: LayoutData['nodes'];
|
||||
labelData?: LabelData;
|
||||
domId?: Node['domId'] | SVGGroup | d3.Selection<SVGAElement, unknown, Element | null, unknown>;
|
||||
}
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
function distance(p1?: Point, p2?: Point): number {
|
||||
if (!p1 || !p2) {
|
||||
return 0;
|
||||
}
|
||||
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
||||
}
|
||||
export const render = async (
|
||||
data4Layout: LayoutData,
|
||||
svg: SVG,
|
||||
@@ -51,15 +61,30 @@ export const render = async (
|
||||
|
||||
// Add the element to the DOM
|
||||
if (!node.isGroup) {
|
||||
// Create a clean node object for ELK with only the properties it expects
|
||||
const child: NodeWithVertex = {
|
||||
...node,
|
||||
id: node.id,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
// Store the original node data for later use
|
||||
label: node.label,
|
||||
isGroup: node.isGroup,
|
||||
shape: node.shape,
|
||||
padding: node.padding,
|
||||
cssClasses: node.cssClasses,
|
||||
cssStyles: node.cssStyles,
|
||||
look: node.look,
|
||||
// Include parentId for subgraph processing
|
||||
parentId: node.parentId,
|
||||
};
|
||||
graph.children.push(child);
|
||||
nodeDb[node.id] = child;
|
||||
|
||||
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
||||
const boundingBox = childNodeEl.node()!.getBBox();
|
||||
// Store the domId separately for rendering, not in the ELK graph
|
||||
child.domId = childNodeEl;
|
||||
child.calcIntersect = node.calcIntersect;
|
||||
child.width = boundingBox.width;
|
||||
child.height = boundingBox.height;
|
||||
} else {
|
||||
@@ -459,302 +484,6 @@ export const render = async (
|
||||
}
|
||||
}
|
||||
|
||||
function intersectLine(
|
||||
p1: { y: number; x: number },
|
||||
p2: { y: number; x: number },
|
||||
q1: { x: any; y: any },
|
||||
q2: { x: any; y: any }
|
||||
) {
|
||||
log.debug('UIO intersectLine', p1, p2, q1, q2);
|
||||
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
|
||||
// p7 and p473.
|
||||
|
||||
// let a1, a2, b1, b2, c1, c2;
|
||||
// let r1, r2, r3, r4;
|
||||
// let denom, offset, num;
|
||||
// let x, y;
|
||||
|
||||
// Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
|
||||
// b1 y + c1 = 0.
|
||||
const a1 = p2.y - p1.y;
|
||||
const b1 = p1.x - p2.x;
|
||||
const c1 = p2.x * p1.y - p1.x * p2.y;
|
||||
|
||||
// Compute r3 and r4.
|
||||
const r3 = a1 * q1.x + b1 * q1.y + c1;
|
||||
const r4 = a1 * q2.x + b1 * q2.y + c1;
|
||||
|
||||
const epsilon = 1e-6;
|
||||
|
||||
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
||||
// same side of line 1, the line segments do not intersect.
|
||||
if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
|
||||
const a2 = q2.y - q1.y;
|
||||
const b2 = q1.x - q2.x;
|
||||
const c2 = q2.x * q1.y - q1.x * q2.y;
|
||||
|
||||
// Compute r1 and r2
|
||||
const r1 = a2 * p1.x + b2 * p1.y + c2;
|
||||
const r2 = a2 * p2.x + b2 * p2.y + c2;
|
||||
|
||||
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
||||
// on same side of second line segment, the line segments do
|
||||
// not intersect.
|
||||
if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Line segments intersect: compute intersection point.
|
||||
const denom = a1 * b2 - a2 * b1;
|
||||
if (denom === 0) {
|
||||
return /*COLLINEAR*/;
|
||||
}
|
||||
|
||||
const offset = Math.abs(denom / 2);
|
||||
|
||||
// The denom/2 is to get rounding instead of truncating. It
|
||||
// is added or subtracted to the numerator, depending upon the
|
||||
// sign of the numerator.
|
||||
let num = b1 * c2 - b2 * c1;
|
||||
const x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
num = a2 * c1 - a1 * c2;
|
||||
const y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
return { x: x, y: y };
|
||||
}
|
||||
|
||||
function sameSign(r1: number, r2: number) {
|
||||
return r1 * r2 > 0;
|
||||
}
|
||||
const diamondIntersection = (
|
||||
bounds: { x: any; y: any; width: any; height: any },
|
||||
outsidePoint: { x: number; y: number },
|
||||
insidePoint: any
|
||||
) => {
|
||||
const x1 = bounds.x;
|
||||
const y1 = bounds.y;
|
||||
|
||||
const w = bounds.width; //+ bounds.padding;
|
||||
const h = bounds.height; // + bounds.padding;
|
||||
|
||||
const polyPoints = [
|
||||
{ x: x1, y: y1 - h / 2 },
|
||||
{ x: x1 + w / 2, y: y1 },
|
||||
{ x: x1, y: y1 + h / 2 },
|
||||
{ x: x1 - w / 2, y: y1 },
|
||||
];
|
||||
log.debug(
|
||||
`APA16 diamondIntersection calc abc89:
|
||||
outsidePoint: ${JSON.stringify(outsidePoint)}
|
||||
insidePoint : ${JSON.stringify(insidePoint)}
|
||||
node-bounds : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`,
|
||||
JSON.stringify(polyPoints)
|
||||
);
|
||||
|
||||
const intersections = [];
|
||||
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
|
||||
polyPoints.forEach(function (entry) {
|
||||
minX = Math.min(minX, entry.x);
|
||||
minY = Math.min(minY, entry.y);
|
||||
});
|
||||
|
||||
const left = x1 - w / 2 - minX;
|
||||
const top = y1 - h / 2 - minY;
|
||||
|
||||
for (let i = 0; i < polyPoints.length; i++) {
|
||||
const p1 = polyPoints[i];
|
||||
const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
|
||||
const intersect = intersectLine(
|
||||
bounds,
|
||||
outsidePoint,
|
||||
{ x: left + p1.x, y: top + p1.y },
|
||||
{ x: left + p2.x, y: top + p2.y }
|
||||
);
|
||||
|
||||
if (intersect) {
|
||||
intersections.push(intersect);
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersections.length) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
log.debug('UIO intersections', intersections);
|
||||
|
||||
if (intersections.length > 1) {
|
||||
// More intersections, find the one nearest to edge end point
|
||||
intersections.sort(function (p, q) {
|
||||
const pdx = p.x - outsidePoint.x;
|
||||
const pdy = p.y - outsidePoint.y;
|
||||
const distp = Math.sqrt(pdx * pdx + pdy * pdy);
|
||||
|
||||
const qdx = q.x - outsidePoint.x;
|
||||
const qdy = q.y - outsidePoint.y;
|
||||
const distq = Math.sqrt(qdx * qdx + qdy * qdy);
|
||||
|
||||
return distp < distq ? -1 : distp === distq ? 0 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
return intersections[0];
|
||||
};
|
||||
|
||||
const intersection = (
|
||||
node: { x: any; y: any; width: number; height: number },
|
||||
outsidePoint: { x: number; y: number },
|
||||
insidePoint: { x: number; y: number }
|
||||
) => {
|
||||
log.debug(`intersection calc abc89:
|
||||
outsidePoint: ${JSON.stringify(outsidePoint)}
|
||||
insidePoint : ${JSON.stringify(insidePoint)}
|
||||
node : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`);
|
||||
const x = node.x;
|
||||
const y = node.y;
|
||||
|
||||
const dx = Math.abs(x - insidePoint.x);
|
||||
// const dy = Math.abs(y - insidePoint.y);
|
||||
const w = node.width / 2;
|
||||
let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
|
||||
const h = node.height / 2;
|
||||
|
||||
const Q = Math.abs(outsidePoint.y - insidePoint.y);
|
||||
const R = Math.abs(outsidePoint.x - insidePoint.x);
|
||||
|
||||
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {
|
||||
// Intersection is top or bottom of rect.
|
||||
const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
|
||||
r = (R * q) / Q;
|
||||
const res = {
|
||||
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,
|
||||
y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,
|
||||
};
|
||||
|
||||
if (r === 0) {
|
||||
res.x = outsidePoint.x;
|
||||
res.y = outsidePoint.y;
|
||||
}
|
||||
if (R === 0) {
|
||||
res.x = outsidePoint.x;
|
||||
}
|
||||
if (Q === 0) {
|
||||
res.y = outsidePoint.y;
|
||||
}
|
||||
|
||||
log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line
|
||||
|
||||
return res;
|
||||
} else {
|
||||
// Intersection on sides of rect
|
||||
if (insidePoint.x < outsidePoint.x) {
|
||||
r = outsidePoint.x - w - x;
|
||||
} else {
|
||||
// r = outsidePoint.x - w - x;
|
||||
r = x - w - outsidePoint.x;
|
||||
}
|
||||
const q = (Q * r) / R;
|
||||
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w;
|
||||
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
|
||||
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
||||
// let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
|
||||
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
||||
log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y });
|
||||
if (r === 0) {
|
||||
_x = outsidePoint.x;
|
||||
_y = outsidePoint.y;
|
||||
}
|
||||
if (R === 0) {
|
||||
_x = outsidePoint.x;
|
||||
}
|
||||
if (Q === 0) {
|
||||
_y = outsidePoint.y;
|
||||
}
|
||||
|
||||
return { x: _x, y: _y };
|
||||
}
|
||||
};
|
||||
const outsideNode = (
|
||||
node: { x: any; y: any; width: number; height: number },
|
||||
point: { x: number; y: number }
|
||||
) => {
|
||||
const x = node.x;
|
||||
const y = node.y;
|
||||
const dx = Math.abs(point.x - x);
|
||||
const dy = Math.abs(point.y - y);
|
||||
const w = node.width / 2;
|
||||
const h = node.height / 2;
|
||||
if (dx >= w || dy >= h) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* This function will page a path and node where the last point(s) in the path is inside the node
|
||||
* and return an update path ending by the border of the node.
|
||||
*/
|
||||
const cutPathAtIntersect = (
|
||||
_points: any[],
|
||||
bounds: { x: any; y: any; width: any; height: any; padding: any },
|
||||
isDiamond: boolean
|
||||
) => {
|
||||
log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
|
||||
const points: any[] = [];
|
||||
let lastPointOutside = _points[0];
|
||||
let isInside = false;
|
||||
_points.forEach((point: any) => {
|
||||
// check if point is inside the boundary rect
|
||||
if (!outsideNode(bounds, point) && !isInside) {
|
||||
// First point inside the rect found
|
||||
// Calc the intersection coord between the point and the last point outside the rect
|
||||
let inter;
|
||||
|
||||
if (isDiamond) {
|
||||
const inter2 = diamondIntersection(bounds, lastPointOutside, point);
|
||||
const distance = Math.sqrt(
|
||||
(lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2
|
||||
);
|
||||
if (distance > 1) {
|
||||
inter = inter2;
|
||||
}
|
||||
}
|
||||
if (!inter) {
|
||||
inter = intersection(bounds, lastPointOutside, point);
|
||||
}
|
||||
|
||||
// Check case where the intersection is the same as the last point
|
||||
let pointPresent = false;
|
||||
points.forEach((p) => {
|
||||
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
|
||||
});
|
||||
// if (!pointPresent) {
|
||||
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
|
||||
points.push(inter);
|
||||
} else {
|
||||
log.debug('abc88 no intersect', inter, points);
|
||||
}
|
||||
// points.push(inter);
|
||||
isInside = true;
|
||||
} else {
|
||||
// Outside
|
||||
log.debug('abc88 outside', point, lastPointOutside, points);
|
||||
lastPointOutside = point;
|
||||
// points.push(point);
|
||||
if (!isInside) {
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
});
|
||||
return points;
|
||||
};
|
||||
|
||||
// @ts-ignore - ELK is not typed
|
||||
const elk = new ELK();
|
||||
const element = svg.select('g');
|
||||
@@ -766,10 +495,7 @@ export const render = async (
|
||||
id: 'root',
|
||||
layoutOptions: {
|
||||
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
||||
'elk.layered.crossingMinimization.forceNodeModelOrder':
|
||||
data4Layout.config.elk?.forceNodeModelOrder,
|
||||
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
|
||||
|
||||
'elk.layered.crossingMinimization.forceNodeModelOrder': true,
|
||||
'elk.algorithm': algorithm,
|
||||
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
|
||||
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
|
||||
@@ -869,11 +595,16 @@ export const render = async (
|
||||
delete node.height;
|
||||
}
|
||||
});
|
||||
elkGraph.edges.forEach((edge: any) => {
|
||||
log.debug('APA01 processing edges, count:', elkGraph.edges.length);
|
||||
elkGraph.edges.forEach((edge: any, index: number) => {
|
||||
log.debug('APA01 processing edge', index, ':', edge);
|
||||
const source = edge.sources[0];
|
||||
const target = edge.targets[0];
|
||||
log.debug('APA01 source:', source, 'target:', target);
|
||||
log.debug('APA01 nodeDb[source]:', nodeDb[source]);
|
||||
log.debug('APA01 nodeDb[target]:', nodeDb[target]);
|
||||
|
||||
if (nodeDb[source].parentId !== nodeDb[target].parentId) {
|
||||
if (nodeDb[source] && nodeDb[target] && nodeDb[source].parentId !== nodeDb[target].parentId) {
|
||||
const ancestorId = findCommonAncestor(source, target, parentLookupDb);
|
||||
// an edge that breaks a subgraph has been identified, set configuration accordingly
|
||||
setIncludeChildrenPolicy(source, ancestorId);
|
||||
@@ -881,7 +612,37 @@ export const render = async (
|
||||
}
|
||||
});
|
||||
|
||||
const g = await elk.layout(elkGraph);
|
||||
log.debug('APA01 before');
|
||||
log.debug('APA01 elkGraph structure:', JSON.stringify(elkGraph, null, 2));
|
||||
log.debug('APA01 elkGraph.children length:', elkGraph.children?.length);
|
||||
log.debug('APA01 elkGraph.edges length:', elkGraph.edges?.length);
|
||||
|
||||
// Validate that all edge references exist as nodes
|
||||
elkGraph.edges?.forEach((edge: any, index: number) => {
|
||||
log.debug(`APA01 validating edge ${index}:`, edge);
|
||||
if (edge.sources) {
|
||||
edge.sources.forEach((sourceId: any) => {
|
||||
const sourceExists = elkGraph.children?.some((child: any) => child.id === sourceId);
|
||||
log.debug(`APA01 source ${sourceId} exists:`, sourceExists);
|
||||
});
|
||||
}
|
||||
if (edge.targets) {
|
||||
edge.targets.forEach((targetId: any) => {
|
||||
const targetExists = elkGraph.children?.some((child: any) => child.id === targetId);
|
||||
log.debug(`APA01 target ${targetId} exists:`, targetExists);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let g;
|
||||
try {
|
||||
g = await elk.layout(elkGraph);
|
||||
log.debug('APA01 after - success');
|
||||
log.debug('APA01 layout result:', JSON.stringify(g, null, 2));
|
||||
} catch (error) {
|
||||
log.error('APA01 ELK layout error:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// debugger;
|
||||
await drawNodes(0, 0, g.children, svg, subGraphsEl, 0);
|
||||
@@ -969,42 +730,37 @@ export const render = async (
|
||||
startNode.innerHTML
|
||||
);
|
||||
}
|
||||
if (startNode.shape === 'diamond' || startNode.shape === 'diam') {
|
||||
edge.points.unshift({
|
||||
x: startNode.offset.posX + startNode.width / 2,
|
||||
y: startNode.offset.posY + startNode.height / 2,
|
||||
});
|
||||
}
|
||||
if (endNode.shape === 'diamond' || endNode.shape === 'diam') {
|
||||
edge.points.push({
|
||||
x: endNode.offset.posX + endNode.width / 2,
|
||||
y: endNode.offset.posY + endNode.height / 2,
|
||||
});
|
||||
}
|
||||
|
||||
edge.points = cutPathAtIntersect(
|
||||
edge.points.reverse(),
|
||||
{
|
||||
x: startNode.offset.posX + startNode.width / 2,
|
||||
y: startNode.offset.posY + startNode.height / 2,
|
||||
width: sw,
|
||||
height: startNode.height,
|
||||
padding: startNode.padding,
|
||||
},
|
||||
startNode.shape === 'diamond' || startNode.shape === 'diam'
|
||||
).reverse();
|
||||
if (startNode.calcIntersect) {
|
||||
const intersection = startNode.calcIntersect(
|
||||
{
|
||||
x: startNode.offset.posX + startNode.width / 2,
|
||||
y: startNode.offset.posY + startNode.height / 2,
|
||||
width: startNode.width,
|
||||
height: startNode.height,
|
||||
},
|
||||
edge.points[0]
|
||||
);
|
||||
|
||||
edge.points = cutPathAtIntersect(
|
||||
edge.points,
|
||||
{
|
||||
x: endNode.offset.posX + endNode.width / 2,
|
||||
y: endNode.offset.posY + endNode.height / 2,
|
||||
width: ew,
|
||||
height: endNode.height,
|
||||
padding: endNode.padding,
|
||||
},
|
||||
endNode.shape === 'diamond' || endNode.shape === 'diam'
|
||||
);
|
||||
if (distance(intersection, edge.points[0]) > epsilon) {
|
||||
edge.points.unshift(intersection);
|
||||
}
|
||||
}
|
||||
if (endNode.calcIntersect) {
|
||||
const intersection = endNode.calcIntersect(
|
||||
{
|
||||
x: endNode.offset.posX + endNode.width / 2,
|
||||
y: endNode.offset.posY + endNode.height / 2,
|
||||
width: endNode.width,
|
||||
height: endNode.height,
|
||||
},
|
||||
edge.points[edge.points.length - 1]
|
||||
);
|
||||
|
||||
if (distance(intersection, edge.points[edge.points.length - 1]) > epsilon) {
|
||||
edge.points.push(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
const paths = insertEdge(
|
||||
edgesEl,
|
||||
@@ -1015,7 +771,6 @@ export const render = async (
|
||||
endNode,
|
||||
data4Layout.diagramId
|
||||
);
|
||||
log.info('APA12 edge points after insert', JSON.stringify(edge.points));
|
||||
|
||||
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
|
||||
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
|
||||
|
@@ -5,6 +5,6 @@
|
||||
"outDir": "./dist",
|
||||
"types": ["vitest/importMeta", "vitest/globals"]
|
||||
},
|
||||
"include": ["./src/**/*.ts"],
|
||||
"include": ["./src/**/*.ts", "./src/**/*.d.ts"],
|
||||
"typeRoots": ["./src/types"]
|
||||
}
|
||||
|
59
packages/mermaid-layout-tidy-tree/README.md
Normal file
59
packages/mermaid-layout-tidy-tree/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# @mermaid-js/layout-tidy-tree
|
||||
|
||||
This package provides a bidirectional tidy tree layout engine for Mermaid based on the non-layered-tidy-tree-layout algorithm.
|
||||
|
||||
> [!NOTE]
|
||||
> The Tidy Tree Layout engine will not be available in all providers that support mermaid by default.
|
||||
> The websites will have to install the @mermaid-js/layout-tidy-tree package to use the Tidy Tree layout engine.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
```
|
||||
|
||||
### With bundlers
|
||||
|
||||
```sh
|
||||
npm install @mermaid-js/layout-tidy-tree
|
||||
```
|
||||
|
||||
```ts
|
||||
import mermaid from 'mermaid';
|
||||
import tidyTreeLayouts from '@mermaid-js/layout-tidy-tree';
|
||||
|
||||
mermaid.registerLayoutLoaders(tidyTreeLayouts);
|
||||
```
|
||||
|
||||
### With CDN
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
||||
import tidyTreeLayouts from 'https://cdn.jsdelivr.net/npm/@mermaid-js/layout-tidy-tree@0/dist/mermaid-layout-tidy-tree.esm.min.mjs';
|
||||
|
||||
mermaid.registerLayoutLoaders(tidyTreeLayouts);
|
||||
</script>
|
||||
```
|
||||
|
||||
## Tidy Tree Layout Overview
|
||||
|
||||
tidy-tree: The bidirectional tidy tree layout
|
||||
|
||||
The bidirectional tidy tree layout algorithm creates two separate trees that grow horizontally in opposite directions from a central root node:
|
||||
Left tree: grows horizontally to the left (children alternate: 1st, 3rd, 5th...)
|
||||
Right tree: grows horizontally to the right (children alternate: 2nd, 4th, 6th...)
|
||||
|
||||
This creates a balanced, symmetric layout that is ideal for mindmaps, organizational charts, and other tree-based diagrams.
|
||||
|
||||
Layout Structure:
|
||||
[Child 3] ← [Child 1] ← [Root] → [Child 2] → [Child 4]
|
||||
↓ ↓ ↓ ↓
|
||||
[GrandChild] [GrandChild] [GrandChild] [GrandChild]
|
46
packages/mermaid-layout-tidy-tree/package.json
Normal file
46
packages/mermaid-layout-tidy-tree/package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@mermaid-js/layout-tidy-tree",
|
||||
"version": "0.1.0",
|
||||
"description": "Tidy-tree layout engine for mermaid",
|
||||
"module": "dist/mermaid-layout-tidy-tree.core.mjs",
|
||||
"types": "dist/layouts.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/mermaid-layout-tidy-tree.core.mjs",
|
||||
"types": "./dist/layouts.d.ts"
|
||||
},
|
||||
"./": "./"
|
||||
},
|
||||
"keywords": [
|
||||
"diagram",
|
||||
"markdown",
|
||||
"tidy-tree",
|
||||
"mermaid",
|
||||
"layout"
|
||||
],
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mermaid-js/mermaid"
|
||||
},
|
||||
"contributors": [
|
||||
"Knut Sveidqvist",
|
||||
"Sidharth Vinod"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"d3": "^7.9.0",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^7.4.3",
|
||||
"mermaid": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mermaid": "^11.0.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
50
packages/mermaid-layout-tidy-tree/src/index.ts
Normal file
50
packages/mermaid-layout-tidy-tree/src/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Bidirectional Tidy-Tree Layout Algorithm for Generic Diagrams
|
||||
*
|
||||
* This module provides a layout algorithm implementation using the
|
||||
* non-layered-tidy-tree-layout algorithm for positioning nodes and edges
|
||||
* in tree structures with a bidirectional approach.
|
||||
*
|
||||
* The algorithm creates two separate trees that grow horizontally in opposite
|
||||
* directions from a central root node:
|
||||
* - Left tree: grows horizontally to the left (children alternate: 1st, 3rd, 5th...)
|
||||
* - Right tree: grows horizontally to the right (children alternate: 2nd, 4th, 6th...)
|
||||
*
|
||||
* This creates a balanced, symmetric layout that is ideal for mindmaps,
|
||||
* organizational charts, and other tree-based diagrams.
|
||||
*
|
||||
* The algorithm follows the unified rendering pattern and can be used
|
||||
* by any diagram type that provides compatible LayoutData.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render function for the bidirectional tidy-tree layout algorithm
|
||||
*
|
||||
* This function follows the unified rendering pattern used by all layout algorithms.
|
||||
* It takes LayoutData, inserts nodes into DOM, runs the bidirectional tidy-tree layout algorithm,
|
||||
* and renders the positioned elements to the SVG.
|
||||
*
|
||||
* Features:
|
||||
* - Alternates root children between left and right trees
|
||||
* - Left tree grows horizontally to the left (rotated 90° counterclockwise)
|
||||
* - Right tree grows horizontally to the right (rotated 90° clockwise)
|
||||
* - Uses tidy-tree algorithm for optimal spacing within each tree
|
||||
* - Creates symmetric, balanced layouts
|
||||
* - Maintains proper edge connections between all nodes
|
||||
*
|
||||
* Layout Structure:
|
||||
* ```
|
||||
* [Child 3] ← [Child 1] ← [Root] → [Child 2] → [Child 4]
|
||||
* ↓ ↓ ↓ ↓
|
||||
* [GrandChild] [GrandChild] [GrandChild] [GrandChild]
|
||||
* ```
|
||||
*
|
||||
* @param layoutData - Layout data containing nodes, edges, and configuration
|
||||
* @param svg - SVG element to render to
|
||||
* @param helpers - Internal helper functions for rendering
|
||||
* @param options - Rendering options
|
||||
*/
|
||||
export { default } from './layouts.js';
|
||||
export * from './types.js';
|
||||
export * from './layout.js';
|
||||
export { render } from './render.js';
|
410
packages/mermaid-layout-tidy-tree/src/layout.test.ts
Normal file
410
packages/mermaid-layout-tidy-tree/src/layout.test.ts
Normal file
@@ -0,0 +1,410 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
|
||||
// Mock non-layered-tidy-tree-layout
|
||||
vi.mock('non-layered-tidy-tree-layout', () => ({
|
||||
BoundingBox: vi.fn().mockImplementation(() => ({})),
|
||||
Layout: vi.fn().mockImplementation(() => ({
|
||||
layout: vi.fn().mockImplementation((treeData) => {
|
||||
const result = { ...treeData };
|
||||
|
||||
if (result.id?.toString().startsWith('virtual-root')) {
|
||||
result.x = 0;
|
||||
result.y = 0;
|
||||
} else {
|
||||
result.x = 100;
|
||||
result.y = 50;
|
||||
}
|
||||
|
||||
if (result.children) {
|
||||
result.children.forEach((child: any, index: number) => {
|
||||
child.x = 50 + index * 100;
|
||||
child.y = 100;
|
||||
|
||||
if (child.children) {
|
||||
child.children.forEach((grandchild: any, gIndex: number) => {
|
||||
grandchild.x = 25 + gIndex * 50;
|
||||
grandchild.y = 200;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
result,
|
||||
boundingBox: {
|
||||
left: 0,
|
||||
right: 200,
|
||||
top: 0,
|
||||
bottom: 250,
|
||||
},
|
||||
};
|
||||
}),
|
||||
})),
|
||||
}));
|
||||
|
||||
import { executeTidyTreeLayout, validateLayoutData } from './layout.js';
|
||||
import type { LayoutResult } from './types.js';
|
||||
import type { LayoutData, MermaidConfig } from 'mermaid';
|
||||
|
||||
describe('Tidy-Tree Layout Algorithm', () => {
|
||||
let mockConfig: MermaidConfig;
|
||||
let mockLayoutData: LayoutData;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig = {
|
||||
theme: 'default',
|
||||
} as MermaidConfig;
|
||||
|
||||
mockLayoutData = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'root',
|
||||
label: 'Root',
|
||||
isGroup: false,
|
||||
shape: 'rect',
|
||||
width: 100,
|
||||
height: 50,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
{
|
||||
id: 'child1',
|
||||
label: 'Child 1',
|
||||
isGroup: false,
|
||||
shape: 'rect',
|
||||
width: 80,
|
||||
height: 40,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
{
|
||||
id: 'child2',
|
||||
label: 'Child 2',
|
||||
isGroup: false,
|
||||
shape: 'rect',
|
||||
width: 80,
|
||||
height: 40,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
{
|
||||
id: 'child3',
|
||||
label: 'Child 3',
|
||||
isGroup: false,
|
||||
shape: 'rect',
|
||||
width: 80,
|
||||
height: 40,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
{
|
||||
id: 'child4',
|
||||
label: 'Child 4',
|
||||
isGroup: false,
|
||||
shape: 'rect',
|
||||
width: 80,
|
||||
height: 40,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'root_child1',
|
||||
start: 'root',
|
||||
end: 'child1',
|
||||
type: 'edge',
|
||||
classes: '',
|
||||
style: [],
|
||||
animate: false,
|
||||
arrowTypeEnd: 'arrow_point',
|
||||
arrowTypeStart: 'none',
|
||||
},
|
||||
{
|
||||
id: 'root_child2',
|
||||
start: 'root',
|
||||
end: 'child2',
|
||||
type: 'edge',
|
||||
classes: '',
|
||||
style: [],
|
||||
animate: false,
|
||||
arrowTypeEnd: 'arrow_point',
|
||||
arrowTypeStart: 'none',
|
||||
},
|
||||
{
|
||||
id: 'root_child3',
|
||||
start: 'root',
|
||||
end: 'child3',
|
||||
type: 'edge',
|
||||
classes: '',
|
||||
style: [],
|
||||
animate: false,
|
||||
arrowTypeEnd: 'arrow_point',
|
||||
arrowTypeStart: 'none',
|
||||
},
|
||||
{
|
||||
id: 'root_child4',
|
||||
start: 'root',
|
||||
end: 'child4',
|
||||
type: 'edge',
|
||||
classes: '',
|
||||
style: [],
|
||||
animate: false,
|
||||
arrowTypeEnd: 'arrow_point',
|
||||
arrowTypeStart: 'none',
|
||||
},
|
||||
],
|
||||
config: mockConfig,
|
||||
direction: 'TB',
|
||||
type: 'test',
|
||||
diagramId: 'test-diagram',
|
||||
markers: [],
|
||||
};
|
||||
});
|
||||
|
||||
describe('validateLayoutData', () => {
|
||||
it('should validate correct layout data', () => {
|
||||
expect(() => validateLayoutData(mockLayoutData)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw error for missing data', () => {
|
||||
expect(() => validateLayoutData(null as any)).toThrow('Layout data is required');
|
||||
});
|
||||
|
||||
it('should throw error for missing config', () => {
|
||||
const invalidData = { ...mockLayoutData, config: null as any };
|
||||
expect(() => validateLayoutData(invalidData)).toThrow('Configuration is required');
|
||||
});
|
||||
|
||||
it('should throw error for invalid nodes array', () => {
|
||||
const invalidData = { ...mockLayoutData, nodes: null as any };
|
||||
expect(() => validateLayoutData(invalidData)).toThrow('Nodes array is required');
|
||||
});
|
||||
|
||||
it('should throw error for invalid edges array', () => {
|
||||
const invalidData = { ...mockLayoutData, edges: null as any };
|
||||
expect(() => validateLayoutData(invalidData)).toThrow('Edges array is required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeTidyTreeLayout function', () => {
|
||||
it('should execute layout algorithm successfully', async () => {
|
||||
const result: LayoutResult = await executeTidyTreeLayout(mockLayoutData, mockConfig);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.nodes).toBeDefined();
|
||||
expect(result.edges).toBeDefined();
|
||||
expect(Array.isArray(result.nodes)).toBe(true);
|
||||
expect(Array.isArray(result.edges)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return positioned nodes with coordinates', async () => {
|
||||
const result: LayoutResult = await executeTidyTreeLayout(mockLayoutData, mockConfig);
|
||||
|
||||
expect(result.nodes.length).toBeGreaterThan(0);
|
||||
result.nodes.forEach((node) => {
|
||||
expect(node.x).toBeDefined();
|
||||
expect(node.y).toBeDefined();
|
||||
expect(typeof node.x).toBe('number');
|
||||
expect(typeof node.y).toBe('number');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return positioned edges with coordinates', async () => {
|
||||
const result: LayoutResult = await executeTidyTreeLayout(mockLayoutData, mockConfig);
|
||||
|
||||
expect(result.edges.length).toBeGreaterThan(0);
|
||||
result.edges.forEach((edge) => {
|
||||
expect(edge.startX).toBeDefined();
|
||||
expect(edge.startY).toBeDefined();
|
||||
expect(edge.midX).toBeDefined();
|
||||
expect(edge.midY).toBeDefined();
|
||||
expect(edge.endX).toBeDefined();
|
||||
expect(edge.endY).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty layout data gracefully', async () => {
|
||||
const emptyData: LayoutData = {
|
||||
...mockLayoutData,
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
await expect(executeTidyTreeLayout(emptyData, mockConfig)).rejects.toThrow(
|
||||
'No nodes found in layout data'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error for missing nodes', async () => {
|
||||
const invalidData = { ...mockLayoutData, nodes: [] };
|
||||
|
||||
await expect(executeTidyTreeLayout(invalidData, mockConfig)).rejects.toThrow(
|
||||
'No nodes found in layout data'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty edges (single node tree)', async () => {
|
||||
const singleNodeData = {
|
||||
...mockLayoutData,
|
||||
edges: [],
|
||||
nodes: [mockLayoutData.nodes[0]],
|
||||
};
|
||||
|
||||
const result = await executeTidyTreeLayout(singleNodeData, mockConfig);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.nodes).toHaveLength(1);
|
||||
expect(result.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should create bidirectional dual-tree layout with alternating left/right children', async () => {
|
||||
const result = await executeTidyTreeLayout(mockLayoutData, mockConfig);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.nodes).toHaveLength(5);
|
||||
|
||||
const rootNode = result.nodes.find((node) => node.id === 'root');
|
||||
expect(rootNode).toBeDefined();
|
||||
expect(rootNode!.x).toBe(0);
|
||||
expect(rootNode!.y).toBe(20);
|
||||
|
||||
const child1 = result.nodes.find((node) => node.id === 'child1');
|
||||
const child2 = result.nodes.find((node) => node.id === 'child2');
|
||||
const child3 = result.nodes.find((node) => node.id === 'child3');
|
||||
const child4 = result.nodes.find((node) => node.id === 'child4');
|
||||
|
||||
expect(child1).toBeDefined();
|
||||
expect(child2).toBeDefined();
|
||||
expect(child3).toBeDefined();
|
||||
expect(child4).toBeDefined();
|
||||
|
||||
expect(child1!.x).toBeLessThan(rootNode!.x);
|
||||
expect(child2!.x).toBeGreaterThan(rootNode!.x);
|
||||
expect(child3!.x).toBeLessThan(rootNode!.x);
|
||||
expect(child4!.x).toBeGreaterThan(rootNode!.x);
|
||||
|
||||
expect(child1!.x).toBeLessThan(-100);
|
||||
expect(child3!.x).toBeLessThan(-100);
|
||||
|
||||
expect(child2!.x).toBeGreaterThan(100);
|
||||
expect(child4!.x).toBeGreaterThan(100);
|
||||
});
|
||||
|
||||
it('should correctly transpose coordinates to prevent high nodes from covering nodes above them', async () => {
|
||||
const testData = {
|
||||
...mockLayoutData,
|
||||
nodes: [
|
||||
{
|
||||
id: 'root',
|
||||
label: 'Root',
|
||||
isGroup: false,
|
||||
shape: 'rect' as const,
|
||||
width: 100,
|
||||
height: 50,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
{
|
||||
id: 'tall-child',
|
||||
label: 'Tall Child',
|
||||
isGroup: false,
|
||||
shape: 'rect' as const,
|
||||
width: 80,
|
||||
height: 120,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
{
|
||||
id: 'short-child',
|
||||
label: 'Short Child',
|
||||
isGroup: false,
|
||||
shape: 'rect' as const,
|
||||
width: 80,
|
||||
height: 30,
|
||||
padding: 10,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cssClasses: '',
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'root_tall',
|
||||
start: 'root',
|
||||
end: 'tall-child',
|
||||
type: 'edge',
|
||||
classes: '',
|
||||
style: [],
|
||||
animate: false,
|
||||
arrowTypeEnd: 'arrow_point',
|
||||
arrowTypeStart: 'none',
|
||||
},
|
||||
{
|
||||
id: 'root_short',
|
||||
start: 'root',
|
||||
end: 'short-child',
|
||||
type: 'edge',
|
||||
classes: '',
|
||||
style: [],
|
||||
animate: false,
|
||||
arrowTypeEnd: 'arrow_point',
|
||||
arrowTypeStart: 'none',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await executeTidyTreeLayout(testData, mockConfig);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.nodes).toHaveLength(3);
|
||||
|
||||
const rootNode = result.nodes.find((node) => node.id === 'root');
|
||||
const tallChild = result.nodes.find((node) => node.id === 'tall-child');
|
||||
const shortChild = result.nodes.find((node) => node.id === 'short-child');
|
||||
|
||||
expect(rootNode).toBeDefined();
|
||||
expect(tallChild).toBeDefined();
|
||||
expect(shortChild).toBeDefined();
|
||||
|
||||
expect(tallChild!.x).not.toBe(shortChild!.x);
|
||||
|
||||
expect(tallChild!.width).toBe(80);
|
||||
expect(tallChild!.height).toBe(120);
|
||||
expect(shortChild!.width).toBe(80);
|
||||
expect(shortChild!.height).toBe(30);
|
||||
|
||||
const yDifference = Math.abs(tallChild!.y - shortChild!.y);
|
||||
expect(yDifference).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
653
packages/mermaid-layout-tidy-tree/src/layout.ts
Normal file
653
packages/mermaid-layout-tidy-tree/src/layout.ts
Normal file
@@ -0,0 +1,653 @@
|
||||
import { BoundingBox, Layout } from 'non-layered-tidy-tree-layout';
|
||||
import type { MermaidConfig, LayoutData } from 'mermaid';
|
||||
import type {
|
||||
LayoutResult,
|
||||
TidyTreeNode,
|
||||
PositionedNode,
|
||||
PositionedEdge,
|
||||
Node,
|
||||
Edge,
|
||||
} from './types.js';
|
||||
|
||||
/**
|
||||
* Execute the tidy-tree layout algorithm on generic layout data
|
||||
*
|
||||
* This function takes layout data and uses the non-layered-tidy-tree-layout
|
||||
* algorithm to calculate optimal node positions for tree structures.
|
||||
*
|
||||
* @param data - The layout data containing nodes, edges, and configuration
|
||||
* @param config - Mermaid configuration object
|
||||
* @returns Promise resolving to layout result with positioned nodes and edges
|
||||
*/
|
||||
export function executeTidyTreeLayout(
|
||||
data: LayoutData,
|
||||
_config: MermaidConfig
|
||||
): Promise<LayoutResult> {
|
||||
let intersectionShift = 50;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!data.nodes || !Array.isArray(data.nodes) || data.nodes.length === 0) {
|
||||
throw new Error('No nodes found in layout data');
|
||||
}
|
||||
|
||||
if (!data.edges || !Array.isArray(data.edges)) {
|
||||
data.edges = [];
|
||||
}
|
||||
|
||||
const { leftTree, rightTree, rootNode } = convertToDualTreeFormat(data);
|
||||
|
||||
const gap = 20;
|
||||
const bottomPadding = 40;
|
||||
intersectionShift = 30;
|
||||
|
||||
const bb = new BoundingBox(gap, bottomPadding);
|
||||
const layout = new Layout(bb);
|
||||
|
||||
let leftResult = null;
|
||||
let rightResult = null;
|
||||
let leftBoundingBox = null;
|
||||
let rightBoundingBox = null;
|
||||
|
||||
if (leftTree) {
|
||||
const leftLayoutResult = layout.layout(leftTree);
|
||||
leftResult = leftLayoutResult.result;
|
||||
leftBoundingBox = leftLayoutResult.boundingBox;
|
||||
}
|
||||
|
||||
if (rightTree) {
|
||||
const rightLayoutResult = layout.layout(rightTree);
|
||||
rightResult = rightLayoutResult.result;
|
||||
rightBoundingBox = rightLayoutResult.boundingBox;
|
||||
}
|
||||
|
||||
const positionedNodes = combineAndPositionTrees(
|
||||
rootNode,
|
||||
leftResult,
|
||||
rightResult,
|
||||
leftBoundingBox,
|
||||
rightBoundingBox,
|
||||
data
|
||||
);
|
||||
const positionedEdges = calculateEdgePositions(
|
||||
data.edges,
|
||||
positionedNodes,
|
||||
intersectionShift
|
||||
);
|
||||
resolve({
|
||||
nodes: positionedNodes,
|
||||
edges: positionedEdges,
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert LayoutData to dual-tree format (left and right trees)
|
||||
*
|
||||
* This function builds two separate tree structures from the nodes and edges,
|
||||
* alternating children between left and right trees.
|
||||
*/
|
||||
function convertToDualTreeFormat(data: LayoutData): {
|
||||
leftTree: TidyTreeNode | null;
|
||||
rightTree: TidyTreeNode | null;
|
||||
rootNode: TidyTreeNode;
|
||||
} {
|
||||
const { nodes, edges } = data;
|
||||
|
||||
const nodeMap = new Map<string, Node>();
|
||||
nodes.forEach((node) => nodeMap.set(node.id, node));
|
||||
|
||||
const children = new Map<string, string[]>();
|
||||
const parents = new Map<string, string>();
|
||||
|
||||
edges.forEach((edge) => {
|
||||
const parentId = edge.start;
|
||||
const childId = edge.end;
|
||||
|
||||
if (parentId && childId) {
|
||||
if (!children.has(parentId)) {
|
||||
children.set(parentId, []);
|
||||
}
|
||||
children.get(parentId)!.push(childId);
|
||||
parents.set(childId, parentId);
|
||||
}
|
||||
});
|
||||
|
||||
const rootNodeData = nodes.find((node) => !parents.has(node.id));
|
||||
if (!rootNodeData && nodes.length === 0) {
|
||||
throw new Error('No nodes available to create tree');
|
||||
}
|
||||
|
||||
const actualRoot = rootNodeData ?? nodes[0];
|
||||
|
||||
const rootNode: TidyTreeNode = {
|
||||
id: actualRoot.id,
|
||||
width: actualRoot.width ?? 100,
|
||||
height: actualRoot.height ?? 50,
|
||||
_originalNode: actualRoot,
|
||||
};
|
||||
|
||||
const rootChildren = children.get(actualRoot.id) ?? [];
|
||||
const leftChildren: string[] = [];
|
||||
const rightChildren: string[] = [];
|
||||
|
||||
rootChildren.forEach((childId, index) => {
|
||||
if (index % 2 === 0) {
|
||||
leftChildren.push(childId);
|
||||
} else {
|
||||
rightChildren.push(childId);
|
||||
}
|
||||
});
|
||||
|
||||
const leftTree = leftChildren.length > 0 ? buildSubTree(leftChildren, children, nodeMap) : null;
|
||||
|
||||
const rightTree =
|
||||
rightChildren.length > 0 ? buildSubTree(rightChildren, children, nodeMap) : null;
|
||||
|
||||
return { leftTree, rightTree, rootNode };
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a subtree from a list of root children
|
||||
* For horizontal trees, we need to transpose width/height since the tree will be rotated 90°
|
||||
*/
|
||||
function buildSubTree(
|
||||
rootChildren: string[],
|
||||
children: Map<string, string[]>,
|
||||
nodeMap: Map<string, Node>
|
||||
): TidyTreeNode {
|
||||
const virtualRoot: TidyTreeNode = {
|
||||
id: `virtual-root-${Math.random()}`,
|
||||
width: 1,
|
||||
height: 1,
|
||||
children: rootChildren
|
||||
.map((childId) => nodeMap.get(childId))
|
||||
.filter((child): child is Node => child !== undefined)
|
||||
.map((child) => convertNodeToTidyTreeTransposed(child, children, nodeMap)),
|
||||
};
|
||||
|
||||
return virtualRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively convert a node and its children to tidy-tree format
|
||||
* This version transposes width/height for horizontal tree layout
|
||||
*/
|
||||
function convertNodeToTidyTreeTransposed(
|
||||
node: Node,
|
||||
children: Map<string, string[]>,
|
||||
nodeMap: Map<string, Node>
|
||||
): TidyTreeNode {
|
||||
const childIds = children.get(node.id) ?? [];
|
||||
const childNodes = childIds
|
||||
.map((childId) => nodeMap.get(childId))
|
||||
.filter((child): child is Node => child !== undefined)
|
||||
.map((child) => convertNodeToTidyTreeTransposed(child, children, nodeMap));
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
width: node.height ?? 50,
|
||||
height: node.width ?? 100,
|
||||
children: childNodes.length > 0 ? childNodes : undefined,
|
||||
_originalNode: node,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Combine and position the left and right trees around the root node
|
||||
* Creates a bidirectional layout where left tree grows left and right tree grows right
|
||||
*/
|
||||
function combineAndPositionTrees(
|
||||
rootNode: TidyTreeNode,
|
||||
leftResult: TidyTreeNode | null,
|
||||
rightResult: TidyTreeNode | null,
|
||||
_leftBoundingBox: any,
|
||||
_rightBoundingBox: any,
|
||||
_data: LayoutData
|
||||
): PositionedNode[] {
|
||||
const positionedNodes: PositionedNode[] = [];
|
||||
|
||||
const rootX = 0;
|
||||
const rootY = 0;
|
||||
|
||||
const treeSpacing = rootNode.width / 2 + 30;
|
||||
const leftTreeNodes: PositionedNode[] = [];
|
||||
const rightTreeNodes: PositionedNode[] = [];
|
||||
|
||||
if (leftResult?.children) {
|
||||
positionLeftTreeBidirectional(leftResult.children, leftTreeNodes, rootX - treeSpacing, rootY);
|
||||
}
|
||||
|
||||
if (rightResult?.children) {
|
||||
positionRightTreeBidirectional(
|
||||
rightResult.children,
|
||||
rightTreeNodes,
|
||||
rootX + treeSpacing,
|
||||
rootY
|
||||
);
|
||||
}
|
||||
|
||||
let leftTreeCenterY = 0;
|
||||
let rightTreeCenterY = 0;
|
||||
|
||||
if (leftTreeNodes.length > 0) {
|
||||
const leftTreeXPositions = [...new Set(leftTreeNodes.map((node) => node.x))].sort(
|
||||
(a, b) => b - a
|
||||
);
|
||||
const firstLevelLeftX = leftTreeXPositions[0];
|
||||
const firstLevelLeftNodes = leftTreeNodes.filter((node) => node.x === firstLevelLeftX);
|
||||
|
||||
if (firstLevelLeftNodes.length > 0) {
|
||||
const leftMinY = Math.min(
|
||||
...firstLevelLeftNodes.map((node) => node.y - (node.height ?? 50) / 2)
|
||||
);
|
||||
const leftMaxY = Math.max(
|
||||
...firstLevelLeftNodes.map((node) => node.y + (node.height ?? 50) / 2)
|
||||
);
|
||||
leftTreeCenterY = (leftMinY + leftMaxY) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (rightTreeNodes.length > 0) {
|
||||
const rightTreeXPositions = [...new Set(rightTreeNodes.map((node) => node.x))].sort(
|
||||
(a, b) => a - b
|
||||
);
|
||||
const firstLevelRightX = rightTreeXPositions[0];
|
||||
const firstLevelRightNodes = rightTreeNodes.filter((node) => node.x === firstLevelRightX);
|
||||
|
||||
if (firstLevelRightNodes.length > 0) {
|
||||
const rightMinY = Math.min(
|
||||
...firstLevelRightNodes.map((node) => node.y - (node.height ?? 50) / 2)
|
||||
);
|
||||
const rightMaxY = Math.max(
|
||||
...firstLevelRightNodes.map((node) => node.y + (node.height ?? 50) / 2)
|
||||
);
|
||||
rightTreeCenterY = (rightMinY + rightMaxY) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
const leftTreeOffset = -leftTreeCenterY;
|
||||
const rightTreeOffset = -rightTreeCenterY;
|
||||
|
||||
positionedNodes.push({
|
||||
id: String(rootNode.id),
|
||||
x: rootX,
|
||||
y: rootY + 20,
|
||||
section: 'root',
|
||||
width: rootNode._originalNode?.width ?? rootNode.width,
|
||||
height: rootNode._originalNode?.height ?? rootNode.height,
|
||||
originalNode: rootNode._originalNode,
|
||||
});
|
||||
|
||||
const leftTreeNodesWithOffset = leftTreeNodes.map((node) => ({
|
||||
id: node.id,
|
||||
x: node.x - (node.width ?? 0) / 2,
|
||||
y: node.y + leftTreeOffset + (node.height ?? 0) / 2,
|
||||
section: 'left' as const,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
originalNode: node.originalNode,
|
||||
}));
|
||||
|
||||
const rightTreeNodesWithOffset = rightTreeNodes.map((node) => ({
|
||||
id: node.id,
|
||||
x: node.x + (node.width ?? 0) / 2,
|
||||
y: node.y + rightTreeOffset + (node.height ?? 0) / 2,
|
||||
section: 'right' as const,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
originalNode: node.originalNode,
|
||||
}));
|
||||
|
||||
positionedNodes.push(...leftTreeNodesWithOffset);
|
||||
positionedNodes.push(...rightTreeNodesWithOffset);
|
||||
|
||||
return positionedNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position nodes from the left tree in a bidirectional layout (grows to the left)
|
||||
* Rotates the tree 90 degrees counterclockwise so it grows horizontally to the left
|
||||
*/
|
||||
function positionLeftTreeBidirectional(
|
||||
nodes: TidyTreeNode[],
|
||||
positionedNodes: PositionedNode[],
|
||||
offsetX: number,
|
||||
offsetY: number
|
||||
): void {
|
||||
nodes.forEach((node) => {
|
||||
const distanceFromRoot = node.y ?? 0;
|
||||
const verticalPosition = node.x ?? 0;
|
||||
|
||||
const originalWidth = node._originalNode?.width ?? 100;
|
||||
const originalHeight = node._originalNode?.height ?? 50;
|
||||
|
||||
const adjustedY = offsetY + verticalPosition;
|
||||
|
||||
positionedNodes.push({
|
||||
id: String(node.id),
|
||||
x: offsetX - distanceFromRoot,
|
||||
y: adjustedY,
|
||||
width: originalWidth,
|
||||
height: originalHeight,
|
||||
originalNode: node._originalNode,
|
||||
});
|
||||
|
||||
if (node.children) {
|
||||
positionLeftTreeBidirectional(node.children, positionedNodes, offsetX, offsetY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Position nodes from the right tree in a bidirectional layout (grows to the right)
|
||||
* Rotates the tree 90 degrees clockwise so it grows horizontally to the right
|
||||
*/
|
||||
function positionRightTreeBidirectional(
|
||||
nodes: TidyTreeNode[],
|
||||
positionedNodes: PositionedNode[],
|
||||
offsetX: number,
|
||||
offsetY: number
|
||||
): void {
|
||||
nodes.forEach((node) => {
|
||||
const distanceFromRoot = node.y ?? 0;
|
||||
const verticalPosition = node.x ?? 0;
|
||||
|
||||
const originalWidth = node._originalNode?.width ?? 100;
|
||||
const originalHeight = node._originalNode?.height ?? 50;
|
||||
|
||||
const adjustedY = offsetY + verticalPosition;
|
||||
|
||||
positionedNodes.push({
|
||||
id: String(node.id),
|
||||
x: offsetX + distanceFromRoot,
|
||||
y: adjustedY,
|
||||
width: originalWidth,
|
||||
height: originalHeight,
|
||||
originalNode: node._originalNode,
|
||||
});
|
||||
|
||||
if (node.children) {
|
||||
positionRightTreeBidirectional(node.children, positionedNodes, offsetX, offsetY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the intersection point of a line with a circle
|
||||
* @param circle - Circle coordinates and radius
|
||||
* @param lineStart - Starting point of the line
|
||||
* @param lineEnd - Ending point of the line
|
||||
* @returns The intersection point
|
||||
*/
|
||||
function computeCircleEdgeIntersection(
|
||||
circle: { x: number; y: number; width: number; height: number },
|
||||
lineStart: { x: number; y: number },
|
||||
lineEnd: { x: number; y: number }
|
||||
): { x: number; y: number } {
|
||||
const radius = Math.min(circle.width, circle.height) / 2;
|
||||
|
||||
const dx = lineEnd.x - lineStart.x;
|
||||
const dy = lineEnd.y - lineStart.y;
|
||||
const length = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (length === 0) {
|
||||
return lineStart;
|
||||
}
|
||||
|
||||
const nx = dx / length;
|
||||
const ny = dy / length;
|
||||
|
||||
return {
|
||||
x: circle.x - nx * radius,
|
||||
y: circle.y - ny * radius,
|
||||
};
|
||||
}
|
||||
|
||||
function intersection(
|
||||
node: { x: number; y: number; width?: number; height?: number },
|
||||
outsidePoint: { x: number; y: number },
|
||||
insidePoint: { x: number; y: number }
|
||||
): { x: number; y: number } {
|
||||
const x = node.x;
|
||||
const y = node.y;
|
||||
|
||||
if (!node.width || !node.height) {
|
||||
return { x: outsidePoint.x, y: outsidePoint.y };
|
||||
}
|
||||
const dx = Math.abs(x - insidePoint.x);
|
||||
const w = node?.width / 2;
|
||||
let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
|
||||
const h = node.height / 2;
|
||||
|
||||
const Q = Math.abs(outsidePoint.y - insidePoint.y);
|
||||
const R = Math.abs(outsidePoint.x - insidePoint.x);
|
||||
|
||||
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {
|
||||
// Intersection is top or bottom of rect.
|
||||
const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
|
||||
r = (R * q) / Q;
|
||||
const res = {
|
||||
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,
|
||||
y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,
|
||||
};
|
||||
|
||||
if (r === 0) {
|
||||
res.x = outsidePoint.x;
|
||||
res.y = outsidePoint.y;
|
||||
}
|
||||
if (R === 0) {
|
||||
res.x = outsidePoint.x;
|
||||
}
|
||||
if (Q === 0) {
|
||||
res.y = outsidePoint.y;
|
||||
}
|
||||
|
||||
return res;
|
||||
} else {
|
||||
if (insidePoint.x < outsidePoint.x) {
|
||||
r = outsidePoint.x - w - x;
|
||||
} else {
|
||||
r = x - w - outsidePoint.x;
|
||||
}
|
||||
const q = (Q * r) / R;
|
||||
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
||||
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
||||
|
||||
if (r === 0) {
|
||||
_x = outsidePoint.x;
|
||||
_y = outsidePoint.y;
|
||||
}
|
||||
if (R === 0) {
|
||||
_x = outsidePoint.x;
|
||||
}
|
||||
if (Q === 0) {
|
||||
_y = outsidePoint.y;
|
||||
}
|
||||
|
||||
return { x: _x, y: _y };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate edge positions based on positioned nodes
|
||||
* Now includes tree membership and node dimensions for precise edge calculations
|
||||
* Edges now stop at shape boundaries instead of extending to centers
|
||||
*/
|
||||
function calculateEdgePositions(
|
||||
edges: Edge[],
|
||||
positionedNodes: PositionedNode[],
|
||||
intersectionShift: number
|
||||
): PositionedEdge[] {
|
||||
const nodeInfo = new Map<string, PositionedNode>();
|
||||
positionedNodes.forEach((node) => {
|
||||
nodeInfo.set(node.id, node);
|
||||
});
|
||||
|
||||
return edges.map((edge) => {
|
||||
const sourceNode = nodeInfo.get(edge.start ?? '');
|
||||
const targetNode = nodeInfo.get(edge.end ?? '');
|
||||
|
||||
if (!sourceNode || !targetNode) {
|
||||
return {
|
||||
id: edge.id,
|
||||
source: edge.start ?? '',
|
||||
target: edge.end ?? '',
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
midX: 0,
|
||||
midY: 0,
|
||||
endX: 0,
|
||||
endY: 0,
|
||||
points: [{ x: 0, y: 0 }],
|
||||
sourceSection: undefined,
|
||||
targetSection: undefined,
|
||||
sourceWidth: undefined,
|
||||
sourceHeight: undefined,
|
||||
targetWidth: undefined,
|
||||
targetHeight: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const sourceCenter = { x: sourceNode.x, y: sourceNode.y };
|
||||
const targetCenter = { x: targetNode.x, y: targetNode.y };
|
||||
|
||||
const isSourceRound = ['circle', 'cloud', 'bang'].includes(
|
||||
sourceNode.originalNode?.shape ?? ''
|
||||
);
|
||||
const isTargetRound = ['circle', 'cloud', 'bang'].includes(
|
||||
targetNode.originalNode?.shape ?? ''
|
||||
);
|
||||
|
||||
let startPos = isSourceRound
|
||||
? computeCircleEdgeIntersection(
|
||||
{
|
||||
x: sourceNode.x,
|
||||
y: sourceNode.y,
|
||||
width: sourceNode.width ?? 100,
|
||||
height: sourceNode.height ?? 100,
|
||||
},
|
||||
targetCenter,
|
||||
sourceCenter
|
||||
)
|
||||
: intersection(sourceNode, sourceCenter, targetCenter);
|
||||
|
||||
let endPos = isTargetRound
|
||||
? computeCircleEdgeIntersection(
|
||||
{
|
||||
x: targetNode.x,
|
||||
y: targetNode.y,
|
||||
width: targetNode.width ?? 100,
|
||||
height: targetNode.height ?? 100,
|
||||
},
|
||||
sourceCenter,
|
||||
targetCenter
|
||||
)
|
||||
: intersection(targetNode, targetCenter, sourceCenter);
|
||||
|
||||
const midX = (startPos.x + endPos.x) / 2;
|
||||
const midY = (startPos.y + endPos.y) / 2;
|
||||
|
||||
const points = [startPos];
|
||||
if (sourceNode.section === 'left') {
|
||||
points.push({
|
||||
x: sourceNode.x - (sourceNode.width ?? 0) / 2 - intersectionShift,
|
||||
y: sourceNode.y,
|
||||
});
|
||||
} else if (sourceNode.section === 'right') {
|
||||
points.push({
|
||||
x: sourceNode.x + (sourceNode.width ?? 0) / 2 + intersectionShift,
|
||||
y: sourceNode.y,
|
||||
});
|
||||
}
|
||||
if (targetNode.section === 'left') {
|
||||
points.push({
|
||||
x: targetNode.x + (targetNode.width ?? 0) / 2 + intersectionShift,
|
||||
y: targetNode.y,
|
||||
});
|
||||
} else if (targetNode.section === 'right') {
|
||||
points.push({
|
||||
x: targetNode.x - (targetNode.width ?? 0) / 2 - intersectionShift,
|
||||
y: targetNode.y,
|
||||
});
|
||||
}
|
||||
|
||||
points.push(endPos);
|
||||
|
||||
const secondPoint = points.length > 1 ? points[1] : targetCenter;
|
||||
startPos = isSourceRound
|
||||
? computeCircleEdgeIntersection(
|
||||
{
|
||||
x: sourceNode.x,
|
||||
y: sourceNode.y,
|
||||
width: sourceNode.width ?? 100,
|
||||
height: sourceNode.height ?? 100,
|
||||
},
|
||||
secondPoint,
|
||||
sourceCenter
|
||||
)
|
||||
: intersection(sourceNode, secondPoint, sourceCenter);
|
||||
points[0] = startPos;
|
||||
|
||||
const secondLastPoint = points.length > 1 ? points[points.length - 2] : sourceCenter;
|
||||
endPos = isTargetRound
|
||||
? computeCircleEdgeIntersection(
|
||||
{
|
||||
x: targetNode.x,
|
||||
y: targetNode.y,
|
||||
width: targetNode.width ?? 100,
|
||||
height: targetNode.height ?? 100,
|
||||
},
|
||||
secondLastPoint,
|
||||
targetCenter
|
||||
)
|
||||
: intersection(targetNode, secondLastPoint, targetCenter);
|
||||
points[points.length - 1] = endPos;
|
||||
|
||||
return {
|
||||
id: edge.id,
|
||||
source: edge.start ?? '',
|
||||
target: edge.end ?? '',
|
||||
startX: startPos.x,
|
||||
startY: startPos.y,
|
||||
midX,
|
||||
midY,
|
||||
endX: endPos.x,
|
||||
endY: endPos.y,
|
||||
points,
|
||||
sourceSection: sourceNode?.section,
|
||||
targetSection: targetNode?.section,
|
||||
sourceWidth: sourceNode?.width,
|
||||
sourceHeight: sourceNode?.height,
|
||||
targetWidth: targetNode?.width,
|
||||
targetHeight: targetNode?.height,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate layout data structure
|
||||
* @param data - The data to validate
|
||||
* @returns True if data is valid, throws error otherwise
|
||||
*/
|
||||
export function validateLayoutData(data: LayoutData): boolean {
|
||||
if (!data) {
|
||||
throw new Error('Layout data is required');
|
||||
}
|
||||
|
||||
if (!data.config) {
|
||||
throw new Error('Configuration is required in layout data');
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.nodes)) {
|
||||
throw new Error('Nodes array is required in layout data');
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.edges)) {
|
||||
throw new Error('Edges array is required in layout data');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
13
packages/mermaid-layout-tidy-tree/src/layouts.ts
Normal file
13
packages/mermaid-layout-tidy-tree/src/layouts.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { LayoutLoaderDefinition } from 'mermaid';
|
||||
|
||||
const loader = async () => await import(`./render.js`);
|
||||
|
||||
const tidyTreeLayout: LayoutLoaderDefinition[] = [
|
||||
{
|
||||
name: 'tidy-tree',
|
||||
loader,
|
||||
algorithm: 'tidy-tree',
|
||||
},
|
||||
];
|
||||
|
||||
export default tidyTreeLayout;
|
18
packages/mermaid-layout-tidy-tree/src/non-layered-tidy-tree-layout.d.ts
vendored
Normal file
18
packages/mermaid-layout-tidy-tree/src/non-layered-tidy-tree-layout.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
declare module 'non-layered-tidy-tree-layout' {
|
||||
export class BoundingBox {
|
||||
constructor(gap: number, bottomPadding: number);
|
||||
}
|
||||
|
||||
export class Layout {
|
||||
constructor(boundingBox: BoundingBox);
|
||||
layout(data: any): {
|
||||
result: any;
|
||||
boundingBox: {
|
||||
left: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
180
packages/mermaid-layout-tidy-tree/src/render.ts
Normal file
180
packages/mermaid-layout-tidy-tree/src/render.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import type { InternalHelpers, LayoutData, RenderOptions, SVG } from 'mermaid';
|
||||
import { executeTidyTreeLayout } from './layout.js';
|
||||
|
||||
interface NodeWithPosition {
|
||||
id: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
domId?: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render function for bidirectional tidy-tree layout algorithm
|
||||
*
|
||||
* This follows the same pattern as ELK and dagre renderers:
|
||||
* 1. Insert nodes into DOM to get their actual dimensions
|
||||
* 2. Run the bidirectional tidy-tree layout algorithm to calculate positions
|
||||
* 3. Position the nodes and edges based on layout results
|
||||
*
|
||||
* The bidirectional layout creates two trees that grow horizontally in opposite
|
||||
* directions from a central root node:
|
||||
* - Left tree: grows horizontally to the left (children: 1st, 3rd, 5th...)
|
||||
* - Right tree: grows horizontally to the right (children: 2nd, 4th, 6th...)
|
||||
*/
|
||||
export const render = async (
|
||||
data4Layout: LayoutData,
|
||||
svg: SVG,
|
||||
{
|
||||
insertCluster,
|
||||
insertEdge,
|
||||
insertEdgeLabel,
|
||||
insertMarkers,
|
||||
insertNode,
|
||||
log,
|
||||
positionEdgeLabel,
|
||||
}: InternalHelpers,
|
||||
{ algorithm: _algorithm }: RenderOptions
|
||||
) => {
|
||||
const nodeDb: Record<string, NodeWithPosition> = {};
|
||||
const clusterDb: Record<string, any> = {};
|
||||
|
||||
const element = svg.select('g');
|
||||
insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);
|
||||
|
||||
const subGraphsEl = element.insert('g').attr('class', 'subgraphs');
|
||||
const edgePaths = element.insert('g').attr('class', 'edgePaths');
|
||||
const edgeLabels = element.insert('g').attr('class', 'edgeLabels');
|
||||
const nodes = element.insert('g').attr('class', 'nodes');
|
||||
// Step 1: Insert nodes into DOM to get their actual dimensions
|
||||
log.debug('Inserting nodes into DOM for dimension calculation');
|
||||
|
||||
await Promise.all(
|
||||
data4Layout.nodes.map(async (node) => {
|
||||
if (node.isGroup) {
|
||||
const clusterNode: NodeWithPosition = {
|
||||
...node,
|
||||
id: node.id,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
};
|
||||
clusterDb[node.id] = clusterNode;
|
||||
nodeDb[node.id] = clusterNode;
|
||||
|
||||
await insertCluster(subGraphsEl, node);
|
||||
} else {
|
||||
const nodeWithPosition: NodeWithPosition = {
|
||||
...node,
|
||||
id: node.id,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
};
|
||||
nodeDb[node.id] = nodeWithPosition;
|
||||
|
||||
const nodeEl = await insertNode(nodes, node, {
|
||||
config: data4Layout.config,
|
||||
dir: data4Layout.direction || 'TB',
|
||||
});
|
||||
|
||||
const boundingBox = nodeEl.node()!.getBBox();
|
||||
nodeWithPosition.width = boundingBox.width;
|
||||
nodeWithPosition.height = boundingBox.height;
|
||||
nodeWithPosition.domId = nodeEl;
|
||||
|
||||
log.debug(`Node ${node.id} dimensions: ${boundingBox.width}x${boundingBox.height}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
// Step 2: Run the bidirectional tidy-tree layout algorithm
|
||||
log.debug('Running bidirectional tidy-tree layout algorithm');
|
||||
|
||||
const updatedLayoutData = {
|
||||
...data4Layout,
|
||||
nodes: data4Layout.nodes.map((node) => {
|
||||
const nodeWithDimensions = nodeDb[node.id];
|
||||
return {
|
||||
...node,
|
||||
width: nodeWithDimensions.width ?? node.width ?? 100,
|
||||
height: nodeWithDimensions.height ?? node.height ?? 50,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
const layoutResult = await executeTidyTreeLayout(updatedLayoutData, data4Layout.config);
|
||||
// Step 3: Position the nodes based on bidirectional layout results
|
||||
log.debug('Positioning nodes based on bidirectional layout results');
|
||||
|
||||
layoutResult.nodes.forEach((positionedNode) => {
|
||||
const node = nodeDb[positionedNode.id];
|
||||
if (node?.domId) {
|
||||
// Position the node at the calculated coordinates from bidirectional layout
|
||||
// The layout algorithm has already calculated positions for:
|
||||
// - Root node at center (0, 0)
|
||||
// - Left tree nodes with negative x coordinates (growing left)
|
||||
// - Right tree nodes with positive x coordinates (growing right)
|
||||
node.domId.attr('transform', `translate(${positionedNode.x}, ${positionedNode.y})`);
|
||||
// Store the final position
|
||||
node.x = positionedNode.x;
|
||||
node.y = positionedNode.y;
|
||||
// Step 3: Position the nodes based on bidirectional layout results
|
||||
log.debug(`Positioned node ${node.id} at (${positionedNode.x}, ${positionedNode.y})`);
|
||||
}
|
||||
});
|
||||
|
||||
log.debug('Inserting and positioning edges');
|
||||
|
||||
await Promise.all(
|
||||
data4Layout.edges.map(async (edge) => {
|
||||
await insertEdgeLabel(edgeLabels, edge);
|
||||
|
||||
const startNode = nodeDb[edge.start ?? ''];
|
||||
const endNode = nodeDb[edge.end ?? ''];
|
||||
|
||||
if (startNode && endNode) {
|
||||
const positionedEdge = layoutResult.edges.find((e) => e.id === edge.id);
|
||||
|
||||
if (positionedEdge) {
|
||||
log.debug('APA01 positionedEdge', positionedEdge);
|
||||
const edgeWithPath = {
|
||||
...edge,
|
||||
points: positionedEdge.points,
|
||||
};
|
||||
const paths = insertEdge(
|
||||
edgePaths,
|
||||
edgeWithPath,
|
||||
clusterDb,
|
||||
data4Layout.type,
|
||||
startNode,
|
||||
endNode,
|
||||
data4Layout.diagramId
|
||||
);
|
||||
|
||||
positionEdgeLabel(edgeWithPath, paths);
|
||||
} else {
|
||||
const edgeWithPath = {
|
||||
...edge,
|
||||
points: [
|
||||
{ x: startNode.x ?? 0, y: startNode.y ?? 0 },
|
||||
{ x: endNode.x ?? 0, y: endNode.y ?? 0 },
|
||||
],
|
||||
};
|
||||
|
||||
const paths = insertEdge(
|
||||
edgePaths,
|
||||
edgeWithPath,
|
||||
clusterDb,
|
||||
data4Layout.type,
|
||||
startNode,
|
||||
endNode,
|
||||
data4Layout.diagramId
|
||||
);
|
||||
positionEdgeLabel(edgeWithPath, paths);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
log.debug('Bidirectional tidy-tree rendering completed');
|
||||
};
|
69
packages/mermaid-layout-tidy-tree/src/types.ts
Normal file
69
packages/mermaid-layout-tidy-tree/src/types.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import type { LayoutData } from 'mermaid';
|
||||
|
||||
export type Node = LayoutData['nodes'][number];
|
||||
export type Edge = LayoutData['edges'][number];
|
||||
|
||||
/**
|
||||
* Positioned node after layout calculation
|
||||
*/
|
||||
export interface PositionedNode {
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
section?: 'root' | 'left' | 'right';
|
||||
width?: number;
|
||||
height?: number;
|
||||
originalNode?: Node;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positioned edge after layout calculation
|
||||
*/
|
||||
export interface PositionedEdge {
|
||||
id: string;
|
||||
source: string;
|
||||
target: string;
|
||||
startX: number;
|
||||
startY: number;
|
||||
midX: number;
|
||||
midY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
sourceSection?: 'root' | 'left' | 'right';
|
||||
targetSection?: 'root' | 'left' | 'right';
|
||||
sourceWidth?: number;
|
||||
sourceHeight?: number;
|
||||
targetWidth?: number;
|
||||
targetHeight?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of layout algorithm execution
|
||||
*/
|
||||
export interface LayoutResult {
|
||||
nodes: PositionedNode[];
|
||||
edges: PositionedEdge[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tidy-tree node structure compatible with non-layered-tidy-tree-layout
|
||||
*/
|
||||
export interface TidyTreeNode {
|
||||
id: string | number;
|
||||
width: number;
|
||||
height: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
children?: TidyTreeNode[];
|
||||
_originalNode?: Node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tidy-tree layout configuration
|
||||
*/
|
||||
export interface TidyTreeLayoutConfig {
|
||||
gap: number;
|
||||
bottomPadding: number;
|
||||
}
|
10
packages/mermaid-layout-tidy-tree/tsconfig.json
Normal file
10
packages/mermaid-layout-tidy-tree/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"types": ["vitest/importMeta", "vitest/globals"]
|
||||
},
|
||||
"include": ["./src/**/*.ts", "./src/**/*.d.ts"],
|
||||
"typeRoots": ["./src/types"]
|
||||
}
|
@@ -1,14 +1,5 @@
|
||||
# @mermaid-js/mermaid-zenuml
|
||||
|
||||
## 0.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6798](https://github.com/mermaid-js/mermaid/pull/6798) [`3ffe961`](https://github.com/mermaid-js/mermaid/commit/3ffe9618aebc9ac96de6e3c826481f542f18c2a9) Thanks [@MrCoder](https://github.com/MrCoder)! - Fixed a critical bug that the ZenUML diagram is not rendered.
|
||||
|
||||
- Updated dependencies [[`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6), [`2c0931d`](https://github.com/mermaid-js/mermaid/commit/2c0931da46794b49d2523211e25f782900c34e94), [`33e08da`](https://github.com/mermaid-js/mermaid/commit/33e08daf175125295a06b1b80279437004a4e865), [`814b68b`](https://github.com/mermaid-js/mermaid/commit/814b68b4a94813f7c6b3d7fb4559532a7bab2652), [`fce7cab`](https://github.com/mermaid-js/mermaid/commit/fce7cabb71d68a20a66246fe23d066512126a412), [`fc07f0d`](https://github.com/mermaid-js/mermaid/commit/fc07f0d8abca49e4f887d7457b7b94fb07d1e3da), [`12e01bd`](https://github.com/mermaid-js/mermaid/commit/12e01bdb5cacf3569133979a5a4f1d8973e9aec1), [`01aaef3`](https://github.com/mermaid-js/mermaid/commit/01aaef39b4a1ec8bc5a0c6bfa3a20b712d67f4dc), [`daf8d8d`](https://github.com/mermaid-js/mermaid/commit/daf8d8d3befcd600618a629977b76463b38d0ad9), [`c36cd05`](https://github.com/mermaid-js/mermaid/commit/c36cd05c45ac3090181152b4dae41f8d7b569bd6), [`8bb29fc`](https://github.com/mermaid-js/mermaid/commit/8bb29fc879329ad109898e4025b4f4eba2ab0649), [`71b04f9`](https://github.com/mermaid-js/mermaid/commit/71b04f93b07f876df2b30656ef36036c1d0e4e4f), [`c99bce6`](https://github.com/mermaid-js/mermaid/commit/c99bce6bab4c7ce0b81b66d44f44853ce4aeb1c3), [`6cc1926`](https://github.com/mermaid-js/mermaid/commit/6cc192680a2531cab28f87a8061a53b786e010f3), [`9da6fb3`](https://github.com/mermaid-js/mermaid/commit/9da6fb39ae278401771943ac85d6d1b875f78cf1), [`e48b0ba`](https://github.com/mermaid-js/mermaid/commit/e48b0ba61dab7f95aa02da603b5b7d383b894932), [`4d62d59`](https://github.com/mermaid-js/mermaid/commit/4d62d5963238400270e9314c6e4d506f48147074), [`e9ce8cf`](https://github.com/mermaid-js/mermaid/commit/e9ce8cf4da9062d85098042044822100889bb0dd), [`9258b29`](https://github.com/mermaid-js/mermaid/commit/9258b2933bbe1ef41087345ffea3731673671c49), [`da90f67`](https://github.com/mermaid-js/mermaid/commit/da90f6760b6efb0da998bcb63b75eecc29e06c08), [`0133f1c`](https://github.com/mermaid-js/mermaid/commit/0133f1c0c5cff4fc4c8e0b99e9cf0b3d49dcbe71), [`895f9d4`](https://github.com/mermaid-js/mermaid/commit/895f9d43ff98ca05ebfba530789f677f31a011ff)]:
|
||||
- mermaid@11.10.0
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mermaid-js/mermaid-zenuml",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.1",
|
||||
"description": "MermaidJS plugin for ZenUML integration",
|
||||
"module": "dist/mermaid-zenuml.core.mjs",
|
||||
"types": "dist/detector.d.ts",
|
||||
|
@@ -1,72 +1,5 @@
|
||||
# mermaid
|
||||
|
||||
## 11.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#6744](https://github.com/mermaid-js/mermaid/pull/6744) [`daf8d8d`](https://github.com/mermaid-js/mermaid/commit/daf8d8d3befcd600618a629977b76463b38d0ad9) Thanks [@SpecularAura](https://github.com/SpecularAura)! - feat: Added support for per link curve styling in flowchart diagram using edge ids
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6857](https://github.com/mermaid-js/mermaid/pull/6857) [`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6) Thanks [@knsv](https://github.com/knsv)! - feat: Exposing elk configuration forceNodeModelOrder and considerModelOrder to the mermaid configuration
|
||||
|
||||
- [#6653](https://github.com/mermaid-js/mermaid/pull/6653) [`2c0931d`](https://github.com/mermaid-js/mermaid/commit/2c0931da46794b49d2523211e25f782900c34e94) Thanks [@darshanr0107](https://github.com/darshanr0107)! - chore: Remove the "-beta" suffix from the XYChart, Block, Sankey diagrams to reflect their stable status
|
||||
|
||||
- [#6683](https://github.com/mermaid-js/mermaid/pull/6683) [`33e08da`](https://github.com/mermaid-js/mermaid/commit/33e08daf175125295a06b1b80279437004a4e865) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Position the edge label in state diagram correctly relative to the edge
|
||||
|
||||
- [#6693](https://github.com/mermaid-js/mermaid/pull/6693) [`814b68b`](https://github.com/mermaid-js/mermaid/commit/814b68b4a94813f7c6b3d7fb4559532a7bab2652) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Apply correct dateFormat in Gantt chart to show only day when specified
|
||||
|
||||
- [#6734](https://github.com/mermaid-js/mermaid/pull/6734) [`fce7cab`](https://github.com/mermaid-js/mermaid/commit/fce7cabb71d68a20a66246fe23d066512126a412) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: handle exclude dates properly in Gantt charts when using dateFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
|
||||
- [#6733](https://github.com/mermaid-js/mermaid/pull/6733) [`fc07f0d`](https://github.com/mermaid-js/mermaid/commit/fc07f0d8abca49e4f887d7457b7b94fb07d1e3da) Thanks [@omkarht](https://github.com/omkarht)! - fix: fixed connection gaps in flowchart for roundedRect, stadium and diamond shape
|
||||
|
||||
- [#6876](https://github.com/mermaid-js/mermaid/pull/6876) [`12e01bd`](https://github.com/mermaid-js/mermaid/commit/12e01bdb5cacf3569133979a5a4f1d8973e9aec1) Thanks [@sidharthv96](https://github.com/sidharthv96)! - fix: sanitize icon labels and icon SVGs
|
||||
|
||||
Resolves CVE-2025-54880 reported by @fourcube
|
||||
|
||||
- [#6801](https://github.com/mermaid-js/mermaid/pull/6801) [`01aaef3`](https://github.com/mermaid-js/mermaid/commit/01aaef39b4a1ec8bc5a0c6bfa3a20b712d67f4dc) Thanks [@sidharthv96](https://github.com/sidharthv96)! - fix: Update casing of ID in requirement diagram
|
||||
|
||||
- [#6796](https://github.com/mermaid-js/mermaid/pull/6796) [`c36cd05`](https://github.com/mermaid-js/mermaid/commit/c36cd05c45ac3090181152b4dae41f8d7b569bd6) Thanks [@HashanCP](https://github.com/HashanCP)! - fix: Make flowchart elk detector regex match less greedy
|
||||
|
||||
- [#6702](https://github.com/mermaid-js/mermaid/pull/6702) [`8bb29fc`](https://github.com/mermaid-js/mermaid/commit/8bb29fc879329ad109898e4025b4f4eba2ab0649) Thanks [@qraqras](https://github.com/qraqras)! - fix(block): overflowing blocks no longer affect later lines
|
||||
|
||||
This may change the layout of block diagrams that have overflowing lines
|
||||
(i.e. block diagrams that use up more columns that the `columns` specifier).
|
||||
|
||||
- [#6717](https://github.com/mermaid-js/mermaid/pull/6717) [`71b04f9`](https://github.com/mermaid-js/mermaid/commit/71b04f93b07f876df2b30656ef36036c1d0e4e4f) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: log warning for blocks exceeding column width
|
||||
|
||||
This update adds a validation check that logs a warning message when a block's width exceeds the defined column layout.
|
||||
|
||||
- [#6820](https://github.com/mermaid-js/mermaid/pull/6820) [`c99bce6`](https://github.com/mermaid-js/mermaid/commit/c99bce6bab4c7ce0b81b66d44f44853ce4aeb1c3) Thanks [@kriss-u](https://github.com/kriss-u)! - fix: Add escaped class literal name on namespace
|
||||
|
||||
- [#6332](https://github.com/mermaid-js/mermaid/pull/6332) [`6cc1926`](https://github.com/mermaid-js/mermaid/commit/6cc192680a2531cab28f87a8061a53b786e010f3) Thanks [@ajuckel](https://github.com/ajuckel)! - fix: Allow equals sign in sequenceDiagram labels
|
||||
|
||||
- [#6651](https://github.com/mermaid-js/mermaid/pull/6651) [`9da6fb3`](https://github.com/mermaid-js/mermaid/commit/9da6fb39ae278401771943ac85d6d1b875f78cf1) Thanks [@darshanr0107](https://github.com/darshanr0107)! - Add validation for negative values in pie charts:
|
||||
|
||||
Prevents crashes during parsing by validating values post-parsing.
|
||||
|
||||
Provides clearer, user-friendly error messages for invalid negative inputs.
|
||||
|
||||
- [#6803](https://github.com/mermaid-js/mermaid/pull/6803) [`e48b0ba`](https://github.com/mermaid-js/mermaid/commit/e48b0ba61dab7f95aa02da603b5b7d383b894932) Thanks [@omkarht](https://github.com/omkarht)! - chore: migrate to class-based ArchitectureDB implementation
|
||||
|
||||
- [#6838](https://github.com/mermaid-js/mermaid/pull/6838) [`4d62d59`](https://github.com/mermaid-js/mermaid/commit/4d62d5963238400270e9314c6e4d506f48147074) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: node border style for handdrawn shapes
|
||||
|
||||
- [#6739](https://github.com/mermaid-js/mermaid/pull/6739) [`e9ce8cf`](https://github.com/mermaid-js/mermaid/commit/e9ce8cf4da9062d85098042044822100889bb0dd) Thanks [@kriss-u](https://github.com/kriss-u)! - fix: Update flowchart direction TD's behavior to be the same as TB
|
||||
|
||||
- [#6833](https://github.com/mermaid-js/mermaid/pull/6833) [`9258b29`](https://github.com/mermaid-js/mermaid/commit/9258b2933bbe1ef41087345ffea3731673671c49) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: correctly render non-directional lines for '---' in block diagrams
|
||||
|
||||
- [#6855](https://github.com/mermaid-js/mermaid/pull/6855) [`da90f67`](https://github.com/mermaid-js/mermaid/commit/da90f6760b6efb0da998bcb63b75eecc29e06c08) Thanks [@sidharthv96](https://github.com/sidharthv96)! - fix: fallback to raw text instead of rendering _Unsupported markdown_ or empty blocks
|
||||
|
||||
Instead of printing **Unsupported markdown: XXX**, or empty blocks when using a markdown feature
|
||||
that Mermaid does not yet support when `htmlLabels: true`(default) or `htmlLabels: false`,
|
||||
fallback to the raw markdown text.
|
||||
|
||||
- [#6876](https://github.com/mermaid-js/mermaid/pull/6876) [`0133f1c`](https://github.com/mermaid-js/mermaid/commit/0133f1c0c5cff4fc4c8e0b99e9cf0b3d49dcbe71) Thanks [@sidharthv96](https://github.com/sidharthv96)! - fix: sanitize KATEX blocks
|
||||
|
||||
Resolves CVE-2025-54881 reported by @fourcube
|
||||
|
||||
- [#6804](https://github.com/mermaid-js/mermaid/pull/6804) [`895f9d4`](https://github.com/mermaid-js/mermaid/commit/895f9d43ff98ca05ebfba530789f677f31a011ff) Thanks [@omkarht](https://github.com/omkarht)! - chore: Update packet diagram to use new class-based database structure
|
||||
|
||||
## 11.9.0
|
||||
|
||||
### Minor Changes
|
||||
@@ -154,7 +87,6 @@
|
||||
### Minor Changes
|
||||
|
||||
- [#6408](https://github.com/mermaid-js/mermaid/pull/6408) [`ad65313`](https://github.com/mermaid-js/mermaid/commit/ad653138e16765d095613a6e5de86dc5e52ac8f0) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: restore curve type configuration functionality for flowcharts. This fixes the issue where curve type settings were not being applied when configured through any of the following methods:
|
||||
|
||||
- Config
|
||||
- Init directive (%%{ init: { 'flowchart': { 'curve': '...' } } }%%)
|
||||
- LinkStyle command (linkStyle default interpolate ...)
|
||||
@@ -173,14 +105,12 @@
|
||||
### Minor Changes
|
||||
|
||||
- [#6187](https://github.com/mermaid-js/mermaid/pull/6187) [`7809b5a`](https://github.com/mermaid-js/mermaid/commit/7809b5a93fae127f45727071f5ff14325222c518) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Flowchart new syntax for node metadata bugs
|
||||
|
||||
- Incorrect label mapping for nodes when using `&`
|
||||
- Syntax error when `}` with trailing spaces before new line
|
||||
|
||||
- [#6136](https://github.com/mermaid-js/mermaid/pull/6136) [`ec0d9c3`](https://github.com/mermaid-js/mermaid/commit/ec0d9c389aa6018043187654044c1e0b5aa4f600) Thanks [@knsv](https://github.com/knsv)! - Adding support for animation of flowchart edges
|
||||
|
||||
- [#6373](https://github.com/mermaid-js/mermaid/pull/6373) [`05bdf0e`](https://github.com/mermaid-js/mermaid/commit/05bdf0e20e2629fe77513218fbd4e28e65f75882) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Upgrade Requirement and ER diagram to use the common renderer flow
|
||||
|
||||
- Added support for directions
|
||||
- Added support for hand drawn look
|
||||
|
||||
@@ -229,7 +159,6 @@
|
||||
- [#5999](https://github.com/mermaid-js/mermaid/pull/5999) [`742ad7c`](https://github.com/mermaid-js/mermaid/commit/742ad7c130964df1fb5544e909d9556081285f68) Thanks [@knsv](https://github.com/knsv)! - Adding Kanban board, a new diagram type
|
||||
|
||||
- [#5880](https://github.com/mermaid-js/mermaid/pull/5880) [`bdf145f`](https://github.com/mermaid-js/mermaid/commit/bdf145ffe362462176d9c1e68d5f3ff5c9d962b0) Thanks [@yari-dewalt](https://github.com/yari-dewalt)! - Class diagram changes:
|
||||
|
||||
- Updates the class diagram to the new unified way of rendering.
|
||||
- Includes a new "classBox" shape to be used in diagrams
|
||||
- Other updates such as:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "11.10.0",
|
||||
"version": "11.9.0",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@@ -82,7 +82,7 @@
|
||||
"katex": "^0.16.22",
|
||||
"khroma": "^2.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^16.0.0",
|
||||
"marked": "^15.0.7",
|
||||
"roughjs": "^4.6.6",
|
||||
"stylis": "^4.3.6",
|
||||
"ts-dedent": "^2.2.0",
|
||||
@@ -123,8 +123,8 @@
|
||||
"rimraf": "^6.0.1",
|
||||
"start-server-and-test": "^2.0.10",
|
||||
"type-fest": "^4.35.0",
|
||||
"typedoc": "^0.27.8",
|
||||
"typedoc-plugin-markdown": "^4.4.2",
|
||||
"typedoc": "^0.28.9",
|
||||
"typedoc-plugin-markdown": "^4.8.0",
|
||||
"typescript": "~5.7.3",
|
||||
"unist-util-flatmap": "^1.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
|
@@ -171,7 +171,9 @@ This Markdown should be kept.
|
||||
expect(buildShapeDoc()).toMatchInlineSnapshot(`
|
||||
"| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
||||
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
|
||||
| Bang | Bang | \`bang\` | Bang | \`bang\` |
|
||||
| Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` |
|
||||
| Cloud | Cloud | \`cloud\` | cloud | \`cloud\` |
|
||||
| Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` |
|
||||
| Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` |
|
||||
| Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` |
|
||||
|
@@ -109,16 +109,6 @@ export interface MermaidConfig {
|
||||
| 'INTERACTIVE'
|
||||
| 'MODEL_ORDER'
|
||||
| 'GREEDY_MODEL_ORDER';
|
||||
/**
|
||||
* The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
|
||||
*
|
||||
*/
|
||||
forceNodeModelOrder?: boolean;
|
||||
/**
|
||||
* Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
|
||||
*
|
||||
*/
|
||||
considerModelOrder?: 'NONE' | 'NODES_AND_EDGES' | 'PREFER_EDGES' | 'PREFER_NODES';
|
||||
};
|
||||
darkMode?: boolean;
|
||||
htmlLabels?: boolean;
|
||||
@@ -1075,6 +1065,10 @@ export interface ArchitectureDiagramConfig extends BaseDiagramConfig {
|
||||
export interface MindmapDiagramConfig extends BaseDiagramConfig {
|
||||
padding?: number;
|
||||
maxNodeWidth?: number;
|
||||
/**
|
||||
* Layout algorithm to use for positioning mindmap nodes
|
||||
*/
|
||||
layoutAlgorithm?: string;
|
||||
}
|
||||
/**
|
||||
* The object containing configurations specific for kanban diagrams
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { select } from 'd3';
|
||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
||||
import { evaluate, sanitizeText } from '../diagrams/common/common.js';
|
||||
import { log } from '../logger.js';
|
||||
import { replaceIconSubstring } from '../rendering-util/createText.js';
|
||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
||||
import { evaluate } from '../diagrams/common/common.js';
|
||||
import { decodeEntities } from '../utils.js';
|
||||
import { replaceIconSubstring } from '../rendering-util/createText.js';
|
||||
|
||||
/**
|
||||
* @param dom
|
||||
@@ -19,14 +19,14 @@ function applyStyle(dom, styleFn) {
|
||||
* @param {any} node
|
||||
* @returns {SVGForeignObjectElement} Node
|
||||
*/
|
||||
function addHtmlLabel(node, config) {
|
||||
function addHtmlLabel(node) {
|
||||
const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'));
|
||||
const div = fo.append('xhtml:div');
|
||||
|
||||
const label = node.label;
|
||||
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||
const span = div.append('span');
|
||||
span.html(sanitizeText(label, config));
|
||||
span.html(label);
|
||||
applyStyle(span, node.labelStyle);
|
||||
span.attr('class', labelClass);
|
||||
|
||||
@@ -49,8 +49,7 @@ const createLabel = async (_vertexText, style, isTitle, isNode) => {
|
||||
if (typeof vertexText === 'object') {
|
||||
vertexText = vertexText[0];
|
||||
}
|
||||
const config = getConfig();
|
||||
if (evaluate(config.flowchart.htmlLabels)) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
|
||||
log.debug('vertexText' + vertexText);
|
||||
@@ -60,7 +59,7 @@ const createLabel = async (_vertexText, style, isTitle, isNode) => {
|
||||
label,
|
||||
labelStyle: style.replace('fill:', 'color:'),
|
||||
};
|
||||
let vertexNode = addHtmlLabel(node, config);
|
||||
let vertexNode = addHtmlLabel(node);
|
||||
// vertexNode.parentNode.removeChild(vertexNode);
|
||||
return vertexNode;
|
||||
} else {
|
||||
|
@@ -24,8 +24,6 @@ const config: RequiredDeep<MermaidConfig> = {
|
||||
// mergeEdges is needed here to be considered
|
||||
mergeEdges: false,
|
||||
nodePlacementStrategy: 'BRANDES_KOEPF',
|
||||
forceNodeModelOrder: false,
|
||||
considerModelOrder: 'NODES_AND_EDGES',
|
||||
},
|
||||
themeCSS: undefined,
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
// tests to check that comments are removed
|
||||
|
||||
import { cleanupComments } from './comments.js';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
@@ -10,12 +8,12 @@ describe('comments', () => {
|
||||
%% This is a comment
|
||||
%% This is another comment
|
||||
graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
%% This is a comment
|
||||
`;
|
||||
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||
"graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
"
|
||||
`);
|
||||
});
|
||||
@@ -29,9 +27,9 @@ graph TD
|
||||
%%{ init: {'theme': 'space before init'}}%%
|
||||
%%{init: {'theme': 'space after ending'}}%%
|
||||
graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
|
||||
B-->C
|
||||
B-->C
|
||||
%% This is a comment
|
||||
`;
|
||||
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||
@@ -39,9 +37,9 @@ graph TD
|
||||
%%{ init: {'theme': 'space before init'}}%%
|
||||
%%{init: {'theme': 'space after ending'}}%%
|
||||
graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
|
||||
B-->C
|
||||
B-->C
|
||||
"
|
||||
`);
|
||||
});
|
||||
@@ -50,14 +48,14 @@ graph TD
|
||||
const text = `
|
||||
%% This is a comment
|
||||
graph TD
|
||||
A-->B
|
||||
%% This is a comment
|
||||
C-->D
|
||||
A-->B
|
||||
%% This is a comment
|
||||
C-->D
|
||||
`;
|
||||
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||
"graph TD
|
||||
A-->B
|
||||
C-->D
|
||||
A-->B
|
||||
C-->D
|
||||
"
|
||||
`);
|
||||
});
|
||||
@@ -70,11 +68,11 @@ graph TD
|
||||
|
||||
%% This is a comment
|
||||
graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
`;
|
||||
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||
"graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
"
|
||||
`);
|
||||
});
|
||||
@@ -82,12 +80,12 @@ graph TD
|
||||
it('should remove comments at end of text with no newline', () => {
|
||||
const text = `
|
||||
graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
%% This is a comment`;
|
||||
|
||||
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||
"graph TD
|
||||
A-->B
|
||||
A-->B
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
@@ -4,5 +4,6 @@
|
||||
* @returns cleaned text
|
||||
*/
|
||||
export const cleanupComments = (text: string): string => {
|
||||
return text.replace(/^\s*%%(?!{)[^\n]+\n?/gm, '').trimStart();
|
||||
const cleaned = text.replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
|
||||
return cleaned.trimStart();
|
||||
};
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import type { Position } from 'cytoscape';
|
||||
import type { LayoutOptions, Position } from 'cytoscape';
|
||||
import cytoscape from 'cytoscape';
|
||||
import type { FcoseLayoutOptions } from 'cytoscape-fcose';
|
||||
import fcose from 'cytoscape-fcose';
|
||||
import { select } from 'd3';
|
||||
import type { DrawDefinition, SVG } from '../../diagram-api/types.js';
|
||||
@@ -41,7 +40,7 @@ registerIconPacks([
|
||||
icons: architectureIcons,
|
||||
},
|
||||
]);
|
||||
cytoscape.use(fcose);
|
||||
cytoscape.use(fcose as any);
|
||||
|
||||
function addServices(services: ArchitectureService[], cy: cytoscape.Core, db: ArchitectureDB) {
|
||||
services.forEach((service) => {
|
||||
@@ -429,7 +428,7 @@ function layoutArchitecture(
|
||||
},
|
||||
alignmentConstraint,
|
||||
relativePlacementConstraint,
|
||||
} as FcoseLayoutOptions);
|
||||
} as LayoutOptions);
|
||||
|
||||
// Once the diagram has been generated and the service's position cords are set, adjust the XY edges to have a 90deg bend
|
||||
layout.one('layoutstop', () => {
|
||||
|
@@ -3,7 +3,6 @@ import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { createText } from '../../rendering-util/createText.js';
|
||||
import { getIconSVG } from '../../rendering-util/icons.js';
|
||||
import type { D3Element } from '../../types.js';
|
||||
import { sanitizeText } from '../common/common.js';
|
||||
import type { ArchitectureDB } from './architectureDb.js';
|
||||
import { architectureIcons } from './architectureIcons.js';
|
||||
import {
|
||||
@@ -272,7 +271,6 @@ export const drawServices = async function (
|
||||
elem: D3Element,
|
||||
services: ArchitectureService[]
|
||||
): Promise<number> {
|
||||
const config = getConfig();
|
||||
for (const service of services) {
|
||||
const serviceElem = elem.append('g');
|
||||
const iconSize = db.getConfigField('iconSize');
|
||||
@@ -287,7 +285,7 @@ export const drawServices = async function (
|
||||
width: iconSize * 1.5,
|
||||
classes: 'architecture-service-label',
|
||||
},
|
||||
config
|
||||
getConfig()
|
||||
);
|
||||
|
||||
textElem
|
||||
@@ -322,7 +320,7 @@ export const drawServices = async function (
|
||||
.attr('class', 'node-icon-text')
|
||||
.attr('style', `height: ${iconSize}px;`)
|
||||
.append('div')
|
||||
.html(sanitizeText(service.iconText, config));
|
||||
.html(service.iconText);
|
||||
const fontSize =
|
||||
parseInt(
|
||||
window
|
||||
|
@@ -311,8 +311,9 @@ export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.leng
|
||||
* @returns Object containing \{width, height\}
|
||||
*/
|
||||
export const calculateMathMLDimensions = async (text: string, config: MermaidConfig) => {
|
||||
text = await renderKatex(text, config);
|
||||
const divElem = document.createElement('div');
|
||||
divElem.innerHTML = await renderKatexSanitized(text, config);
|
||||
divElem.innerHTML = text;
|
||||
divElem.id = 'katex-temp';
|
||||
divElem.style.visibility = 'hidden';
|
||||
divElem.style.position = 'absolute';
|
||||
@@ -324,7 +325,14 @@ export const calculateMathMLDimensions = async (text: string, config: MermaidCon
|
||||
return dim;
|
||||
};
|
||||
|
||||
const renderKatexUnsanitized = async (text: string, config: MermaidConfig): Promise<string> => {
|
||||
/**
|
||||
* Attempts to render and return the KaTeX portion of a string with MathML
|
||||
*
|
||||
* @param text - The text to test
|
||||
* @param config - Configuration for Mermaid
|
||||
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
|
||||
*/
|
||||
export const renderKatex = async (text: string, config: MermaidConfig): Promise<string> => {
|
||||
if (!hasKatex(text)) {
|
||||
return text;
|
||||
}
|
||||
@@ -365,20 +373,6 @@ const renderKatexUnsanitized = async (text: string, config: MermaidConfig): Prom
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to render and return the KaTeX portion of a string with MathML
|
||||
*
|
||||
* @param text - The text to test
|
||||
* @param config - Configuration for Mermaid
|
||||
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
|
||||
*/
|
||||
export const renderKatexSanitized = async (
|
||||
text: string,
|
||||
config: MermaidConfig
|
||||
): Promise<string> => {
|
||||
return sanitizeText(await renderKatexUnsanitized(text, config), config);
|
||||
};
|
||||
|
||||
export default {
|
||||
getRows,
|
||||
sanitizeText,
|
||||
|
297
packages/mermaid/src/diagrams/mindmap/mindmapDb.getData.test.ts
Normal file
297
packages/mermaid/src/diagrams/mindmap/mindmapDb.getData.test.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { MindmapDB } from './mindmapDb.js';
|
||||
import type { MindmapLayoutNode, MindmapLayoutEdge } from './mindmapDb.js';
|
||||
import type { Edge } from '../../rendering-util/types.js';
|
||||
|
||||
// Mock the getConfig function
|
||||
vi.mock('../../diagram-api/diagramAPI.js', () => ({
|
||||
getConfig: vi.fn(() => ({
|
||||
mindmap: {
|
||||
layoutAlgorithm: 'cose-bilkent',
|
||||
padding: 10,
|
||||
maxNodeWidth: 200,
|
||||
useMaxWidth: true,
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('MindmapDb getData function', () => {
|
||||
let db: MindmapDB;
|
||||
|
||||
beforeEach(() => {
|
||||
db = new MindmapDB();
|
||||
// Clear the database before each test
|
||||
db.clear();
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return empty data when no mindmap is set', () => {
|
||||
const result = db.getData();
|
||||
|
||||
expect(result.nodes).toEqual([]);
|
||||
expect(result.edges).toEqual([]);
|
||||
expect(result.config).toBeDefined();
|
||||
expect(result.rootNode).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return structured data for simple mindmap', () => {
|
||||
// Create a simple mindmap structure
|
||||
db.addNode(0, 'root', 'Root Node', 0);
|
||||
db.addNode(1, 'child1', 'Child 1', 0);
|
||||
db.addNode(1, 'child2', 'Child 2', 0);
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
expect(result.nodes).toHaveLength(3);
|
||||
expect(result.edges).toHaveLength(2);
|
||||
expect(result.config).toBeDefined();
|
||||
expect(result.rootNode).toBeDefined();
|
||||
|
||||
// Check root node
|
||||
const rootNode = (result.nodes as MindmapLayoutNode[]).find((n) => n.id === '0');
|
||||
expect(rootNode).toBeDefined();
|
||||
expect(rootNode?.label).toBe('Root Node');
|
||||
expect(rootNode?.level).toBe(0);
|
||||
|
||||
// Check child nodes
|
||||
const child1 = (result.nodes as MindmapLayoutNode[]).find((n) => n.id === '1');
|
||||
expect(child1).toBeDefined();
|
||||
expect(child1?.label).toBe('Child 1');
|
||||
expect(child1?.level).toBe(1);
|
||||
|
||||
// Check edges
|
||||
expect(result.edges).toContainEqual(
|
||||
expect.objectContaining({
|
||||
start: '0',
|
||||
end: '1',
|
||||
depth: 0,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should return structured data for hierarchical mindmap', () => {
|
||||
// Create a hierarchical mindmap structure
|
||||
db.addNode(0, 'root', 'Root Node', 0);
|
||||
db.addNode(1, 'child1', 'Child 1', 0);
|
||||
db.addNode(2, 'grandchild1', 'Grandchild 1', 0);
|
||||
db.addNode(2, 'grandchild2', 'Grandchild 2', 0);
|
||||
db.addNode(1, 'child2', 'Child 2', 0);
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
expect(result.nodes).toHaveLength(5);
|
||||
expect(result.edges).toHaveLength(4);
|
||||
|
||||
// Check that all levels are represented
|
||||
const levels = result.nodes.map((n) => (n as MindmapLayoutNode).level);
|
||||
expect(levels).toContain(0); // root
|
||||
expect(levels).toContain(1); // children
|
||||
expect(levels).toContain(2); // grandchildren
|
||||
|
||||
// Check edge relationships
|
||||
const edgeRelations = result.edges.map(
|
||||
(e) => `${(e as MindmapLayoutEdge).start}->${(e as MindmapLayoutEdge).end}`
|
||||
);
|
||||
expect(edgeRelations).toContain('0->1'); // root to child1
|
||||
expect(edgeRelations).toContain('1->2'); // child1 to grandchild1
|
||||
expect(edgeRelations).toContain('1->3'); // child1 to grandchild2
|
||||
expect(edgeRelations).toContain('0->4'); // root to child2
|
||||
});
|
||||
|
||||
it('should preserve node properties in processed data', () => {
|
||||
// Add a node with specific properties
|
||||
db.addNode(0, 'root', 'Root Node', 2); // type 2 = rectangle
|
||||
|
||||
// Set additional properties
|
||||
const mindmap = db.getMindmap();
|
||||
if (mindmap) {
|
||||
mindmap.width = 150;
|
||||
mindmap.height = 75;
|
||||
mindmap.padding = 15;
|
||||
mindmap.section = 1;
|
||||
mindmap.class = 'custom-class';
|
||||
mindmap.icon = 'star';
|
||||
}
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
expect(result.nodes).toHaveLength(1);
|
||||
const node = result.nodes[0] as MindmapLayoutNode;
|
||||
|
||||
expect(node.type).toBe(2);
|
||||
expect(node.width).toBe(150);
|
||||
expect(node.height).toBe(75);
|
||||
expect(node.padding).toBe(15);
|
||||
expect(node.section).toBeUndefined(); // Root node has undefined section
|
||||
expect(node.cssClasses).toBe('mindmap-node section-root section--1 custom-class');
|
||||
expect(node.icon).toBe('star');
|
||||
});
|
||||
|
||||
it('should generate unique edge IDs', () => {
|
||||
db.addNode(0, 'root', 'Root Node', 0);
|
||||
db.addNode(1, 'child1', 'Child 1', 0);
|
||||
db.addNode(1, 'child2', 'Child 2', 0);
|
||||
db.addNode(1, 'child3', 'Child 3', 0);
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
const edgeIds = result.edges.map((e: Edge) => e.id);
|
||||
const uniqueIds = new Set(edgeIds);
|
||||
|
||||
expect(edgeIds).toHaveLength(3);
|
||||
expect(uniqueIds.size).toBe(3); // All IDs should be unique
|
||||
});
|
||||
|
||||
it('should handle nodes with missing optional properties', () => {
|
||||
db.addNode(0, 'root', 'Root Node', 0);
|
||||
|
||||
const result = db.getData();
|
||||
const node = result.nodes[0] as MindmapLayoutNode;
|
||||
|
||||
// Should handle undefined/missing properties gracefully
|
||||
expect(node.section).toBeUndefined(); // Root node has undefined section
|
||||
expect(node.cssClasses).toBe('mindmap-node section-root section--1'); // Root node gets special classes
|
||||
expect(node.icon).toBeUndefined();
|
||||
expect(node.x).toBeUndefined();
|
||||
expect(node.y).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should assign correct section classes based on sibling position', () => {
|
||||
// Create the example mindmap structure:
|
||||
// A
|
||||
// a0
|
||||
// aa0
|
||||
// a1
|
||||
// aaa
|
||||
// a2
|
||||
db.addNode(0, 'A', 'A', 0); // Root
|
||||
db.addNode(1, 'a0', 'a0', 0); // First child of root
|
||||
db.addNode(2, 'aa0', 'aa0', 0); // Child of a0
|
||||
db.addNode(1, 'a1', 'a1', 0); // Second child of root
|
||||
db.addNode(2, 'aaa', 'aaa', 0); // Child of a1
|
||||
db.addNode(1, 'a2', 'a2', 0); // Third child of root
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
// Find nodes by their labels
|
||||
const nodeA = result.nodes.find((n) => n.label === 'A') as MindmapLayoutNode;
|
||||
const nodeA0 = result.nodes.find((n) => n.label === 'a0') as MindmapLayoutNode;
|
||||
const nodeAa0 = result.nodes.find((n) => n.label === 'aa0') as MindmapLayoutNode;
|
||||
const nodeA1 = result.nodes.find((n) => n.label === 'a1') as MindmapLayoutNode;
|
||||
const nodeAaa = result.nodes.find((n) => n.label === 'aaa') as MindmapLayoutNode;
|
||||
const nodeA2 = result.nodes.find((n) => n.label === 'a2') as MindmapLayoutNode;
|
||||
|
||||
// Check section assignments
|
||||
expect(nodeA.section).toBeUndefined(); // Root has undefined section
|
||||
expect(nodeA0.section).toBe(0); // First child of root
|
||||
expect(nodeAa0.section).toBe(0); // Inherits from parent a0
|
||||
expect(nodeA1.section).toBe(1); // Second child of root
|
||||
expect(nodeAaa.section).toBe(1); // Inherits from parent a1
|
||||
expect(nodeA2.section).toBe(2); // Third child of root
|
||||
|
||||
// Check CSS classes
|
||||
expect(nodeA.cssClasses).toBe('mindmap-node section-root section--1');
|
||||
expect(nodeA0.cssClasses).toBe('mindmap-node section-0');
|
||||
expect(nodeAa0.cssClasses).toBe('mindmap-node section-0');
|
||||
expect(nodeA1.cssClasses).toBe('mindmap-node section-1');
|
||||
expect(nodeAaa.cssClasses).toBe('mindmap-node section-1');
|
||||
expect(nodeA2.cssClasses).toBe('mindmap-node section-2');
|
||||
});
|
||||
|
||||
it('should preserve custom classes while adding section classes', () => {
|
||||
db.addNode(0, 'root', 'Root Node', 0);
|
||||
db.addNode(1, 'child', 'Child Node', 0);
|
||||
|
||||
// Add custom classes to nodes
|
||||
const mindmap = db.getMindmap();
|
||||
if (mindmap) {
|
||||
mindmap.class = 'custom-root-class';
|
||||
if (mindmap.children?.[0]) {
|
||||
mindmap.children[0].class = 'custom-child-class';
|
||||
}
|
||||
}
|
||||
|
||||
const result = db.getData();
|
||||
const rootNode = result.nodes.find((n) => n.label === 'Root Node') as MindmapLayoutNode;
|
||||
const childNode = result.nodes.find((n) => n.label === 'Child Node') as MindmapLayoutNode;
|
||||
|
||||
// Should include both section classes and custom classes
|
||||
expect(rootNode.cssClasses).toBe('mindmap-node section-root section--1 custom-root-class');
|
||||
expect(childNode.cssClasses).toBe('mindmap-node section-0 custom-child-class');
|
||||
});
|
||||
|
||||
it('should not create any fake root nodes', () => {
|
||||
// Create a simple mindmap
|
||||
db.addNode(0, 'A', 'A', 0);
|
||||
db.addNode(1, 'a0', 'a0', 0);
|
||||
db.addNode(1, 'a1', 'a1', 0);
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
// Check that we only have the expected nodes
|
||||
expect(result.nodes).toHaveLength(3);
|
||||
expect(result.nodes.map((n) => n.label)).toEqual(['A', 'a0', 'a1']);
|
||||
|
||||
// Check that there's no node with label "mindmap" or any other fake root
|
||||
const mindmapNode = result.nodes.find((n) => n.label === 'mindmap');
|
||||
expect(mindmapNode).toBeUndefined();
|
||||
|
||||
// Verify the root node has the correct classes
|
||||
const rootNode = result.nodes.find((n) => n.label === 'A') as MindmapLayoutNode;
|
||||
expect(rootNode.cssClasses).toBe('mindmap-node section-root section--1');
|
||||
expect(rootNode.level).toBe(0);
|
||||
});
|
||||
|
||||
it('should assign correct section classes to edges', () => {
|
||||
// Create the example mindmap structure:
|
||||
// A
|
||||
// a0
|
||||
// aa0
|
||||
// a1
|
||||
// aaa
|
||||
// a2
|
||||
db.addNode(0, 'A', 'A', 0); // Root
|
||||
db.addNode(1, 'a0', 'a0', 0); // First child of root
|
||||
db.addNode(2, 'aa0', 'aa0', 0); // Child of a0
|
||||
db.addNode(1, 'a1', 'a1', 0); // Second child of root
|
||||
db.addNode(2, 'aaa', 'aaa', 0); // Child of a1
|
||||
db.addNode(1, 'a2', 'a2', 0); // Third child of root
|
||||
|
||||
const result = db.getData();
|
||||
|
||||
// Should have 5 edges: A->a0, a0->aa0, A->a1, a1->aaa, A->a2
|
||||
expect(result.edges).toHaveLength(5);
|
||||
|
||||
// Find edges by their start and end nodes
|
||||
const edgeA_a0 = result.edges.find(
|
||||
(e) => e.start === '0' && e.end === '1'
|
||||
) as MindmapLayoutEdge;
|
||||
const edgeA0_aa0 = result.edges.find(
|
||||
(e) => e.start === '1' && e.end === '2'
|
||||
) as MindmapLayoutEdge;
|
||||
const edgeA_a1 = result.edges.find(
|
||||
(e) => e.start === '0' && e.end === '3'
|
||||
) as MindmapLayoutEdge;
|
||||
const edgeA1_aaa = result.edges.find(
|
||||
(e) => e.start === '3' && e.end === '4'
|
||||
) as MindmapLayoutEdge;
|
||||
const edgeA_a2 = result.edges.find(
|
||||
(e) => e.start === '0' && e.end === '5'
|
||||
) as MindmapLayoutEdge;
|
||||
|
||||
// Check edge classes
|
||||
expect(edgeA_a0.classes).toBe('edge section-edge-0 edge-depth-1'); // A->a0: section-0, depth-1
|
||||
expect(edgeA0_aa0.classes).toBe('edge section-edge-0 edge-depth-2'); // a0->aa0: section-0, depth-2
|
||||
expect(edgeA_a1.classes).toBe('edge section-edge-1 edge-depth-1'); // A->a1: section-1, depth-1
|
||||
expect(edgeA1_aaa.classes).toBe('edge section-edge-1 edge-depth-2'); // a1->aaa: section-1, depth-2
|
||||
expect(edgeA_a2.classes).toBe('edge section-edge-2 edge-depth-1'); // A->a2: section-2, depth-1
|
||||
|
||||
// Check section assignments match the child nodes
|
||||
expect(edgeA_a0.section).toBe(0);
|
||||
expect(edgeA0_aa0.section).toBe(0);
|
||||
expect(edgeA_a1.section).toBe(1);
|
||||
expect(edgeA1_aaa.section).toBe(1);
|
||||
expect(edgeA_a2.section).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
@@ -5,6 +5,22 @@ import { log } from '../../logger.js';
|
||||
import type { MindmapNode } from './mindmapTypes.js';
|
||||
import defaultConfig from '../../defaultConfig.js';
|
||||
|
||||
import type { LayoutData, Node, Edge } from '../../rendering-util/types.js';
|
||||
|
||||
// Extend Node type for mindmap-specific properties
|
||||
export type MindmapLayoutNode = Node & {
|
||||
level: number;
|
||||
nodeId: string;
|
||||
type: number;
|
||||
section?: number;
|
||||
};
|
||||
|
||||
// Extend Edge type for mindmap-specific properties
|
||||
export type MindmapLayoutEdge = Edge & {
|
||||
depth: number;
|
||||
section?: number;
|
||||
};
|
||||
|
||||
const nodeType = {
|
||||
DEFAULT: 0,
|
||||
NO_BORDER: 0,
|
||||
@@ -27,7 +43,6 @@ export class MindmapDB {
|
||||
this.nodeType = nodeType;
|
||||
this.clear();
|
||||
this.getType = this.getType.bind(this);
|
||||
this.getMindmap = this.getMindmap.bind(this);
|
||||
this.getElementById = this.getElementById.bind(this);
|
||||
this.getParent = this.getParent.bind(this);
|
||||
this.getMindmap = this.getMindmap.bind(this);
|
||||
@@ -156,6 +171,215 @@ export class MindmapDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign section numbers to nodes based on their position relative to root
|
||||
* @param node - The mindmap node to process
|
||||
* @param sectionNumber - The section number to assign (undefined for root)
|
||||
*/
|
||||
public assignSections(node: MindmapNode, sectionNumber?: number): void {
|
||||
// For root node, section should be undefined (not -1)
|
||||
if (node.level === 0) {
|
||||
node.section = undefined;
|
||||
} else {
|
||||
// For non-root nodes, assign the section number
|
||||
node.section = sectionNumber;
|
||||
}
|
||||
// For root node's children, assign section numbers based on their index
|
||||
// For other nodes, inherit parent's section number
|
||||
if (node.children) {
|
||||
for (const [index, child] of node.children.entries()) {
|
||||
const childSectionNumber = node.level === 0 ? index : sectionNumber;
|
||||
this.assignSections(child, childSectionNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert mindmap tree structure to flat array of nodes
|
||||
* @param node - The mindmap node to process
|
||||
* @param processedNodes - Array to collect processed nodes
|
||||
*/
|
||||
public flattenNodes(node: MindmapNode, processedNodes: MindmapLayoutNode[]): void {
|
||||
// Build CSS classes for the node
|
||||
const cssClasses = ['mindmap-node'];
|
||||
|
||||
// Add section-specific classes
|
||||
if (node.level === 0) {
|
||||
// Root node gets special classes
|
||||
cssClasses.push('section-root', 'section--1');
|
||||
} else if (node.section !== undefined) {
|
||||
// Child nodes get section class based on their section number
|
||||
cssClasses.push(`section-${node.section}`);
|
||||
}
|
||||
|
||||
// Add any custom classes from the node
|
||||
if (node.class) {
|
||||
cssClasses.push(node.class);
|
||||
}
|
||||
|
||||
const classes = cssClasses.join(' ');
|
||||
|
||||
// Map mindmap node type to valid shape name
|
||||
const getShapeFromType = (type: number) => {
|
||||
switch (type) {
|
||||
case nodeType.CIRCLE:
|
||||
return 'circle';
|
||||
case nodeType.RECT:
|
||||
return 'rect';
|
||||
case nodeType.ROUNDED_RECT:
|
||||
return 'rounded';
|
||||
case nodeType.CLOUD:
|
||||
return 'cloud';
|
||||
case nodeType.BANG:
|
||||
return 'bang';
|
||||
case nodeType.HEXAGON:
|
||||
return 'hexagon';
|
||||
case nodeType.DEFAULT:
|
||||
return 'defaultMindmapNode';
|
||||
case nodeType.NO_BORDER:
|
||||
default:
|
||||
return 'rect';
|
||||
}
|
||||
};
|
||||
|
||||
const processedNode: MindmapLayoutNode = {
|
||||
id: node.id.toString(),
|
||||
domId: 'node_' + node.id.toString(),
|
||||
label: node.descr,
|
||||
isGroup: false,
|
||||
shape: getShapeFromType(node.type),
|
||||
width: node.width,
|
||||
height: node.height ?? 0,
|
||||
padding: node.padding,
|
||||
cssClasses: classes,
|
||||
cssStyles: [],
|
||||
look: 'default',
|
||||
icon: node.icon,
|
||||
x: node.x,
|
||||
y: node.y,
|
||||
// Mindmap-specific properties
|
||||
level: node.level,
|
||||
nodeId: node.nodeId,
|
||||
type: node.type,
|
||||
section: node.section,
|
||||
};
|
||||
|
||||
processedNodes.push(processedNode);
|
||||
|
||||
// Recursively process children
|
||||
if (node.children) {
|
||||
for (const child of node.children) {
|
||||
this.flattenNodes(child, processedNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate edges from parent-child relationships in mindmap tree
|
||||
* @param node - The mindmap node to process
|
||||
* @param edges - Array to collect edges
|
||||
*/
|
||||
public generateEdges(node: MindmapNode, edges: MindmapLayoutEdge[]): void {
|
||||
if (!node.children) {
|
||||
return;
|
||||
}
|
||||
for (const child of node.children) {
|
||||
// Build CSS classes for the edge
|
||||
let edgeClasses = 'edge';
|
||||
|
||||
// Add section-specific classes based on the child's section
|
||||
if (child.section !== undefined) {
|
||||
edgeClasses += ` section-edge-${child.section}`;
|
||||
}
|
||||
|
||||
// Add depth class based on the parent's level + 1 (depth of the edge)
|
||||
const edgeDepth = node.level + 1;
|
||||
edgeClasses += ` edge-depth-${edgeDepth}`;
|
||||
|
||||
const edge: MindmapLayoutEdge = {
|
||||
id: `edge_${node.id}_${child.id}`,
|
||||
start: node.id.toString(),
|
||||
end: child.id.toString(),
|
||||
type: 'normal',
|
||||
curve: 'basis',
|
||||
thickness: 'normal',
|
||||
look: 'default',
|
||||
classes: edgeClasses,
|
||||
// Store mindmap-specific data
|
||||
depth: node.level,
|
||||
section: child.section,
|
||||
};
|
||||
|
||||
edges.push(edge);
|
||||
|
||||
// Recursively process child edges
|
||||
this.generateEdges(child, edges);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get structured data for layout algorithms
|
||||
* Following the pattern established by ER diagrams
|
||||
* @returns Structured data containing nodes, edges, and config
|
||||
*/
|
||||
public getData(): LayoutData {
|
||||
const mindmapRoot = this.getMindmap();
|
||||
const config = getConfig();
|
||||
|
||||
if (!mindmapRoot) {
|
||||
return {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
config,
|
||||
};
|
||||
}
|
||||
log.debug('getData: mindmapRoot', mindmapRoot, config);
|
||||
|
||||
// Assign section numbers to all nodes based on their position relative to root
|
||||
this.assignSections(mindmapRoot);
|
||||
|
||||
// Convert tree structure to flat arrays
|
||||
const processedNodes: MindmapLayoutNode[] = [];
|
||||
const processedEdges: MindmapLayoutEdge[] = [];
|
||||
|
||||
this.flattenNodes(mindmapRoot, processedNodes);
|
||||
this.generateEdges(mindmapRoot, processedEdges);
|
||||
|
||||
log.debug(
|
||||
`getData: processed ${processedNodes.length} nodes and ${processedEdges.length} edges`
|
||||
);
|
||||
|
||||
// Create shapes map for ELK compatibility
|
||||
const shapes = new Map<string, any>();
|
||||
for (const node of processedNodes) {
|
||||
shapes.set(node.id, {
|
||||
shape: node.shape,
|
||||
width: node.width,
|
||||
height: node.height,
|
||||
padding: node.padding,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
nodes: processedNodes,
|
||||
edges: processedEdges,
|
||||
config,
|
||||
// Store the root node for mindmap-specific layout algorithms
|
||||
rootNode: mindmapRoot,
|
||||
// Properties required by dagre layout algorithm
|
||||
markers: [], // Mindmaps don't use markers
|
||||
direction: 'TB', // Top-to-bottom direction for mindmaps
|
||||
nodeSpacing: 50, // Default spacing between nodes
|
||||
rankSpacing: 50, // Default spacing between ranks
|
||||
// Add shapes for ELK compatibility
|
||||
shapes: Object.fromEntries(shapes),
|
||||
// Additional properties that layout algorithms might expect
|
||||
type: 'mindmap',
|
||||
diagramId: 'mindmap-' + Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
// Expose logger to grammar
|
||||
public getLogger() {
|
||||
return log;
|
||||
}
|
||||
|
@@ -1,200 +1,108 @@
|
||||
import cytoscape from 'cytoscape';
|
||||
// @ts-expect-error No types available
|
||||
import coseBilkent from 'cytoscape-cose-bilkent';
|
||||
import { select } from 'd3';
|
||||
import type { MermaidConfig } from '../../config.type.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import type { DrawDefinition } from '../../diagram-api/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import type { D3Element } from '../../types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
import type { FilledMindMapNode, MindmapNode } from './mindmapTypes.js';
|
||||
import { drawNode, positionNode } from './svgDraw.js';
|
||||
import { getDiagramElement } from '../../rendering-util/insertElementsForSize.js';
|
||||
import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/render.js';
|
||||
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||
import type { LayoutData } from '../../rendering-util/types.js';
|
||||
import type { FilledMindMapNode } from './mindmapTypes.js';
|
||||
import { drawNode } from './svgDraw.js';
|
||||
import defaultConfig from '../../defaultConfig.js';
|
||||
import type { MindmapDB } from './mindmapDb.js';
|
||||
// Inject the layout algorithm into cytoscape
|
||||
cytoscape.use(coseBilkent);
|
||||
|
||||
async function drawNodes(
|
||||
async function _drawNodes(
|
||||
db: MindmapDB,
|
||||
svg: D3Element,
|
||||
svg: any,
|
||||
mindmap: FilledMindMapNode,
|
||||
section: number,
|
||||
conf: MermaidConfig
|
||||
conf: any
|
||||
) {
|
||||
await drawNode(db, svg, mindmap, section, conf);
|
||||
if (mindmap.children) {
|
||||
await Promise.all(
|
||||
mindmap.children.map((child, index) =>
|
||||
drawNodes(db, svg, child, section < 0 ? index : section, conf)
|
||||
_drawNodes(db, svg, child, section < 0 ? index : section, conf)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'cytoscape' {
|
||||
interface EdgeSingular {
|
||||
_private: {
|
||||
bodyBounds: unknown;
|
||||
rscratch: {
|
||||
startX: number;
|
||||
startY: number;
|
||||
midX: number;
|
||||
midY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function drawEdges(edgesEl: D3Element, cy: cytoscape.Core) {
|
||||
cy.edges().map((edge, id) => {
|
||||
const data = edge.data();
|
||||
if (edge[0]._private.bodyBounds) {
|
||||
const bounds = edge[0]._private.rscratch;
|
||||
log.trace('Edge: ', id, data);
|
||||
edgesEl
|
||||
.insert('path')
|
||||
.attr(
|
||||
'd',
|
||||
`M ${bounds.startX},${bounds.startY} L ${bounds.midX},${bounds.midY} L${bounds.endX},${bounds.endY} `
|
||||
)
|
||||
.attr('class', 'edge section-edge-' + data.section + ' edge-depth-' + data.depth);
|
||||
/**
|
||||
* Update the layout data with actual node dimensions after drawing
|
||||
*/
|
||||
function _updateNodeDimensions(data4Layout: LayoutData, mindmapRoot: FilledMindMapNode) {
|
||||
const updateNode = (node: FilledMindMapNode) => {
|
||||
// Find the corresponding node in the layout data
|
||||
const layoutNode = data4Layout.nodes.find((n) => n.id === node.id.toString());
|
||||
if (layoutNode) {
|
||||
// Update with the actual dimensions calculated by drawNode
|
||||
layoutNode.width = node.width;
|
||||
layoutNode.height = node.height;
|
||||
log.debug('Updated node dimensions:', node.id, 'width:', node.width, 'height:', node.height);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) {
|
||||
cy.add({
|
||||
group: 'nodes',
|
||||
data: {
|
||||
id: mindmap.id.toString(),
|
||||
labelText: mindmap.descr,
|
||||
height: mindmap.height,
|
||||
width: mindmap.width,
|
||||
level: level,
|
||||
nodeId: mindmap.id,
|
||||
padding: mindmap.padding,
|
||||
type: mindmap.type,
|
||||
},
|
||||
position: {
|
||||
x: mindmap.x!,
|
||||
y: mindmap.y!,
|
||||
},
|
||||
});
|
||||
if (mindmap.children) {
|
||||
mindmap.children.forEach((child) => {
|
||||
addNodes(child, cy, conf, level + 1);
|
||||
cy.add({
|
||||
group: 'edges',
|
||||
data: {
|
||||
id: `${mindmap.id}_${child.id}`,
|
||||
source: mindmap.id,
|
||||
target: child.id,
|
||||
depth: level,
|
||||
section: child.section,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// Recursively update children
|
||||
if (node.children) {
|
||||
node.children.forEach(updateNode);
|
||||
}
|
||||
};
|
||||
|
||||
function layoutMindmap(node: MindmapNode, conf: MermaidConfig): Promise<cytoscape.Core> {
|
||||
return new Promise((resolve) => {
|
||||
// Add temporary render element
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
const cy = cytoscape({
|
||||
container: document.getElementById('cy'), // container to render in
|
||||
style: [
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'curve-style': 'bezier',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
// Remove element after layout
|
||||
renderEl.remove();
|
||||
addNodes(node, cy, conf, 0);
|
||||
|
||||
// Make cytoscape care about the dimensions of the nodes
|
||||
cy.nodes().forEach(function (n) {
|
||||
n.layoutDimensions = () => {
|
||||
const data = n.data();
|
||||
return { w: data.width, h: data.height };
|
||||
};
|
||||
});
|
||||
|
||||
cy.layout({
|
||||
name: 'cose-bilkent',
|
||||
// @ts-ignore Types for cose-bilkent are not correct?
|
||||
quality: 'proof',
|
||||
styleEnabled: false,
|
||||
animate: false,
|
||||
}).run();
|
||||
cy.ready((e) => {
|
||||
log.info('Ready', e);
|
||||
resolve(cy);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function positionNodes(db: MindmapDB, cy: cytoscape.Core) {
|
||||
cy.nodes().map((node, id) => {
|
||||
const data = node.data();
|
||||
data.x = node.position().x;
|
||||
data.y = node.position().y;
|
||||
positionNode(db, data);
|
||||
const el = db.getElementById(data.nodeId);
|
||||
log.info('id:', id, 'Position: (', node.position().x, ', ', node.position().y, ')', data);
|
||||
el.attr(
|
||||
'transform',
|
||||
`translate(${node.position().x - data.width / 2}, ${node.position().y - data.height / 2})`
|
||||
);
|
||||
el.attr('attr', `apa-${id})`);
|
||||
});
|
||||
updateNode(mindmapRoot);
|
||||
}
|
||||
|
||||
export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
||||
log.debug('Rendering mindmap diagram\n' + text);
|
||||
const { securityLevel, mindmap: conf, layout } = getConfig();
|
||||
|
||||
// Draw the nodes first to get their dimensions, then update the layout data
|
||||
const db = diagObj.db as MindmapDB;
|
||||
|
||||
// The getData method provided in all supported diagrams is used to extract the data from the parsed structure
|
||||
// into the Layout data format
|
||||
const data4Layout = db.getData();
|
||||
|
||||
// Create the root SVG - the element is the div containing the SVG element
|
||||
const svg = getDiagramElement(id, securityLevel);
|
||||
|
||||
data4Layout.type = diagObj.type;
|
||||
data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout, {
|
||||
fallback: 'cose-bilkent',
|
||||
});
|
||||
// For mindmap diagrams, prioritize mindmap-specific layout algorithm configuration
|
||||
|
||||
data4Layout.diagramId = id;
|
||||
|
||||
// Ensure required properties are set for compatibility with different layout algorithms
|
||||
data4Layout.markers = ['point'];
|
||||
data4Layout.direction = 'TB';
|
||||
|
||||
const mm = db.getMindmap();
|
||||
if (!mm) {
|
||||
return;
|
||||
}
|
||||
data4Layout.nodes.forEach((node) => {
|
||||
if (node.shape === 'rounded') {
|
||||
node.radius = 15;
|
||||
node.taper = 15;
|
||||
node.stroke = 'none';
|
||||
node.from = 'mindmap';
|
||||
} else if (node.shape === 'rect') {
|
||||
node.height = 46;
|
||||
node.width = 92;
|
||||
node.from = 'mindmap';
|
||||
} else if (node.shape === 'circle') {
|
||||
node.from = 'mindmap';
|
||||
}
|
||||
});
|
||||
|
||||
const conf = getConfig();
|
||||
conf.htmlLabels = false;
|
||||
|
||||
const svg = selectSvgElement(id);
|
||||
|
||||
// Draw the graph and start with drawing the nodes without proper position
|
||||
// this gives us the size of the nodes and we can set the positions later
|
||||
|
||||
const edgesElem = svg.append('g');
|
||||
edgesElem.attr('class', 'mindmap-edges');
|
||||
const nodesElem = svg.append('g');
|
||||
nodesElem.attr('class', 'mindmap-nodes');
|
||||
await drawNodes(db, nodesElem, mm as FilledMindMapNode, -1, conf);
|
||||
|
||||
// Next step is to layout the mindmap, giving each node a position
|
||||
|
||||
const cy = await layoutMindmap(mm, conf);
|
||||
|
||||
// After this we can draw, first the edges and the then nodes with the correct position
|
||||
drawEdges(edgesElem, cy);
|
||||
positionNodes(db, cy);
|
||||
|
||||
// Use the unified rendering system
|
||||
await render(data4Layout, svg);
|
||||
// Setup the view box and size of the svg element
|
||||
setupGraphViewbox(
|
||||
undefined,
|
||||
setupViewPortForSVG(
|
||||
svg,
|
||||
conf.mindmap?.padding ?? defaultConfig.mindmap.padding,
|
||||
conf.mindmap?.useMaxWidth ?? defaultConfig.mindmap.useMaxWidth
|
||||
conf?.padding ?? defaultConfig.mindmap.padding,
|
||||
'mindmapDiagram',
|
||||
conf?.useMaxWidth ?? defaultConfig.mindmap.useMaxWidth
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -64,6 +64,12 @@ const getStyles: DiagramStylesProvider = (options) =>
|
||||
.section-root text {
|
||||
fill: ${options.gitBranchLabel0};
|
||||
}
|
||||
.section-root span {
|
||||
color: ${options.gitBranchLabel0};
|
||||
}
|
||||
.section-2 span {
|
||||
color: ${options.gitBranchLabel0};
|
||||
}
|
||||
.icon-container {
|
||||
height:100%;
|
||||
display: flex;
|
||||
|
@@ -1368,7 +1368,7 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com
|
||||
it('should handle box without description', async () => {
|
||||
const diagram = await Diagram.fromText(`
|
||||
sequenceDiagram
|
||||
box Aqua
|
||||
box aqua
|
||||
participant a as Alice
|
||||
participant b as Bob
|
||||
end
|
||||
@@ -1384,7 +1384,7 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com
|
||||
const boxes = diagram.db.getBoxes();
|
||||
expect(boxes[0].name).toBeFalsy();
|
||||
expect(boxes[0].actorKeys).toEqual(['a', 'b']);
|
||||
expect(boxes[0].fill).toEqual('Aqua');
|
||||
expect(boxes[0].fill).toEqual('aqua');
|
||||
});
|
||||
|
||||
it('should handle simple actor creation', async () => {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user