Compare commits

..

5 Commits

Author SHA1 Message Date
darshanr0107
fca17f3b10 chore: update ER diagram docs
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-03 13:10:50 +05:30
darshanr0107
94dfdf31b8 fix: add test case to endsure whitespace-only relationship labels are handled
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-02 11:40:35 +05:30
darshanr0107
f981d3d5b7 chore: updated changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-20 20:06:45 +05:30
darshanr0107
7139e1e5f7 chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-20 18:58:20 +05:30
darshanr0107
299226f8c2 fix: relationship label to be optional in ER diagram syntax
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-20 18:36:45 +05:30
175 changed files with 7811 additions and 13775 deletions

View File

@@ -33,11 +33,6 @@ 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',

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Make relationship-label optional in ER diagrams

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
chore: Fix mindmap rendering in docs and apply tidytree layout

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Ensure edge label color is applied when using classDef with edge IDs

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Resolve gantt chart crash due to invalid array length

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Add IDs in architecture diagrams

View File

@@ -1,9 +0,0 @@
---
'mermaid': patch
---
chore: revert marked dependency from ^15.0.7 to ^16.0.0
- Reverted marked package version to ^16.0.0 for better compatibility
- This is a dependency update that maintains API compatibility
- All tests pass with the updated version

View File

@@ -5,10 +5,8 @@ bmatrix
braintree
catmull
compositTitleSize
cose
curv
doublecircle
elem
elems
gantt
gitgraph

View File

@@ -1,5 +1,4 @@
BRANDES
Buzan
circo
handDrawn
KOEPF

View File

@@ -36,7 +36,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
with:
config-file: ./.github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
@@ -48,7 +48,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -62,4 +62,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10

View File

@@ -23,6 +23,9 @@ env:
jobs:
e2e-applitools:
runs-on: ubuntu-latest
container:
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
options: --user 1001
steps:
- if: ${{ ! env.USE_APPLI }}
name: Warn if not using Applitools
@@ -53,7 +56,7 @@ jobs:
args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
- name: Cypress run
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
id: cypress
with:
start: pnpm run dev

View File

@@ -27,12 +27,12 @@ jobs:
with:
node-version-file: '.node-version'
- name: Install dependencies
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
with:
runTests: false
- name: Cypress run
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
id: cypress
with:
install: false
@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
uses: peter-evans/create-pull-request@915d841dae6a4f191bb78faf61a257411d7be4d2
uses: peter-evans/create-pull-request@cb4d3bfce175d44325c6b7697f81e0afe8a79bdf
with:
add-paths: |
cypress/timings.json

View File

@@ -45,7 +45,7 @@ jobs:
node-version-file: '.node-version'
- name: Cache snapshots
id: cache-snapshot
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
@@ -59,7 +59,7 @@ jobs:
- name: Install dependencies
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
with:
# just perform install
runTests: false
@@ -95,13 +95,13 @@ jobs:
# These cached snapshots are downloaded, providing the reference snapshots.
- name: Cache snapshots
id: cache-snapshot
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
- name: Install dependencies
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
with:
runTests: false
@@ -117,7 +117,7 @@ jobs:
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
id: cypress
with:
install: false

View File

@@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Restore lychee cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}

View File

@@ -36,7 +36,7 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
with:
version: pnpm changeset:version
publish: pnpm changeset:publish

View File

@@ -20,18 +20,18 @@ jobs:
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
with:
sarif_file: results.sarif

View File

@@ -19,7 +19,7 @@ jobs:
message: 'chore: update browsers list'
push: false
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
with:
branch: update-browserslist
title: Update Browserslist

View File

@@ -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
View File

@@ -4,7 +4,6 @@ node_modules/
coverage/
.idea/
.pnpm-store/
.instructions/
dist
v8-compile-cache-0

View File

@@ -98,12 +98,12 @@ describe('Configuration', () => {
it('should handle arrowMarkerAbsolute set to true', () => {
renderGraph(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{
arrowMarkerAbsolute: true,
}
@@ -113,7 +113,8 @@ describe('Configuration', () => {
cy.get('path')
.first()
.should('have.attr', 'marker-end')
.and('include', 'url(http://localhost');
.should('exist')
.and('include', 'url(http\\:\\/\\/localhost');
});
});
it('should not taint the initial configuration when using multiple directives', () => {

View File

@@ -524,18 +524,5 @@ describe('Class diagram', () => {
`,
{}
);
it('should handle an empty class body with empty braces', () => {
imgSnapshotTest(
` classDiagram
class FooBase~T~ {}
class Bar {
+Zip
+Zap()
}
FooBase <|-- Ba
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
});

View File

@@ -322,6 +322,18 @@ ORDER ||--|{ LINE-ITEM : contains
);
});
it('should render an ER diagram without labels also', () => {
imgSnapshotTest(
`
erDiagram
BOOK }|..|{ AUTHOR
BOOK }|..|{ GENRE
AUTHOR }|..|{ GENRE
`,
{ logLevel: 1 }
);
});
it('should render relationship labels with line breaks', () => {
imgSnapshotTest(
`

View File

@@ -109,7 +109,7 @@ describe('Flowchart ELK', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
verifyNumber(maxWidthValue, 380, 15);
verifyNumber(maxWidthValue, 380);
});
});
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
@@ -128,7 +128,7 @@ describe('Flowchart ELK', () => {
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
verifyNumber(width, 380, 15);
verifyNumber(width, 380);
expect(svg).to.not.have.attr('style');
});
});

View File

@@ -1186,17 +1186,4 @@ end
imgSnapshotTest(graph, { htmlLabels: false });
});
});
it('V2 - 17: should apply class def colour to edge label', () => {
imgSnapshotTest(
` graph LR
id1(Start) link@-- "Label" -->id2(Stop)
style id1 fill:#f9f,stroke:#333,stroke-width:4px
class id2 myClass
classDef myClass fill:#bbf,stroke:#f66,stroke-width:2px,color:white,stroke-dasharray: 5 5
class link myClass
`
);
});
});

View File

@@ -803,34 +803,4 @@ describe('Gantt diagram', () => {
{}
);
});
it('should handle numeric timestamps with dateFormat x', () => {
imgSnapshotTest(
`
gantt
title Process time profile (ms)
dateFormat x
axisFormat %L
tickInterval 250millisecond
section Pipeline
Parse JSON p1: 000, 120
`,
{}
);
});
it('should handle numeric timestamps with dateFormat X', () => {
imgSnapshotTest(
`
gantt
title Process time profile (ms)
dateFormat X
axisFormat %L
tickInterval 250millisecond
section Pipeline
Parse JSON p1: 000, 120
`,
{}
);
});
});

View File

@@ -1,79 +0,0 @@
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&lt;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
`
);
});
});

View File

@@ -159,10 +159,12 @@ root
});
it('square shape', () => {
imgSnapshotTest(
`mindmap
`
mindmap
root[
The root
]`,
]
`,
{},
undefined,
shouldHaveRoot
@@ -170,10 +172,12 @@ root
});
it('rounded rect shape', () => {
imgSnapshotTest(
`mindmap
`
mindmap
root((
The root
))`,
))
`,
{},
undefined,
shouldHaveRoot
@@ -181,10 +185,12 @@ root
});
it('circle shape', () => {
imgSnapshotTest(
`mindmap
`
mindmap
root(
The root
)`,
)
`,
{},
undefined,
shouldHaveRoot
@@ -192,8 +198,10 @@ root
});
it('default shape', () => {
imgSnapshotTest(
`mindmap
The root`,
`
mindmap
The root
`,
{},
undefined,
shouldHaveRoot
@@ -201,10 +209,12 @@ root
});
it('adding children', () => {
imgSnapshotTest(
`mindmap
`
mindmap
The root
child1
child2`,
child2
`,
{},
undefined,
shouldHaveRoot
@@ -212,11 +222,13 @@ root
});
it('adding grand children', () => {
imgSnapshotTest(
`mindmap
`
mindmap
The root
child1
child2
child3`,
child3
`,
{},
undefined,
shouldHaveRoot
@@ -228,21 +240,25 @@ root
`mindmap
id1[\`**Start** with
a second line 😎\`]
id2[\`The dog in **the** hog... a *very long text* about it Word!\`]`
id2[\`The dog in **the** hog... a *very long text* about it
Word!\`]
`
);
});
});
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
` mindmap
`
mindmap
root
Photograph
Waterfall
Landscape
Geography
Mountains
Rocks`,
Rocks
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});

View File

@@ -1,659 +0,0 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
const looks = ['classic'];
const participantTypes = [
{ type: 'participant', display: 'participant' },
{ type: 'actor', display: 'actor' },
{ type: 'boundary', display: 'boundary' },
{ type: 'control', display: 'control' },
{ type: 'entity', display: 'entity' },
{ type: 'database', display: 'database' },
{ type: 'collections', display: 'collections' },
{ type: 'queue', display: 'queue' },
];
const restrictedTypes = ['boundary', 'control', 'entity', 'database', 'collections', 'queue'];
const interactionTypes = ['->>', '-->>', '->', '-->', '-x', '--x', '->>+', '-->>+'];
const notePositions = ['left of', 'right of', 'over'];
function getParticipantLine(name, type, alias) {
if (restrictedTypes.includes(type)) {
return ` participant ${name}@{ "type" : "${type}" }\n`;
} else if (alias) {
return ` participant ${name}@{ "type" : "${type}" } \n`;
} else {
return ` participant ${name}@{ "type" : "${type}" }\n`;
}
}
looks.forEach((look) => {
describe(`Sequence Diagram Tests - ${look} look`, () => {
it('should render all participant types', () => {
let diagramCode = `sequenceDiagram\n`;
participantTypes.forEach((pt, index) => {
const name = `${pt.display}${index}`;
diagramCode += getParticipantLine(name, pt.type);
});
for (let i = 0; i < participantTypes.length - 1; i++) {
diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
}
imgSnapshotTest(diagramCode, { look, sequence: { diagramMarginX: 50, diagramMarginY: 10 } });
});
it('should render all interaction types', () => {
let diagramCode = `sequenceDiagram\n`;
diagramCode += getParticipantLine('A', 'actor');
diagramCode += getParticipantLine('B', 'boundary');
interactionTypes.forEach((interaction, index) => {
diagramCode += ` A ${interaction} B: ${interaction} message ${index}\n`;
});
imgSnapshotTest(diagramCode, { look });
});
it('should render participant creation and destruction', () => {
let diagramCode = `sequenceDiagram\n`;
participantTypes.forEach((pt, index) => {
const name = `${pt.display}${index}`;
diagramCode += getParticipantLine('A', pt.type);
diagramCode += getParticipantLine('B', pt.type);
diagramCode += ` create participant ${name}@{ "type" : "${pt.type}" }\n`;
diagramCode += ` A ->> ${name}: Hello ${pt.display}\n`;
if (index % 2 === 0) {
diagramCode += ` destroy ${name}\n`;
}
});
imgSnapshotTest(diagramCode, { look });
});
it('should render notes in all positions', () => {
let diagramCode = `sequenceDiagram\n`;
diagramCode += getParticipantLine('A', 'actor');
diagramCode += getParticipantLine('B', 'boundary');
notePositions.forEach((position, index) => {
diagramCode += ` Note ${position} A: Note ${position} ${index}\n`;
});
diagramCode += ` A ->> B: Message with notes\n`;
imgSnapshotTest(diagramCode, { look });
});
it('should render parallel interactions', () => {
let diagramCode = `sequenceDiagram\n`;
participantTypes.slice(0, 4).forEach((pt, index) => {
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
});
diagramCode += ` par Parallel actions\n`;
for (let i = 0; i < 3; i += 2) {
diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
if (i < participantTypes.length - 2) {
diagramCode += ` and\n`;
}
}
diagramCode += ` end\n`;
imgSnapshotTest(diagramCode, { look });
});
it('should render alternative flows', () => {
let diagramCode = `sequenceDiagram\n`;
diagramCode += getParticipantLine('A', 'actor');
diagramCode += getParticipantLine('B', 'boundary');
diagramCode += ` alt Successful case\n`;
diagramCode += ` A ->> B: Request\n`;
diagramCode += ` B -->> A: Success\n`;
diagramCode += ` else Failure case\n`;
diagramCode += ` A ->> B: Request\n`;
diagramCode += ` B --x A: Failure\n`;
diagramCode += ` end\n`;
imgSnapshotTest(diagramCode, { look });
});
it('should render loops', () => {
let diagramCode = `sequenceDiagram\n`;
participantTypes.slice(0, 3).forEach((pt, index) => {
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
});
diagramCode += ` loop For each participant\n`;
for (let i = 0; i < 3; i++) {
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Message ${i}\n`;
}
diagramCode += ` end\n`;
imgSnapshotTest(diagramCode, { look });
});
it('should render boxes around groups', () => {
let diagramCode = `sequenceDiagram\n`;
diagramCode += ` box Group 1\n`;
participantTypes.slice(0, 3).forEach((pt, index) => {
diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
});
diagramCode += ` end\n`;
diagramCode += ` box rgb(200,220,255) Group 2\n`;
participantTypes.slice(3, 6).forEach((pt, index) => {
diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
});
diagramCode += ` end\n`;
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[3].display}0: Cross-group message\n`;
imgSnapshotTest(diagramCode, { look });
});
it('should render with different font settings', () => {
let diagramCode = `sequenceDiagram\n`;
participantTypes.slice(0, 3).forEach((pt, index) => {
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
});
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Regular message\n`;
diagramCode += ` Note right of ${participantTypes[1].display}1: Regular note\n`;
imgSnapshotTest(diagramCode, {
look,
sequence: {
actorFontFamily: 'courier',
actorFontSize: 14,
messageFontFamily: 'Arial',
messageFontSize: 12,
noteFontFamily: 'times',
noteFontSize: 16,
noteAlign: 'left',
},
});
});
});
});
// Additional tests for specific combinations
describe('Sequence Diagram Special Cases', () => {
it('should render complex sequence with all features', () => {
const diagramCode = `
sequenceDiagram
box rgb(200,220,255) Authentication
actor User
participant LoginUI@{ "type": "boundary" }
participant AuthService@{ "type": "control" }
participant UserDB@{ "type": "database" }
end
box rgb(200,255,220) Order Processing
participant Order@{ "type": "entity" }
participant OrderQueue@{ "type": "queue" }
participant AuditLogs@{ "type": "collections" }
end
User ->> LoginUI: Enter credentials
LoginUI ->> AuthService: Validate
AuthService ->> UserDB: Query user
UserDB -->> AuthService: User data
alt Valid credentials
AuthService -->> LoginUI: Success
LoginUI -->> User: Welcome
par Place order
User ->> Order: New order
Order ->> OrderQueue: Process
and
Order ->> AuditLogs: Record
end
loop Until confirmed
OrderQueue ->> Order: Update status
Order -->> User: Notification
end
else Invalid credentials
AuthService --x LoginUI: Failure
LoginUI --x User: Retry
end
`;
imgSnapshotTest(diagramCode, {});
});
it('should render with wrapped messages and notes', () => {
const diagramCode = `
sequenceDiagram
participant A
participant B
A ->> B: This is a very long message that should wrap properly in the diagram rendering
Note over A,B: This is a very long note that should also wrap properly when rendered in the diagram
par Wrapped parallel
A ->> B: Parallel message 1<br>with explicit line break
and
B ->> A: Parallel message 2<br>with explicit line break
end
loop Wrapped loop
Note right of B: This is a long note<br>in a loop
A ->> B: Message in loop
end
`;
imgSnapshotTest(diagramCode, { sequence: { wrap: true } });
});
describe('Sequence Diagram Rendering with Different Participant Types', () => {
it('should render a sequence diagram with various participant types', () => {
imgSnapshotTest(
`
sequenceDiagram
participant User@{ "type": "actor" }
participant AuthService@{ "type": "control" }
participant UI@{ "type": "boundary" }
participant OrderController@{ "type": "control" }
participant Product@{ "type": "entity" }
participant MongoDB@{ "type": "database" }
participant Products@{ "type": "collections" }
participant OrderQueue@{ "type": "queue" }
User ->> UI: Login request
UI ->> AuthService: Validate credentials
AuthService -->> UI: Authentication token
UI ->> OrderController: Place order
OrderController ->> Product: Check availability
Product -->> OrderController: Available
OrderController ->> MongoDB: Save order
MongoDB -->> OrderController: Order saved
OrderController ->> OrderQueue: Process payment
OrderQueue -->> User: Order confirmation
`
);
});
it('should render participant creation and destruction with different types', () => {
imgSnapshotTest(`
sequenceDiagram
participant Alice@{ "type" : "boundary" }
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl@{ "type" : "control" }
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bob
Bob->>Alice: I agree
`);
});
it('should handle complex interactions between different participant types', () => {
imgSnapshotTest(
`
sequenceDiagram
box rgb(200,220,255) Authentication
participant User@{ "type": "actor" }
participant LoginUI@{ "type": "boundary" }
participant AuthService@{ "type": "control" }
participant UserDB@{ "type": "database" }
end
box rgb(200,255,220) Order Processing
participant Order@{ "type": "entity" }
participant OrderQueue@{ "type": "queue" }
participant AuditLogs@{ "type": "collections" }
end
User ->> LoginUI: Enter credentials
LoginUI ->> AuthService: Validate
AuthService ->> UserDB: Query user
UserDB -->> AuthService: User data
alt Valid credentials
AuthService -->> LoginUI: Success
LoginUI -->> User: Welcome
par Place order
User ->> Order: New order
Order ->> OrderQueue: Process
and
Order ->> AuditLogs: Record
end
loop Until confirmed
OrderQueue ->> Order: Update status
Order -->> User: Notification
end
else Invalid credentials
AuthService --x LoginUI: Failure
LoginUI --x User: Retry
end
`,
{ sequence: { useMaxWidth: false } }
);
});
it('should render parallel processes with different participant types', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Customer@{ "type": "actor" }
participant Frontend@{ "type": "participant" }
participant PaymentService@{ "type": "boundary" }
participant InventoryManager@{ "type": "control" }
participant Order@{ "type": "entity" }
participant OrdersDB@{ "type": "database" }
participant NotificationQueue@{ "type": "queue" }
Customer ->> Frontend: Place order
Frontend ->> Order: Create order
par Parallel Processing
Order ->> PaymentService: Process payment
and
Order ->> InventoryManager: Reserve items
end
PaymentService -->> Order: Payment confirmed
InventoryManager -->> Order: Items reserved
Order ->> OrdersDB: Save finalized order
OrdersDB -->> Order: Order saved
Order ->> NotificationQueue: Send confirmation
NotificationQueue -->> Customer: Order confirmation
`
);
});
});
it('should render different participant types with notes and loops', () => {
imgSnapshotTest(
`
sequenceDiagram
actor Admin
participant Dashboard
participant AuthService@{ "type" : "boundary" }
participant UserManager@{ "type" : "control" }
participant UserProfile@{ "type" : "entity" }
participant UserDB@{ "type" : "database" }
participant Logs@{ "type" : "database" }
Admin ->> Dashboard: Open user management
loop Authentication check
Dashboard ->> AuthService: Verify admin rights
AuthService ->> Dashboard: Access granted
end
Dashboard ->> UserManager: List users
UserManager ->> UserDB: Query users
UserDB ->> UserManager: Return user data
Note right of UserDB: Encrypted data<br/>requires decryption
UserManager ->> UserProfile: Format profiles
UserProfile ->> UserManager: Formatted data
UserManager ->> Dashboard: Display users
Dashboard ->> Logs: Record access
Logs ->> Admin: Audit trail
`
);
});
it('should render different participant types with alternative flows', () => {
imgSnapshotTest(
`
sequenceDiagram
actor Client
participant MobileApp
participant CloudService@{ "type" : "boundary" }
participant DataProcessor@{ "type" : "control" }
participant Transaction@{ "type" : "entity" }
participant TransactionsDB@{ "type" : "database" }
participant EventBus@{ "type" : "queue" }
Client ->> MobileApp: Initiate transaction
MobileApp ->> CloudService: Authenticate
alt Authentication successful
CloudService -->> MobileApp: Auth token
MobileApp ->> DataProcessor: Process data
DataProcessor ->> Transaction: Create transaction
Transaction ->> TransactionsDB: Save record
TransactionsDB -->> Transaction: Confirmation
Transaction ->> EventBus: Publish event
EventBus -->> Client: Notification
else Authentication failed
CloudService -->> MobileApp: Error
MobileApp -->> Client: Show error
end
`
);
});
it('should render different participant types with wrapping text', () => {
imgSnapshotTest(
`
sequenceDiagram
participant B@{ "type" : "boundary" }
participant C@{ "type" : "control" }
participant E@{ "type" : "entity" }
participant DB@{ "type" : "database" }
participant COL@{ "type" : "collections" }
participant Q@{ "type" : "queue" }
FE ->> B: Another long message<br/>with explicit<br/>line breaks
B -->> FE: Response message that is also quite long and needs to wrap
FE ->> C: Process data
C ->> E: Validate
E -->> C: Validation result
C ->> DB: Save
DB -->> C: Save result
C ->> COL: Log
COL -->> Q: Forward
Q -->> LongNameUser: Final response with confirmation of all actions taken
`,
{ sequence: { wrap: true } }
);
});
describe('Sequence Diagram - New Participant Types with Long Notes and Messages', () => {
it('should render long notes left of boundary', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "boundary" }
actor Bob
Alice->>Bob: Hola
Note left of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render wrapped long notes left of control', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "control" }
actor Bob
Alice->>Bob: Hola
Note left of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long notes right of entity', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "entity" }
actor Bob
Alice->>Bob: Hola
Note right of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render wrapped long notes right of database', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "database" }
actor Bob
Alice->>Bob: Hola
Note right of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long notes over collections', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "collections" }
actor Bob
Alice->>Bob: Hola
Note over Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render wrapped long notes over queue', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "queue" }
actor Bob
Alice->>Bob: Hola
Note over Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render notes over actor and boundary', () => {
imgSnapshotTest(
`
sequenceDiagram
actor Alice
participant Charlie@{ "type" : "boundary" }
note over Alice: Some note
note over Charlie: Other note
`,
{}
);
});
it('should render long messages from database to collections', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "database" }
participant Bob@{ "type" : "collections" }
Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render wrapped long messages from control to entity', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "control" }
participant Bob@{ "type" : "entity" }
Alice->>Bob:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though
`,
{}
);
});
it('should render long messages from queue to boundary', () => {
imgSnapshotTest(
`
sequenceDiagram
participant Alice@{ "type" : "queue" }
participant Bob@{ "type" : "boundary" }
Alice->>Bob: I'm short
Bob->>Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
`,
{}
);
});
it('should render wrapped long messages from actor to database', () => {
imgSnapshotTest(
`
sequenceDiagram
actor Alice
participant Bob@{ "type" : "database" }
Alice->>Bob: I'm short
Bob->>Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
`,
{}
);
});
});
describe('svg size', () => {
it('should render a sequence diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
sequenceDiagram
actor Alice
participant Bob@{ "type" : "boundary" }
participant John@{ "type" : "control" }
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
Bob-->Alice: Checking with John...
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
`,
{ sequence: { useMaxWidth: true } }
);
cy.get('svg').should((svg) => {
expect(svg).to.have.attr('width', '100%');
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(820 * 0.95, 820 * 1.05);
});
});
it('should render a sequence diagram when useMaxWidth is false', () => {
renderGraph(
`
sequenceDiagram
actor Alice
participant Bob@{ "type" : "boundary" }
participant John@{ "type" : "control" }
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
Bob-->Alice: Checking with John...
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
`,
{ sequence: { useMaxWidth: false } }
);
cy.get('svg').should((svg) => {
const width = parseFloat(svg.attr('width'));
expect(width).to.be.within(820 * 0.95, 820 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
});
});

View File

@@ -893,17 +893,6 @@ describe('Sequence diagram', () => {
}
);
});
it('should handle bidirectional arrows with autonumber', () => {
imgSnapshotTest(`
sequenceDiagram
autonumber
participant A
participant B
A<<->>B: This is a bidirectional message
A->B: This is a normal message`);
});
it('should support actor links and properties when not mirrored EXPERIMENTAL: USE WITH CAUTION', () => {
//Be aware that the syntax for "properties" is likely to be changed.
imgSnapshotTest(

View File

@@ -32,8 +32,26 @@
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Recursive:wght@300..1000&display=swap"
rel="stylesheet"
/>
<style>
.recursive-mermaid {
font-family: 'Recursive', sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
font-variation-settings:
'slnt' 0,
'CASL' 0,
'CRSV' 0.5,
'MONO' 0;
}
body {
/* background: rgb(221, 208, 208); */
/* background: #333; */
@@ -45,7 +63,9 @@
h1 {
color: grey;
}
.mermaid {
border: 1px solid red;
}
.mermaid2 {
display: none;
}
@@ -83,6 +103,11 @@
width: 100%;
}
.class2 {
fill: red;
fill-opacity: 1;
}
/* tspan {
font-size: 6px !important;
} */
@@ -106,194 +131,6 @@
<body>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart-elk TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart TB
process_C
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
process_B-->|via_AWSBatch|container_Beta
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
classDiagram
note "I love this diagram!\nDo you love it?"
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class12 <|.. Class08
Class11 ..>Class12
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
note for Class10 "Cool class\nI said it's very cool class!"
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
compute1
subgraph project
router
nat
subgraph subnet1
compute1
end
end
%% router --> subnet1
subnet1 --> nat
%% nat --> internet
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
lb1
lb2
compute1
compute2
subgraph project
router
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> router
router --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -320,149 +157,84 @@ treemap
"Leaf 2.2": 25
"Leaf 2.3": 12
</pre>
<pre id="diagram5" class="mermaid">
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
I --> D & D
D@{ shape: question}
I@{ shape: question}
classDef class1 fill:red,color:blue,stroke:#FFD600;
</pre>
<pre id="diagram4" 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
Pen and paper
Mermaid
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] --> B[B]
A[A] --- B([C])
A@{ shape: diamond}
%%B@{ shape: diamond}
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] -- Mermaid js --> B[B]
A[A] -- Mermaid js --- B[B]
A@{ shape: diamond}
B@{ shape: diamond}
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
I --> D & D
D@{ shape: question}
I@{ shape: question}
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
flowchart:
curve: rounded
elk:
nodePlacementStrategy: NETWORK_SIMPLEX
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
D --> I & I
a["a"]
D@{ shape: trap-b}
I@{ shape: lean-l}
</pre>
<pre id="diagram4" class="mermaid">
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
treemap:
valueFormat: '$0,0'
---
flowchart LR
%% subgraph s1["Untitled subgraph"]
C["Evaluate"]
%% end
treemap
"Budget"
"Operations"
"Salaries": 7000
"Equipment": 2000
"Supplies": 1000
"Marketing"
"Advertising": 4000
"Events": 1000
B --> C
</pre>
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
flowchart:
//curve: linear
---
flowchart LR
%% A ==> B
%% A2 --> B2
A{A} --> B((Bo boo)) & B & B & B
treemap
title Accessible Treemap Title
"Category A"
"Item A1": 10
"Item A2": 20
"Category B"
"Item B1": 15
"Item B2": 25
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
theme: default
look: classic
---
<pre id="diagram4" class="mermaid2">
flowchart LR
subgraph s1["APA"]
D{"Use the editor"}
end
subgraph S2["S2"]
s1
I>"fa:fa-code Text"]
E["E"]
end
D -- Mermaid js --> I
D --> I & E
E --> I
AB["apa@apa@"] --> B(("`apa@apa`"))
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart
D(("for D"))
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@==> B
e1@{ animate: true}
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round
class e1 animate
</pre>
<h2>infinite</h2>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class e1 animate
</pre>
<h2>Mermaid - edge-animation-slow</h2>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
e1@{ animation: fast}
</pre>
<h2>Mermaid - edge-animation-fast</h2>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
classDef animate stroke-dasharray: 1000,stroke-dashoffset: 1000,animation: dash 10s linear;
class e1 edge-animation-fast
</pre>
<pre id="diagram4" class="mermaid2">
info </pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -487,7 +259,7 @@ config:
end
end
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -500,7 +272,7 @@ config:
D-->I
D-->I
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -539,7 +311,7 @@ flowchart LR
n8@{ shape: rect}
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -555,7 +327,7 @@ flowchart LR
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -564,7 +336,7 @@ flowchart LR
A{A} --> B & C
</pre
>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -576,7 +348,7 @@ flowchart LR
end
</pre
>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -594,7 +366,7 @@ flowchart LR
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
kanban:
@@ -613,81 +385,81 @@ kanban
task3[💻 Develop login feature]@{ ticket: 103 }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
kanban
id2[In progress]
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
kanban:
@@ -751,22 +523,18 @@ kanban
alert('It worked');
}
await mermaid.initialize({
// theme: 'base',
// theme: 'forest',
// theme: 'default',
// theme: 'forest',
// handDrawnSeed: 12,
// look: 'handDrawn',
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
// layout: 'dagre',
layout: 'elk',
// layout: 'elk',
// layout: 'fixed',
// htmlLabels: false,
flowchart: { titleTopMargin: 10 },
// fontFamily: 'Caveat',
// fontFamily: 'Kalam',
// fontFamily: 'courier',
fontFamily: 'arial',
fontFamily: "'Recursive', sans-serif",
sequence: {
actorFontFamily: 'courier',
noteFontFamily: 'courier',

View File

@@ -1,376 +0,0 @@
<!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&lt;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&lt;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&lt;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&lt;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>

View File

@@ -1,6 +1,5 @@
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';
@@ -66,7 +65,6 @@ const contentLoaded = async function () {
await mermaid.registerExternalDiagrams([externalExample, zenUml]);
mermaid.registerLayoutLoaders(layouts);
mermaid.registerLayoutLoaders(tidyTree);
mermaid.initialize(graphObj.mermaid);
/**
* CC-BY-4.0

View File

@@ -2,227 +2,219 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 5841
"duration": 6297
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2138
"duration": 2187
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3370
"duration": 3509
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 2052
"duration": 2218
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 12243
"duration": 12104
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 2065
"duration": 2151
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 31288
"duration": 33064
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3421
"duration": 3488
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 97
"duration": 106
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 18500
"duration": 18317
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 5793
"duration": 5592
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 40966
"duration": 39358
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 39176
"duration": 37160
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 23468
"duration": 23660
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 38291
"duration": 36866
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 16949
"duration": 17334
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9480
"duration": 9871
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2753
"duration": 2833
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 88028
"duration": 85321
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 15615
"duration": 15673
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3706
"duration": 3724
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 43905
"duration": 41178
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 31217
"duration": 29966
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7531
"duration": 7689
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 25423
"duration": 24709
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 49664
"duration": 45565
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 32525
"duration": 31144
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 20915
"duration": 20808
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 53556
"duration": 49985
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 283038
"duration": 273272
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 59434
"duration": 55880
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3101
"duration": 3271
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7099
"duration": 7293
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7567
"duration": 7861
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3817
"duration": 3922
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2624
},
{
"spec": "cypress/integration/rendering/mindmap-tidy-tree.spec.js",
"duration": 4246
"duration": 2726
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 11967
"duration": 11670
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 151914
"duration": 146020
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 116698
"duration": 114244
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4967
"duration": 5036
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 6700
"duration": 6545
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 8963
"duration": 9097
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5540
"duration": 5676
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2782
"duration": 2795
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 54797
"duration": 51660
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 6914
},
{
"spec": "cypress/integration/rendering/sequencediagram-v2.spec.js",
"duration": 20481
"duration": 6957
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 38490
"duration": 36026
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 30766
"duration": 29551
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16705
"duration": 17364
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30928
"duration": 30209
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8424
"duration": 8699
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 12533
"duration": 12168
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 21197
"duration": 21453
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3455
"duration": 3577
}
]
}

View File

@@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es2020",
"lib": ["es2020", "dom"],
"types": ["cypress", "node", "@argos-ci/cypress/support"],
"types": ["cypress", "node", "@argos-ci/cypress/dist/support.d.ts"],
"allowImportingTsExtensions": true,
"noEmit": true
},

View File

@@ -11,7 +11,7 @@
rel="stylesheet"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
rel="stylesheet"
/>
<link

View File

@@ -29,8 +29,7 @@ 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

View File

@@ -33,8 +33,7 @@ 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:
@@ -48,8 +47,7 @@ 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).

View File

@@ -22,6 +22,7 @@ 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

View File

@@ -6,7 +6,7 @@
# Frequently Asked Questions
1. [How to add title to flowchart?](https://github.com/mermaid-js/mermaid/issues/1433#issuecomment-1991554712)
1. [How to add title to flowchart?](https://github.com/mermaid-js/mermaid/issues/556#issuecomment-363182217)
2. [How to specify custom CSS file?](https://github.com/mermaidjs/mermaid.cli/pull/24#issuecomment-373402785)
3. [How to fix tooltip misplacement issue?](https://github.com/mermaid-js/mermaid/issues/542#issuecomment-3343564621)
4. [How to specify gantt diagram xAxis format?](https://github.com/mermaid-js/mermaid/issues/269#issuecomment-373229136)

View File

@@ -1,40 +0,0 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/layouts.md](../../packages/mermaid/src/docs/config/layouts.md).
# Layouts
This page lists the available layout algorithms supported in Mermaid diagrams.
## Supported Layouts
- **elk**: [ELK (Eclipse Layout Kernel)](https://www.eclipse.org/elk/)
- **tidy-tree**: Tidy tree layout for hierarchical diagrams [Tidy Tree Configuration](/config/tidy-tree)
- **cose-bilkent**: Cose Bilkent layout for force-directed graphs
- **dagre**: Dagre layout for layered graphs
## How to Use
You can specify the layout in your diagram's YAML config or initialization options. For example:
```mermaid-example
---
config:
layout: elk
---
graph TD;
A-->B;
B-->C;
```
```mermaid
---
config:
layout: elk
---
graph TD;
A-->B;
B-->C;
```

View File

@@ -19,7 +19,6 @@
- [addDirective](functions/addDirective.md)
- [getConfig](functions/getConfig.md)
- [getSiteConfig](functions/getSiteConfig.md)
- [getUserDefinedConfig](functions/getUserDefinedConfig.md)
- [reset](functions/reset.md)
- [sanitize](functions/sanitize.md)
- [saveConfigFromInitialize](functions/saveConfigFromInitialize.md)

View File

@@ -1,19 +0,0 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/config/functions/getUserDefinedConfig.md](../../../../../packages/mermaid/src/docs/config/setup/config/functions/getUserDefinedConfig.md).
[**mermaid**](../../README.md)
---
# Function: getUserDefinedConfig()
> **getUserDefinedConfig**(): [`MermaidConfig`](../../mermaid/interfaces/MermaidConfig.md)
Defined in: [packages/mermaid/src/config.ts:252](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L252)
## Returns
[`MermaidConfig`](../../mermaid/interfaces/MermaidConfig.md)

View File

@@ -10,6 +10,10 @@
# mermaid
## Classes
- [UnknownDiagramError](classes/UnknownDiagramError.md)
## Interfaces
- [DetailedError](interfaces/DetailedError.md)
@@ -23,7 +27,6 @@
- [RenderOptions](interfaces/RenderOptions.md)
- [RenderResult](interfaces/RenderResult.md)
- [RunOptions](interfaces/RunOptions.md)
- [UnknownDiagramError](interfaces/UnknownDiagramError.md)
## Type Aliases

View File

@@ -0,0 +1,159 @@
> **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`

View File

@@ -10,7 +10,7 @@
# Interface: ExternalDiagramDefinition
Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L96)
Defined in: [packages/mermaid/src/diagram-api/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L94)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/me
> **detector**: `DiagramDetector`
Defined in: [packages/mermaid/src/diagram-api/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L98)
Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L96)
---
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:98](https://github.com/me
> **id**: `string`
Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L97)
Defined in: [packages/mermaid/src/diagram-api/types.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L95)
---
@@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/me
> **loader**: `DiagramLoader`
Defined in: [packages/mermaid/src/diagram-api/types.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L99)
Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L97)

View File

@@ -10,7 +10,7 @@
# Interface: LayoutData
Defined in: [packages/mermaid/src/rendering-util/types.ts:168](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L168)
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)
## Indexable
@@ -22,7 +22,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:168](https://github.co
> **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/rendering-util/types.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L171)
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)
---
@@ -30,7 +30,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:171](https://github.co
> **edges**: `Edge`\[]
Defined in: [packages/mermaid/src/rendering-util/types.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L170)
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)
---
@@ -38,4 +38,4 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:170](https://github.co
> **nodes**: `Node`\[]
Defined in: [packages/mermaid/src/rendering-util/types.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L169)
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)

View File

@@ -10,7 +10,7 @@
# Interface: LayoutLoaderDefinition
Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24)
Defined in: [packages/mermaid/src/rendering-util/render.ts:21](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L21)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.co
> `optional` **algorithm**: `string`
Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L27)
Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24)
---
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.co
> **loader**: `LayoutLoader`
Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L26)
Defined in: [packages/mermaid/src/rendering-util/render.ts:23](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L23)
---
@@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.co
> **name**: `string`
Defined in: [packages/mermaid/src/rendering-util/render.ts:25](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L25)
Defined in: [packages/mermaid/src/rendering-util/render.ts:22](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L22)

View File

@@ -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](#initialize).
**Deprecated**, please set configuration in [initialize](Mermaid.md#initialize).
##### nodes?
@@ -141,13 +141,13 @@ Called once for each rendered diagram's id.
#### Deprecated
Use [initialize](#initialize) and [run](#run) instead.
Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#run) instead.
Renders the mermaid diagrams
#### Deprecated
Use [initialize](#initialize) and [run](#run) instead.
Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#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,81 +184,73 @@ Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/
#### Deprecated
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.
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.
---
### 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.
@@ -340,7 +332,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)

View File

@@ -10,7 +10,7 @@
# Interface: ParseOptions
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:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L72)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mer
> `optional` **suppressErrors**: `boolean`
Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93)
Defined in: [packages/mermaid/src/types.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L77)
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
The `parseError` function will not be called.

View File

@@ -10,7 +10,7 @@
# Interface: ParseResult
Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L96)
Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mer
> **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/types.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L104)
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
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:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100)
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
The diagram type, e.g. 'flowchart', 'sequence', etc.

View File

@@ -10,7 +10,7 @@
# Interface: RenderOptions
Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L10)
Defined in: [packages/mermaid/src/rendering-util/render.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L7)
## Properties
@@ -18,4 +18,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.co
> `optional` **algorithm**: `string`
Defined in: [packages/mermaid/src/rendering-util/render.ts:11](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L11)
Defined in: [packages/mermaid/src/rendering-util/render.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L8)

View File

@@ -10,7 +10,7 @@
# Interface: RenderResult
Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L114)
Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/me
> `optional` **bindFunctions**: (`element`) => `void`
Defined in: [packages/mermaid/src/types.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L132)
Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116)
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:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L122)
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
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:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L118)
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
The svg code for the rendered graph.

View File

@@ -1,65 +0,0 @@
> **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`

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L128)
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)

View File

@@ -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:130](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L130)
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)

View File

@@ -1,89 +0,0 @@
> **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
The **tidy-tree** layout arranges nodes in a hierarchical, tree-like structure. It is especially useful for diagrams where parent-child relationships are important, such as mindmaps.
## Features
- Organizes nodes in a tidy, non-overlapping tree
- Ideal for mindmaps and hierarchical data
- Automatically adjusts spacing for readability
## Example Usage
```mermaid-example
---
config:
layout: tidy-tree
---
mindmap
root((mindmap is a long thing))
A
B
C
D
```
```mermaid
---
config:
layout: tidy-tree
---
mindmap
root((mindmap is a long thing))
A
B
C
D
```
```mermaid-example
---
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
```
```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
```
## Note
- Currently, tidy-tree is primarily supported for mindmap diagrams.

View File

@@ -29,6 +29,7 @@ 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)

View File

@@ -35,11 +35,13 @@ 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)

View File

@@ -6,18 +6,6 @@
# Blog
## [The Essential Guide to Mermaid Chart Plugin for VS Code \[08/2025\]](https://docs.mermaidchart.com/blog/posts/the-essential-guide-to-mermaid-chart-plugin-for-vs-code-08-2025)
9/9/2025 • 5 mins
Creating diagrams in VS Code has never been easier—the Mermaid VS Code plugin transforms text-based syntax into clear, professional visuals right inside your editor. With live previews, smart editing, and AI-powered diagram generation, it removes the friction from visualization so you can focus on coding.
## [How to Create Perfect Flowcharts Using AI in 2025 Step-by-Step Guide](https://docs.mermaidchart.com/blog/posts/how-to-create-perfect-flowcharts-using-ai-in-2025-step-by-step-guide)
7/22/2025 • 8 mins
In 2025, AI tools make creating flowcharts faster and easier than ever. With Mermaids AI flowchart generator, you can turn plain text into clear, accurate diagrams in seconds. This guide walks through how it works and how to get the most out of it for technical and business workflows alike.
## [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

View File

@@ -194,7 +194,7 @@ architecture-beta
## Icons
By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`.
Users can use any of the 200,000+ icons available in iconify.design, or add other custom icons, by [registering an icon pack](../config/icons.md).
Users can use any of the 200,000+ icons available in iconify.design, or [add custom icons](../config/icons.md).
After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack.

View File

@@ -139,6 +139,7 @@ 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)
@@ -152,6 +153,7 @@ 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
@@ -161,6 +163,7 @@ 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
@@ -169,15 +172,18 @@ 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

View File

@@ -135,6 +135,44 @@ erDiagram
"This **is** _Markdown_"
```
#### Optional Relationship Labels
Starting from Mermaid version 11.11.0, the relationship label in ER diagrams is optional. You can define relationships without specifying a label, and the diagram will render correctly.
For example, the following is valid:
```mermaid-example
erDiagram
BOOK }|..|{ AUTHOR
BOOK }|..|{ GENRE
AUTHOR }|..|{ GENRE
```
```mermaid
erDiagram
BOOK }|..|{ AUTHOR
BOOK }|..|{ GENRE
AUTHOR }|..|{ GENRE
```
This will show the relationships between the entities without any labels on the connecting lines.
You can still add a label if you want to describe the relationship:
```mermaid-example
erDiagram
BOOK }|..|{ AUTHOR : written_by
BOOK }|..|{ GENRE : categorized_as
AUTHOR }|..|{ GENRE : specializes_in
```
```mermaid
erDiagram
BOOK }|..|{ AUTHOR : written_by
BOOK }|..|{ GENRE : categorized_as
AUTHOR }|..|{ GENRE : specializes_in
```
### Relationship Syntax
The `relationship` part of each statement can be broken down into three sub-components:

View File

@@ -326,9 +326,7 @@ Below is a comprehensive list of the newly introduced shapes and their correspon
| **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` |
@@ -985,23 +983,11 @@ flowchart TD
- `b`
- **w**: The width of the image. If not defined, this will default to the natural width of the image.
- **h**: The height of the image. If not defined, this will default to the natural height of the image.
- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the width (`w`) accordingly to the height (`h`). If not defined, this will default to `off` Possible values are:
- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the height (`h`) accordingly to the width (`w`). If not defined, this will default to `off` Possible values are:
- `on`
- `off`
If you want to resize an image, but keep the same aspect ratio, set `h`, and set `constraint: on` to constrain the aspect ratio. E.g.
```mermaid-example
flowchart TD
%% My image with a constrained aspect ratio
A@{ img: "https://mermaid.js.org/favicon.svg", label: "My example image label", pos: "t", h: 60, constraint: "on" }
```
```mermaid
flowchart TD
%% My image with a constrained aspect ratio
A@{ img: "https://mermaid.js.org/favicon.svg", label: "My example image label", pos: "t", h: 60, constraint: "on" }
```
These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging.
## Links between nodes

View File

@@ -360,8 +360,7 @@ 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

View File

@@ -314,22 +314,3 @@ 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)

View File

@@ -74,126 +74,6 @@ sequenceDiagram
Bob->>Alice: Hi Alice
```
### Boundary
If you want to use the boundary symbol for a participant, use the JSON configuration syntax as shown below.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "boundary" }
participant Bob
Alice->>Bob: Request from boundary
Bob->>Alice: Response to boundary
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "boundary" }
participant Bob
Alice->>Bob: Request from boundary
Bob->>Alice: Response to boundary
```
### Control
If you want to use the control symbol for a participant, use the JSON configuration syntax as shown below.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "control" }
participant Bob
Alice->>Bob: Control request
Bob->>Alice: Control response
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "control" }
participant Bob
Alice->>Bob: Control request
Bob->>Alice: Control response
```
### Entity
If you want to use the entity symbol for a participant, use the JSON configuration syntax as shown below.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "entity" }
participant Bob
Alice->>Bob: Entity request
Bob->>Alice: Entity response
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "entity" }
participant Bob
Alice->>Bob: Entity request
Bob->>Alice: Entity response
```
### Database
If you want to use the database symbol for a participant, use the JSON configuration syntax as shown below.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "database" }
participant Bob
Alice->>Bob: DB query
Bob->>Alice: DB result
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "database" }
participant Bob
Alice->>Bob: DB query
Bob->>Alice: DB result
```
### Collections
If you want to use the collections symbol for a participant, use the JSON configuration syntax as shown below.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "collections" }
participant Bob
Alice->>Bob: Collections request
Bob->>Alice: Collections response
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "collections" }
participant Bob
Alice->>Bob: Collections request
Bob->>Alice: Collections response
```
### Queue
If you want to use the queue symbol for a participant, use the JSON configuration syntax as shown below.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "queue" }
participant Bob
Alice->>Bob: Queue message
Bob->>Alice: Queue response
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "queue" }
participant Bob
Alice->>Bob: Queue message
Bob->>Alice: Queue response
```
### Aliases
The actor can have a convenient identifier and a descriptive label.

View File

@@ -38,5 +38,3 @@ Each user journey is split into sections, these describe the part of the task
the user is trying to complete.
Tasks syntax is `Task name: <score>: <comma separated list of actors>`
Score is a number between 1 and 5, inclusive.

View File

@@ -138,7 +138,7 @@ xychart
## Chart Theme Variables
Themes for xychart reside inside the `xychart` attribute, allowing customization through the following syntax:
Themes for xychart resides inside xychart attribute so to set the variables use this syntax:
```yaml
---
@@ -163,52 +163,6 @@ config:
| yAxisLineColor | Color of the y-axis line |
| plotColorPalette | String of colors separated by comma e.g. "#f3456, #43445" |
### Setting Colors for Lines and Bars
To set the color for lines and bars, use the `plotColorPalette` parameter. Colors in the palette will correspond sequentially to the elements in your chart (e.g., first bar/line will use the first color specified in the palette).
```mermaid-example
---
config:
themeVariables:
xyChart:
plotColorPalette: '#000000, #0000FF, #00FF00, #FF0000'
---
xychart
title "Different Colors in xyChart"
x-axis "categoriesX" ["Category 1", "Category 2", "Category 3", "Category 4"]
y-axis "valuesY" 0 --> 50
%% Black line
line [10,20,30,40]
%% Blue bar
bar [20,30,25,35]
%% Green bar
bar [15,25,20,30]
%% Red line
line [5,15,25,35]
```
```mermaid
---
config:
themeVariables:
xyChart:
plotColorPalette: '#000000, #0000FF, #00FF00, #FF0000'
---
xychart
title "Different Colors in xyChart"
x-axis "categoriesX" ["Category 1", "Category 2", "Category 3", "Category 4"]
y-axis "valuesY" 0 --> 50
%% Black line
line [10,20,30,40]
%% Blue bar
bar [20,30,25,35]
%% Green bar
bar [15,25,20,30]
%% Red line
line [5,15,25,35]
```
## Example on config and theme
```mermaid-example

View File

@@ -17,7 +17,6 @@ export default tseslint.config(
...tseslint.configs.stylisticTypeChecked,
{
ignores: [
'**/*.d.ts',
'**/dist/',
'**/node_modules/',
'.git/',

View File

@@ -63,36 +63,36 @@
]
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.55.2",
"@argos-ci/cypress": "^6.1.1",
"@applitools/eyes-cypress": "^3.44.9",
"@argos-ci/cypress": "^5.0.2",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.7",
"@changesets/cli": "^2.27.12",
"@cspell/eslint-plugin": "^8.19.4",
"@cypress/code-coverage": "^3.14.6",
"@cypress/code-coverage": "^3.12.49",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.4",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@rollup/plugin-typescript": "^12.1.2",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/js-yaml": "^4.0.9",
"@types/jsdom": "^21.1.7",
"@types/lodash": "^4.17.20",
"@types/lodash": "^4.17.15",
"@types/mdast": "^4.0.4",
"@types/node": "^22.18.6",
"@types/node": "^22.13.5",
"@types/rollup-plugin-visualizer": "^5.0.3",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/spy": "^3.2.4",
"@vitest/ui": "^3.2.4",
"@vitest/coverage-v8": "^3.0.6",
"@vitest/spy": "^3.0.6",
"@vitest/ui": "^3.0.6",
"ajv": "^8.17.1",
"chokidar": "3.6.0",
"concurrently": "^9.2.1",
"concurrently": "^9.1.2",
"cors": "^2.8.5",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cspell": "^9.2.1",
"cypress": "^14.5.4",
"cspell": "^9.1.3",
"cypress": "^14.5.1",
"cypress-image-snapshot": "^4.0.1",
"cypress-split": "^1.24.23",
"esbuild": "^0.25.10",
"cypress-split": "^1.24.14",
"esbuild": "^0.25.0",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^4.3.0",
@@ -106,30 +106,30 @@
"eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^59.0.1",
"express": "^5.1.0",
"globals": "^16.4.0",
"globby": "^14.1.0",
"globals": "^16.0.0",
"globby": "^14.0.2",
"husky": "^9.1.7",
"jest": "^30.1.3",
"jest": "^30.0.4",
"jison": "^0.4.18",
"js-yaml": "^4.1.0",
"jsdom": "^26.1.0",
"langium-cli": "3.3.0",
"lint-staged": "^16.1.6",
"lint-staged": "^16.1.2",
"markdown-table": "^3.0.4",
"nyc": "^17.1.0",
"path-browserify": "^1.0.1",
"prettier": "^3.6.2",
"prettier-plugin-jsdoc": "^1.3.3",
"prettier": "^3.5.2",
"prettier-plugin-jsdoc": "^1.3.2",
"rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.3",
"start-server-and-test": "^2.1.2",
"start-server-and-test": "^2.0.10",
"tslib": "^2.8.1",
"tsx": "^4.20.5",
"tsx": "^4.7.3",
"typescript": "~5.7.3",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.7",
"vite": "^7.0.3",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.2.4"
"vitest": "^3.0.6"
},
"nyc": {
"report-dir": "coverage/cypress"

View File

@@ -3,7 +3,6 @@
"version": "1.0.0",
"description": "Mermaid examples package",
"author": "Sidharth Vinod",
"license": "MIT",
"type": "module",
"module": "./dist/mermaid-examples.core.mjs",
"types": "./dist/mermaid.d.ts",

View File

@@ -37,12 +37,12 @@
]
},
"dependencies": {
"@braintree/sanitize-url": "^7.1.1",
"@braintree/sanitize-url": "^7.0.4",
"d3": "^7.9.0",
"khroma": "^2.1.0"
},
"devDependencies": {
"concurrently": "^9.2.1",
"concurrently": "^9.1.2",
"mermaid": "workspace:*",
"rimraf": "^6.0.1"
},

View File

@@ -1,16 +1,5 @@
# @mermaid-js/layout-elk
## 0.2.0
### Minor Changes
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
### Patch Changes
- Updated dependencies [[`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800), [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20), [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05), [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8)]:
- mermaid@11.11.0
## 0.1.9
### Patch Changes

View File

@@ -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 -->

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/layout-elk",
"version": "0.2.0",
"version": "0.1.9",
"description": "ELK layout engine for mermaid",
"module": "dist/mermaid-layout-elk.core.mjs",
"types": "dist/layouts.d.ts",

View File

@@ -1,67 +0,0 @@
import { describe, it, expect } from 'vitest';
import {
intersection,
ensureTrulyOutside,
makeInsidePoint,
tryNodeIntersect,
replaceEndpoint,
type RectLike,
type P,
} from '../geometry.js';
const approx = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps;
describe('geometry helpers', () => {
it('intersection: vertical approach hits bottom border', () => {
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
const h = rect.height / 2; // 25
const outside: P = { x: 0, y: 100 };
const inside: P = { x: 0, y: 0 };
const res = intersection(rect, outside, inside);
expect(approx(res.x, 0)).toBe(true);
expect(approx(res.y, h)).toBe(true);
});
it('ensureTrulyOutside nudges near-boundary point outward', () => {
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
// near bottom boundary (y ~ h)
const near: P = { x: 0, y: rect.height / 2 - 0.2 };
const out = ensureTrulyOutside(rect, near, 10);
expect(out.y).toBeGreaterThan(rect.height / 2);
});
it('makeInsidePoint keeps x for vertical and y from center', () => {
const rect: RectLike = { x: 10, y: 5, width: 100, height: 50 };
const outside: P = { x: 10, y: 40 };
const center: P = { x: 99, y: -123 }; // center y should be used
const inside = makeInsidePoint(rect, outside, center);
expect(inside.x).toBe(outside.x);
expect(inside.y).toBe(center.y);
});
it('tryNodeIntersect returns null for wrong-side intersections', () => {
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
const outside: P = { x: -50, y: 0 };
const node = { intersect: () => ({ x: 10, y: 0 }) } as any; // right side of center
const res = tryNodeIntersect(node, rect, outside);
expect(res).toBeNull();
});
it('replaceEndpoint dedup removes end/start appropriately', () => {
const pts: P[] = [
{ x: 0, y: 0 },
{ x: 1, y: 1 },
];
// remove duplicate end
replaceEndpoint(pts, 'end', { x: 1, y: 1 });
expect(pts.length).toBe(1);
const pts2: P[] = [
{ x: 0, y: 0 },
{ x: 1, y: 1 },
];
// remove duplicate start
replaceEndpoint(pts2, 'start', { x: 0, y: 0 });
expect(pts2.length).toBe(1);
});
});

View File

@@ -1,9 +0,0 @@
export interface TreeData {
parentById: Record<string, string>;
childrenById: Record<string, string[]>;
}
export declare const findCommonAncestor: (
id1: string,
id2: string,
{ parentById }: TreeData
) => string;

View File

@@ -1,209 +0,0 @@
/* Geometry utilities extracted from render.ts for reuse and testing */
export interface P {
x: number;
y: number;
}
export interface RectLike {
x: number; // center x
y: number; // center y
width: number;
height: number;
padding?: number;
}
export interface NodeLike {
intersect?: (p: P) => P | null;
}
export const EPS = 1;
export const PUSH_OUT = 10;
export const onBorder = (bounds: RectLike, p: P, tol = 0.5): boolean => {
const halfW = bounds.width / 2;
const halfH = bounds.height / 2;
const left = bounds.x - halfW;
const right = bounds.x + halfW;
const top = bounds.y - halfH;
const bottom = bounds.y + halfH;
const onLeft = Math.abs(p.x - left) <= tol && p.y >= top - tol && p.y <= bottom + tol;
const onRight = Math.abs(p.x - right) <= tol && p.y >= top - tol && p.y <= bottom + tol;
const onTop = Math.abs(p.y - top) <= tol && p.x >= left - tol && p.x <= right + tol;
const onBottom = Math.abs(p.y - bottom) <= tol && p.x >= left - tol && p.x <= right + tol;
return onLeft || onRight || onTop || onBottom;
};
/**
* Compute intersection between a rectangle (center x/y, width/height) and the line
* segment from insidePoint -\> outsidePoint. Returns the point on the rectangle border.
*
* This version avoids snapping to outsidePoint when certain variables evaluate to 0
* (previously caused vertical top/bottom cases to miss the border). It only enforces
* axis-constant behavior for purely vertical/horizontal approaches.
*/
export const intersection = (node: RectLike, outsidePoint: P, insidePoint: P): P => {
const x = node.x;
const y = node.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,
};
// Keep axis-constant special-cases only
if (R === 0) {
res.x = outsidePoint.x;
}
if (Q === 0) {
res.y = outsidePoint.y;
}
return res;
} else {
// Intersection on sides of rect
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;
// Only handle axis-constant cases
if (R === 0) {
_x = outsidePoint.x;
}
if (Q === 0) {
_y = outsidePoint.y;
}
return { x: _x, y: _y };
}
};
export const outsideNode = (node: RectLike, point: P): boolean => {
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;
return dx >= w || dy >= h;
};
export const ensureTrulyOutside = (bounds: RectLike, p: P, push = PUSH_OUT): P => {
const dx = Math.abs(p.x - bounds.x);
const dy = Math.abs(p.y - bounds.y);
const w = bounds.width / 2;
const h = bounds.height / 2;
if (Math.abs(dx - w) < EPS || Math.abs(dy - h) < EPS) {
const dirX = p.x - bounds.x;
const dirY = p.y - bounds.y;
const len = Math.sqrt(dirX * dirX + dirY * dirY);
if (len > 0) {
return {
x: bounds.x + (dirX / len) * (len + push),
y: bounds.y + (dirY / len) * (len + push),
};
}
}
return p;
};
export const makeInsidePoint = (bounds: RectLike, outside: P, center: P): P => {
const isVertical = Math.abs(outside.x - bounds.x) < EPS;
const isHorizontal = Math.abs(outside.y - bounds.y) < EPS;
return {
x: isVertical
? outside.x
: outside.x < bounds.x
? bounds.x - bounds.width / 4
: bounds.x + bounds.width / 4,
y: isHorizontal ? outside.y : center.y,
};
};
export const tryNodeIntersect = (node: NodeLike, bounds: RectLike, outside: P): P | null => {
if (!node?.intersect) {
return null;
}
const res = node.intersect(outside);
if (!res) {
return null;
}
const wrongSide =
(outside.x < bounds.x && res.x > bounds.x) || (outside.x > bounds.x && res.x < bounds.x);
if (wrongSide) {
return null;
}
const dist = Math.hypot(outside.x - res.x, outside.y - res.y);
if (dist <= EPS) {
return null;
}
return res;
};
export const fallbackIntersection = (bounds: RectLike, outside: P, center: P): P => {
const inside = makeInsidePoint(bounds, outside, center);
return intersection(bounds, outside, inside);
};
export const computeNodeIntersection = (
node: NodeLike,
bounds: RectLike,
outside: P,
center: P
): P => {
const outside2 = ensureTrulyOutside(bounds, outside);
return tryNodeIntersect(node, bounds, outside2) ?? fallbackIntersection(bounds, outside2, center);
};
export const replaceEndpoint = (
points: P[],
which: 'start' | 'end',
value: P | null | undefined,
tol = 0.1
) => {
if (!value || points.length === 0) {
return;
}
if (which === 'start') {
if (
points.length > 0 &&
Math.abs(points[0].x - value.x) < tol &&
Math.abs(points[0].y - value.y) < tol
) {
// duplicate start remove it
points.shift();
} else {
points[0] = value;
}
} else {
const last = points.length - 1;
if (
points.length > 0 &&
Math.abs(points[last].x - value.x) < tol &&
Math.abs(points[last].y - value.y) < tol
) {
// duplicate end remove it
points.pop();
} else {
points[last] = value;
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,6 @@
"outDir": "./dist",
"types": ["vitest/importMeta", "vitest/globals"]
},
"include": ["./src/**/*.ts", "./src/**/*.d.ts"],
"include": ["./src/**/*.ts"],
"typeRoots": ["./src/types"]
}

View File

@@ -1,12 +0,0 @@
# @mermaid-js/layout-tidy-tree
## 0.2.0
### Minor Changes
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
### Patch Changes
- Updated dependencies [[`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800), [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20), [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05), [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8)]:
- mermaid@11.11.0

View File

@@ -1,59 +0,0 @@
# @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]

View File

@@ -1,46 +0,0 @@
{
"name": "@mermaid-js/layout-tidy-tree",
"version": "0.2.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"
]
}

View File

@@ -1,50 +0,0 @@
/**
* 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';

View File

@@ -1,409 +0,0 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { executeTidyTreeLayout, validateLayoutData } from './layout.js';
import type { LayoutResult } from './types.js';
import type { LayoutData, MermaidConfig } from 'mermaid';
// 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,
},
};
}),
})),
}));
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);
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);
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);
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)).rejects.toThrow(
'No nodes found in layout data'
);
});
it('should throw error for missing nodes', async () => {
const invalidData = { ...mockLayoutData, nodes: [] };
await expect(executeTidyTreeLayout(invalidData)).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);
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);
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);
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);
});
});
});

View File

@@ -1,629 +0,0 @@
import type { LayoutData } from 'mermaid';
import type { Bounds, Point } from 'mermaid/src/types.js';
import { BoundingBox, Layout } from 'non-layered-tidy-tree-layout';
import type {
Edge,
LayoutResult,
Node,
PositionedEdge,
PositionedNode,
TidyTreeNode,
} 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): 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;
if (leftTree) {
const leftLayoutResult = layout.layout(leftTree);
leftResult = leftLayoutResult.result;
}
if (rightTree) {
const rightLayoutResult = layout.layout(rightTree);
rightResult = rightLayoutResult.result;
}
const positionedNodes = combineAndPositionTrees(rootNode, leftResult, rightResult);
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
): 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: Bounds, lineStart: Point, lineEnd: Point): Point {
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: PositionedNode, outsidePoint: Point, insidePoint: Point): Point {
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;
}

View File

@@ -1,13 +0,0 @@
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;

View File

@@ -1,18 +0,0 @@
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;
};
};
}
}

View File

@@ -1,180 +0,0 @@
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);
// 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');
};

View File

@@ -1,69 +0,0 @@
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;
}

View File

@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"types": ["vitest/importMeta", "vitest/globals"]
},
"include": ["./src/**/*.ts", "./src/**/*.d.ts"],
"typeRoots": ["./src/types"]
}

View File

@@ -33,7 +33,7 @@
],
"license": "MIT",
"dependencies": {
"@zenuml/core": "^3.41.4"
"@zenuml/core": "^3.35.2"
},
"devDependencies": {
"mermaid": "workspace:^"

View File

@@ -1,19 +1,5 @@
# mermaid
## 11.11.0
### Minor Changes
- [#6704](https://github.com/mermaid-js/mermaid/pull/6704) [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05) Thanks [@omkarht](https://github.com/omkarht)! - feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
### Patch Changes
- [#6905](https://github.com/mermaid-js/mermaid/pull/6905) [`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Render newlines as spaces in class diagrams
- [#6886](https://github.com/mermaid-js/mermaid/pull/6886) [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Handle arrows correctly when auto number is enabled
## 11.10.0
### Minor Changes
@@ -168,6 +154,7 @@
### 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 ...)
@@ -186,12 +173,14 @@
### 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
@@ -240,6 +229,7 @@
- [#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:

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.11.0",
"version": "11.10.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",
@@ -67,29 +67,29 @@
]
},
"dependencies": {
"@braintree/sanitize-url": "^7.1.1",
"@iconify/utils": "^3.0.2",
"@braintree/sanitize-url": "^7.0.4",
"@iconify/utils": "^2.1.33",
"@mermaid-js/parser": "workspace:^",
"@types/d3": "^7.4.3",
"cytoscape": "^3.33.1",
"cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.2.0",
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.11",
"dayjs": "^1.11.18",
"dompurify": "^3.2.7",
"dayjs": "^1.11.13",
"dompurify": "^3.2.5",
"katex": "^0.16.22",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
"marked": "^16.3.0",
"marked": "^16.0.0",
"roughjs": "^4.6.6",
"stylis": "^4.3.6",
"ts-dedent": "^2.2.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.5",
"@adobe/jsonschema2md": "^8.0.2",
"@iconify/types": "^2.0.0",
"@types/cytoscape": "^3.21.9",
"@types/cytoscape-fcose": "^2.2.4",
@@ -105,30 +105,30 @@
"@types/stylis": "^4.2.7",
"@types/uuid": "^10.0.0",
"ajv": "^8.17.1",
"canvas": "^3.2.0",
"canvas": "^3.1.0",
"chokidar": "3.6.0",
"concurrently": "^9.2.1",
"concurrently": "^9.1.2",
"csstree-validator": "^4.0.1",
"globby": "^14.1.0",
"globby": "^14.0.2",
"jison": "^0.4.18",
"js-base64": "^3.7.8",
"js-base64": "^3.7.7",
"jsdom": "^26.1.0",
"json-schema-to-typescript": "^15.0.4",
"micromatch": "^4.0.8",
"path-browserify": "^1.0.1",
"prettier": "^3.6.2",
"prettier": "^3.5.2",
"remark": "^15.0.1",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"rimraf": "^6.0.1",
"start-server-and-test": "^2.1.2",
"type-fest": "^4.41.0",
"typedoc": "^0.28.13",
"typedoc-plugin-markdown": "^4.8.1",
"start-server-and-test": "^2.0.10",
"type-fest": "^4.35.0",
"typedoc": "^0.27.8",
"typedoc-plugin-markdown": "^4.4.2",
"typescript": "~5.7.3",
"unist-util-flatmap": "^1.0.0",
"unist-util-visit": "^5.0.0",
"vitepress": "^1.6.4",
"vitepress": "^1.0.2",
"vitepress-plugin-search": "1.0.4-alpha.22"
},
"files": [

View File

@@ -171,9 +171,7 @@ 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\` |

View File

@@ -78,187 +78,3 @@ describe('when working with site config', () => {
expect(config_4.altFontFamily).toBeUndefined();
});
});
describe('getUserDefinedConfig', () => {
beforeEach(() => {
configApi.reset();
});
it('should return empty object when no user config is defined', () => {
const userConfig = configApi.getUserDefinedConfig();
expect(userConfig).toEqual({});
});
it('should return config from initialize only', () => {
const initConfig: MermaidConfig = { theme: 'dark', fontFamily: 'Arial' };
configApi.saveConfigFromInitialize(initConfig);
const userConfig = configApi.getUserDefinedConfig();
expect(userConfig).toEqual(initConfig);
});
it('should return config from directives only', () => {
const directive1: MermaidConfig = { layout: 'elk', fontSize: 14 };
const directive2: MermaidConfig = { theme: 'forest' };
configApi.addDirective(directive1);
configApi.addDirective(directive2);
expect(configApi.getUserDefinedConfig()).toMatchInlineSnapshot(`
{
"fontFamily": "Arial",
"fontSize": 14,
"layout": "elk",
"theme": "forest",
}
`);
});
it('should combine initialize config and directives', () => {
const initConfig: MermaidConfig = { theme: 'dark', fontFamily: 'Arial', layout: 'dagre' };
const directive1: MermaidConfig = { layout: 'elk', fontSize: 14 };
const directive2: MermaidConfig = { theme: 'forest' };
configApi.saveConfigFromInitialize(initConfig);
configApi.addDirective(directive1);
configApi.addDirective(directive2);
const userConfig = configApi.getUserDefinedConfig();
expect(userConfig).toMatchInlineSnapshot(`
{
"fontFamily": "Arial",
"fontSize": 14,
"layout": "elk",
"theme": "forest",
}
`);
});
it('should handle nested config objects properly', () => {
const initConfig: MermaidConfig = {
flowchart: { nodeSpacing: 50, rankSpacing: 100 },
theme: 'default',
};
const directive: MermaidConfig = {
flowchart: { nodeSpacing: 75, curve: 'basis' },
mindmap: { padding: 20 },
};
configApi.saveConfigFromInitialize(initConfig);
configApi.addDirective(directive);
const userConfig = configApi.getUserDefinedConfig();
expect(userConfig).toMatchInlineSnapshot(`
{
"flowchart": {
"curve": "basis",
"nodeSpacing": 75,
"rankSpacing": 100,
},
"mindmap": {
"padding": 20,
},
"theme": "default",
}
`);
});
it('should handle complex nested overrides', () => {
const initConfig: MermaidConfig = {
flowchart: {
nodeSpacing: 50,
rankSpacing: 100,
curve: 'linear',
},
theme: 'default',
};
const directive1: MermaidConfig = {
flowchart: {
nodeSpacing: 75,
},
fontSize: 12,
};
const directive2: MermaidConfig = {
flowchart: {
curve: 'basis',
nodeSpacing: 100,
},
mindmap: {
padding: 15,
},
};
configApi.saveConfigFromInitialize(initConfig);
configApi.addDirective(directive1);
configApi.addDirective(directive2);
const userConfig = configApi.getUserDefinedConfig();
expect(userConfig).toMatchInlineSnapshot(`
{
"flowchart": {
"curve": "basis",
"nodeSpacing": 100,
"rankSpacing": 100,
},
"fontSize": 12,
"mindmap": {
"padding": 15,
},
"theme": "default",
}
`);
});
it('should return independent copies (not references)', () => {
const initConfig: MermaidConfig = { theme: 'dark', flowchart: { nodeSpacing: 50 } };
configApi.saveConfigFromInitialize(initConfig);
const userConfig1 = configApi.getUserDefinedConfig();
const userConfig2 = configApi.getUserDefinedConfig();
userConfig1.theme = 'neutral';
userConfig1.flowchart!.nodeSpacing = 999;
expect(userConfig2).toMatchInlineSnapshot(`
{
"flowchart": {
"nodeSpacing": 50,
},
"theme": "dark",
}
`);
});
it('should handle edge cases with undefined values', () => {
const initConfig: MermaidConfig = { theme: 'dark', layout: undefined };
const directive: MermaidConfig = { fontSize: 14, fontFamily: undefined };
configApi.saveConfigFromInitialize(initConfig);
configApi.addDirective(directive);
expect(configApi.getUserDefinedConfig()).toMatchInlineSnapshot(`
{
"fontSize": 14,
"layout": undefined,
"theme": "dark",
}
`);
});
it('should retain config from initialize after reset', () => {
const initConfig: MermaidConfig = { theme: 'dark' };
const directive: MermaidConfig = { layout: 'elk' };
configApi.saveConfigFromInitialize(initConfig);
configApi.addDirective(directive);
expect(configApi.getUserDefinedConfig()).toMatchInlineSnapshot(`
{
"layout": "elk",
"theme": "dark",
}
`);
configApi.reset();
});
});

View File

@@ -248,17 +248,3 @@ const checkConfig = (config: MermaidConfig) => {
issueWarning('LAZY_LOAD_DEPRECATED');
}
};
export const getUserDefinedConfig = (): MermaidConfig => {
let userConfig: MermaidConfig = {};
if (configFromInitialize) {
userConfig = assignWithDepth(userConfig, configFromInitialize);
}
for (const d of directives) {
userConfig = assignWithDepth(userConfig, d);
}
return userConfig;
};

View File

@@ -1075,10 +1075,6 @@ 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

Some files were not shown because too many files have changed in this diff Show More