mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-04 08:06:43 +02:00
Compare commits
54 Commits
renovate/p
...
4743-timel
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ea8260aa51 | ||
![]() |
6d9fad01a9 | ||
![]() |
075e1b5e1f | ||
![]() |
3c9bd7be29 | ||
![]() |
6995248443 | ||
![]() |
93467a6fce | ||
![]() |
95d48e3497 | ||
![]() |
e438e035bc | ||
![]() |
2bc5b6d2fa | ||
![]() |
2cfebef122 | ||
![]() |
dabc220ed2 | ||
![]() |
7bdcf93412 | ||
![]() |
d86e46b705 | ||
![]() |
71e09bcaef | ||
![]() |
cba659d097 | ||
![]() |
9f6ee53382 | ||
![]() |
3248bf3da4 | ||
![]() |
e7a7ff8a2a | ||
![]() |
68fc68c239 | ||
![]() |
769b362005 | ||
![]() |
e4d3aa4610 | ||
![]() |
716548548a | ||
![]() |
4bece53a3c | ||
![]() |
297be4a868 | ||
![]() |
fb6ace73b5 | ||
![]() |
bf362673fc | ||
![]() |
d042b21b12 | ||
![]() |
677ff82d13 | ||
![]() |
981829a426 | ||
![]() |
327a5aa9fd | ||
![]() |
848f69a75c | ||
![]() |
99dbeba407 | ||
![]() |
d525acc05b | ||
![]() |
4915545429 | ||
![]() |
334fe87bc6 | ||
![]() |
283e7810d2 | ||
![]() |
237d01d510 | ||
![]() |
d2c5cbd408 | ||
![]() |
20467bcbe6 | ||
![]() |
afeb761296 | ||
![]() |
3abcfbb8d2 | ||
![]() |
ee82694645 | ||
![]() |
012530e98e | ||
![]() |
a4a27611dd | ||
![]() |
5055ade44e | ||
![]() |
b61bec8faf | ||
![]() |
dd213fe86c | ||
![]() |
76d073b027 | ||
![]() |
cc476d59d1 | ||
![]() |
800f23fc01 | ||
![]() |
67aa1a4dc1 | ||
![]() |
c1bcdcfbad | ||
![]() |
f528e2daa4 | ||
![]() |
a867842f32 |
5
.changeset/hungry-baths-glow.md
Normal file
5
.changeset/hungry-baths-glow.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
|
5
.changeset/ninety-adults-wink.md
Normal file
5
.changeset/ninety-adults-wink.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': major
|
||||||
|
---
|
||||||
|
|
||||||
|
Currently, HTML tags such as <em>, <strong>, <sup>, <a>, <ul>, and <li> are supported in Flowchart and Class diagram labels but not in Timeline diagrams. This change introduces support for basic HTML formatting in Timeline labels, enabling richer text formatting and better usability for multi-line content like descriptions, footnotes, and styled annotations
|
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||||
with:
|
with:
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- 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.
|
# ℹ️ 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
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -62,4 +62,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||||
|
2
.github/workflows/e2e-applitools.yml
vendored
2
.github/workflows/e2e-applitools.yml
vendored
@@ -56,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"
|
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
|
- name: Cypress run
|
||||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||||
id: cypress
|
id: cypress
|
||||||
with:
|
with:
|
||||||
start: pnpm run dev
|
start: pnpm run dev
|
||||||
|
4
.github/workflows/e2e-timings.yml
vendored
4
.github/workflows/e2e-timings.yml
vendored
@@ -27,12 +27,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||||
with:
|
with:
|
||||||
runTests: false
|
runTests: false
|
||||||
|
|
||||||
- name: Cypress run
|
- name: Cypress run
|
||||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||||
id: cypress
|
id: cypress
|
||||||
with:
|
with:
|
||||||
install: false
|
install: false
|
||||||
|
10
.github/workflows/e2e.yml
vendored
10
.github/workflows/e2e.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
- name: Cache snapshots
|
- name: Cache snapshots
|
||||||
id: cache-snapshot
|
id: cache-snapshot
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||||
with:
|
with:
|
||||||
path: ./cypress/snapshots
|
path: ./cypress/snapshots
|
||||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
|
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:
|
with:
|
||||||
# just perform install
|
# just perform install
|
||||||
runTests: false
|
runTests: false
|
||||||
@@ -95,13 +95,13 @@ jobs:
|
|||||||
# These cached snapshots are downloaded, providing the reference snapshots.
|
# These cached snapshots are downloaded, providing the reference snapshots.
|
||||||
- name: Cache snapshots
|
- name: Cache snapshots
|
||||||
id: cache-snapshot
|
id: cache-snapshot
|
||||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||||
with:
|
with:
|
||||||
path: ./cypress/snapshots
|
path: ./cypress/snapshots
|
||||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||||
with:
|
with:
|
||||||
runTests: false
|
runTests: false
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ jobs:
|
|||||||
# Install NPM dependencies, cache them correctly
|
# Install NPM dependencies, cache them correctly
|
||||||
# and run all Cypress tests
|
# and run all Cypress tests
|
||||||
- name: Cypress run
|
- name: Cypress run
|
||||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||||
id: cypress
|
id: cypress
|
||||||
with:
|
with:
|
||||||
install: false
|
install: false
|
||||||
|
2
.github/workflows/link-checker.yml
vendored
2
.github/workflows/link-checker.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Restore lychee cache
|
- name: Restore lychee cache
|
||||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||||
with:
|
with:
|
||||||
path: .lycheecache
|
path: .lycheecache
|
||||||
key: cache-lychee-${{ github.sha }}
|
key: cache-lychee-${{ github.sha }}
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
- name: Create Release Pull Request or Publish to npm
|
||||||
id: changesets
|
id: changesets
|
||||||
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
|
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
|
||||||
with:
|
with:
|
||||||
version: pnpm changeset:version
|
version: pnpm changeset:version
|
||||||
publish: pnpm changeset:publish
|
publish: pnpm changeset:publish
|
||||||
|
6
.github/workflows/scorecard.yml
vendored
6
.github/workflows/scorecard.yml
vendored
@@ -20,18 +20,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run analysis
|
- name: Run analysis
|
||||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
||||||
with:
|
with:
|
||||||
results_file: results.sarif
|
results_file: results.sarif
|
||||||
results_format: sarif
|
results_format: sarif
|
||||||
publish_results: true
|
publish_results: true
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
retention-days: 5
|
retention-days: 5
|
||||||
- name: Upload to code-scanning
|
- 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:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
2
.github/workflows/update-browserlist.yml
vendored
2
.github/workflows/update-browserlist.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
message: 'chore: update browsers list'
|
message: 'chore: update browsers list'
|
||||||
push: false
|
push: false
|
||||||
- name: Create Pull Request
|
- 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:
|
with:
|
||||||
branch: update-browserslist
|
branch: update-browserslist
|
||||||
title: Update Browserslist
|
title: Update Browserslist
|
||||||
|
659
cypress/integration/rendering/sequencediagram-v2.spec.js
Normal file
659
cypress/integration/rendering/sequencediagram-v2.spec.js
Normal file
@@ -0,0 +1,659 @@
|
|||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -225,4 +225,24 @@ timeline
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('13: should render markdown htmlLabels', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`---
|
||||||
|
config:
|
||||||
|
theme: forest
|
||||||
|
---
|
||||||
|
|
||||||
|
timeline
|
||||||
|
title Timeline of Industrial Revolution
|
||||||
|
section 17th-20th century
|
||||||
|
Industry 1.0 : Machinery, Water power, Steam <br>power
|
||||||
|
Industry 2.0 : Electricity, <strong>Internal combustion engine </strong>, Mass production
|
||||||
|
Industry 3.0 : Electronics, Computers, Automation
|
||||||
|
section 21st century
|
||||||
|
Industry 4.0 : Internet, Robotics, Internet of Things
|
||||||
|
Industry 5.0 : Artificial intelligence, Big data, 3D printing
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link
|
<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"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
|
@@ -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.
|
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**
|
> **💡 Tip** > [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
||||||
> [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone git@github.com/your-fork/mermaid
|
git clone git@github.com/your-fork/mermaid
|
||||||
|
@@ -33,8 +33,7 @@ mindmap
|
|||||||
|
|
||||||
## Join the Development
|
## Join the Development
|
||||||
|
|
||||||
> **💡 Tip**
|
> **💡 Tip** > **Check out our** [**detailed contribution guide**](./contributing.md).
|
||||||
> **Check out our** [**detailed contribution guide**](./contributing.md).
|
|
||||||
|
|
||||||
Where to start:
|
Where to start:
|
||||||
|
|
||||||
@@ -48,8 +47,7 @@ Where to start:
|
|||||||
|
|
||||||
## A Question Or a Suggestion?
|
## A Question Or a Suggestion?
|
||||||
|
|
||||||
> **💡 Tip**
|
> **💡 Tip** > **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
|
||||||
> **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).
|
If you have faced a vulnerability [report it to us](./security.md).
|
||||||
|
|
||||||
|
@@ -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.
|
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:
|
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
|
- theme
|
||||||
- fontFamily
|
- fontFamily
|
||||||
- logLevel
|
- logLevel
|
||||||
|
@@ -88,103 +88,13 @@ Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### stackTraceLimit
|
### prepareStackTrace()?
|
||||||
|
|
||||||
> `static` **stackTraceLimit**: `number`
|
> `static` `optional` **prepareStackTrace**: (`err`, `stackTraces`) => `any`
|
||||||
|
|
||||||
Defined in: node_modules/.pnpm/@types+node\@22.17.2/node_modules/@types/node/globals.d.ts:161
|
Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:143
|
||||||
|
|
||||||
The `Error.stackTraceLimit` property specifies the number of stack frames
|
Optional override for formatting stack traces
|
||||||
collected by a stack trace (whether generated by `new Error().stack` or
|
|
||||||
`Error.captureStackTrace(obj)`).
|
|
||||||
|
|
||||||
The default value is `10` but may be set to any valid JavaScript number. Changes
|
|
||||||
will affect any stack trace captured _after_ the value has been changed.
|
|
||||||
|
|
||||||
If set to a non-number value, or set to a negative number, stack traces will
|
|
||||||
not capture any frames.
|
|
||||||
|
|
||||||
#### Inherited from
|
|
||||||
|
|
||||||
`Error.stackTraceLimit`
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### captureStackTrace()
|
|
||||||
|
|
||||||
> `static` **captureStackTrace**(`targetObject`, `constructorOpt`?): `void`
|
|
||||||
|
|
||||||
Defined in: node_modules/.pnpm/@types+node\@22.17.2/node_modules/@types/node/globals.d.ts:145
|
|
||||||
|
|
||||||
Creates a `.stack` property on `targetObject`, which when accessed returns
|
|
||||||
a string representing the location in the code at which
|
|
||||||
`Error.captureStackTrace()` was called.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const myObject = {};
|
|
||||||
Error.captureStackTrace(myObject);
|
|
||||||
myObject.stack; // Similar to `new Error().stack`
|
|
||||||
```
|
|
||||||
|
|
||||||
The first line of the trace will be prefixed with
|
|
||||||
`${myObject.name}: ${myObject.message}`.
|
|
||||||
|
|
||||||
The optional `constructorOpt` argument accepts a function. If given, all frames
|
|
||||||
above `constructorOpt`, including `constructorOpt`, will be omitted from the
|
|
||||||
generated stack trace.
|
|
||||||
|
|
||||||
The `constructorOpt` argument is useful for hiding implementation
|
|
||||||
details of error generation from the user. For instance:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function a() {
|
|
||||||
b();
|
|
||||||
}
|
|
||||||
|
|
||||||
function b() {
|
|
||||||
c();
|
|
||||||
}
|
|
||||||
|
|
||||||
function c() {
|
|
||||||
// Create an error without stack trace to avoid calculating the stack trace twice.
|
|
||||||
const { stackTraceLimit } = Error;
|
|
||||||
Error.stackTraceLimit = 0;
|
|
||||||
const error = new Error();
|
|
||||||
Error.stackTraceLimit = stackTraceLimit;
|
|
||||||
|
|
||||||
// Capture the stack trace above function b
|
|
||||||
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
a();
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
|
|
||||||
##### targetObject
|
|
||||||
|
|
||||||
`object`
|
|
||||||
|
|
||||||
##### constructorOpt?
|
|
||||||
|
|
||||||
`Function`
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
`void`
|
|
||||||
|
|
||||||
#### Inherited from
|
|
||||||
|
|
||||||
`Error.captureStackTrace`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### prepareStackTrace()
|
|
||||||
|
|
||||||
> `static` **prepareStackTrace**(`err`, `stackTraces`): `any`
|
|
||||||
|
|
||||||
Defined in: node_modules/.pnpm/@types+node\@22.17.2/node_modules/@types/node/globals.d.ts:149
|
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
@@ -207,3 +117,43 @@ Defined in: node_modules/.pnpm/@types+node\@22.17.2/node_modules/@types/node/glo
|
|||||||
#### Inherited from
|
#### Inherited from
|
||||||
|
|
||||||
`Error.prepareStackTrace`
|
`Error.prepareStackTrace`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### stackTraceLimit
|
||||||
|
|
||||||
|
> `static` **stackTraceLimit**: `number`
|
||||||
|
|
||||||
|
Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:145
|
||||||
|
|
||||||
|
#### Inherited from
|
||||||
|
|
||||||
|
`Error.stackTraceLimit`
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### captureStackTrace()
|
||||||
|
|
||||||
|
> `static` **captureStackTrace**(`targetObject`, `constructorOpt`?): `void`
|
||||||
|
|
||||||
|
Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:136
|
||||||
|
|
||||||
|
Create .stack property on a target object
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
##### targetObject
|
||||||
|
|
||||||
|
`object`
|
||||||
|
|
||||||
|
##### constructorOpt?
|
||||||
|
|
||||||
|
`Function`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`void`
|
||||||
|
|
||||||
|
#### Inherited from
|
||||||
|
|
||||||
|
`Error.captureStackTrace`
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: ParseOptions
|
# Interface: ParseOptions
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L72)
|
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mer
|
|||||||
|
|
||||||
> `optional` **suppressErrors**: `boolean`
|
> `optional` **suppressErrors**: `boolean`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L77)
|
Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
|
||||||
|
|
||||||
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
|
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
|
||||||
The `parseError` function will not be called.
|
The `parseError` function will not be called.
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: ParseResult
|
# Interface: ParseResult
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
|
Defined in: [packages/mermaid/src/types.ts:92](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L92)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mer
|
|||||||
|
|
||||||
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
> **config**: [`MermaidConfig`](MermaidConfig.md)
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
|
Defined in: [packages/mermaid/src/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100)
|
||||||
|
|
||||||
The config passed as YAML frontmatter or directives
|
The config passed as YAML frontmatter or directives
|
||||||
|
|
||||||
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
|
|||||||
|
|
||||||
> **diagramType**: `string`
|
> **diagramType**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
|
Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L96)
|
||||||
|
|
||||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: RenderResult
|
# Interface: RenderResult
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
|
Defined in: [packages/mermaid/src/types.ts:110](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L110)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mer
|
|||||||
|
|
||||||
> `optional` **bindFunctions**: (`element`) => `void`
|
> `optional` **bindFunctions**: (`element`) => `void`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116)
|
Defined in: [packages/mermaid/src/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L128)
|
||||||
|
|
||||||
Bind function to be called after the svg has been inserted into the DOM.
|
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.
|
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`
|
> **diagramType**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
|
Defined in: [packages/mermaid/src/types.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L118)
|
||||||
|
|
||||||
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||||
|
|
||||||
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
|||||||
|
|
||||||
> **svg**: `string`
|
> **svg**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
|
Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L114)
|
||||||
|
|
||||||
The svg code for the rendered graph.
|
The svg code for the rendered graph.
|
||||||
|
@@ -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.
|
- **Plugins** - A plugin system for extending the functionality of Mermaid.
|
||||||
|
|
||||||
Official Mermaid Chart plugins:
|
Official Mermaid Chart plugins:
|
||||||
|
|
||||||
- [Mermaid Chart GPT](https://chatgpt.com/g/g-684cc36f30208191b21383b88650a45d-mermaid-chart-diagrams-and-charts)
|
- [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)
|
- [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)
|
- [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud)
|
||||||
|
@@ -35,11 +35,13 @@ The Mermaid Chart team is excited to introduce a new Visual Editor for Flowchart
|
|||||||
Learn more:
|
Learn more:
|
||||||
|
|
||||||
- Visual Editor For Flowcharts
|
- Visual Editor For Flowcharts
|
||||||
|
|
||||||
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-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)
|
- [Demo video](https://www.youtube.com/watch?v=5aja0gijoO0)
|
||||||
|
|
||||||
- Visual Editor For Sequence diagrams
|
- Visual Editor For Sequence diagrams
|
||||||
|
|
||||||
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-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)
|
- [Demo video](https://youtu.be/imc2u5_N6Dc)
|
||||||
|
@@ -139,6 +139,7 @@ The following unfinished features are not supported in the short term.
|
|||||||
- [ ] Legend
|
- [ ] Legend
|
||||||
|
|
||||||
- [x] System Context
|
- [x] System Context
|
||||||
|
|
||||||
- [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
|
- [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] Person_Ext
|
- [x] Person_Ext
|
||||||
- [x] System(alias, label, ?descr, ?sprite, ?tags, $link)
|
- [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] System_Boundary
|
||||||
|
|
||||||
- [x] Container diagram
|
- [x] Container diagram
|
||||||
|
|
||||||
- [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
- [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] ContainerDb
|
- [x] ContainerDb
|
||||||
- [x] ContainerQueue
|
- [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] Container_Boundary(alias, label, ?tags, $link)
|
||||||
|
|
||||||
- [x] Component diagram
|
- [x] Component diagram
|
||||||
|
|
||||||
- [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
- [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] ComponentDb
|
- [x] ComponentDb
|
||||||
- [x] ComponentQueue
|
- [x] ComponentQueue
|
||||||
@@ -169,15 +172,18 @@ The following unfinished features are not supported in the short term.
|
|||||||
- [x] ComponentQueue_Ext
|
- [x] ComponentQueue_Ext
|
||||||
|
|
||||||
- [x] Dynamic diagram
|
- [x] Dynamic diagram
|
||||||
|
|
||||||
- [x] RelIndex(index, from, to, label, ?tags, $link)
|
- [x] RelIndex(index, from, to, label, ?tags, $link)
|
||||||
|
|
||||||
- [x] Deployment diagram
|
- [x] Deployment diagram
|
||||||
|
|
||||||
- [x] Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link)
|
- [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(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_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] Node_R(alias, label, ?type, ?descr, ?sprite, ?tags, $link): right aligned Node()
|
||||||
|
|
||||||
- [x] Relationship Types
|
- [x] Relationship Types
|
||||||
|
|
||||||
- [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
- [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] BiRel (bidirectional relationship)
|
- [x] BiRel (bidirectional relationship)
|
||||||
- [x] Rel_U, Rel_Up
|
- [x] Rel_U, Rel_Up
|
||||||
|
@@ -983,11 +983,23 @@ flowchart TD
|
|||||||
- `b`
|
- `b`
|
||||||
- **w**: The width of the image. If not defined, this will default to the natural width of the image.
|
- **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.
|
- **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 height (`h`) accordingly to the width (`w`). 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 width (`w`) accordingly to the height (`h`). If not defined, this will default to `off` Possible values are:
|
||||||
- `on`
|
- `on`
|
||||||
- `off`
|
- `off`
|
||||||
|
|
||||||
These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging.
|
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" }
|
||||||
|
```
|
||||||
|
|
||||||
## Links between nodes
|
## Links between nodes
|
||||||
|
|
||||||
|
@@ -360,8 +360,7 @@ gantt
|
|||||||
weekday monday
|
weekday monday
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Warning**
|
> **Warning** > `millisecond` and `second` support was added in v10.3.0
|
||||||
> `millisecond` and `second` support was added in v10.3.0
|
|
||||||
|
|
||||||
## Output in compact mode
|
## Output in compact mode
|
||||||
|
|
||||||
|
@@ -74,6 +74,126 @@ sequenceDiagram
|
|||||||
Bob->>Alice: Hi Alice
|
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
|
### Aliases
|
||||||
|
|
||||||
The actor can have a convenient identifier and a descriptive label.
|
The actor can have a convenient identifier and a descriptive label.
|
||||||
|
42
package.json
42
package.json
@@ -64,35 +64,35 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@applitools/eyes-cypress": "^3.44.9",
|
"@applitools/eyes-cypress": "^3.44.9",
|
||||||
"@argos-ci/cypress": "^5.0.7",
|
"@argos-ci/cypress": "^5.0.2",
|
||||||
"@changesets/changelog-github": "^0.5.1",
|
"@changesets/changelog-github": "^0.5.1",
|
||||||
"@changesets/cli": "^2.27.12",
|
"@changesets/cli": "^2.27.12",
|
||||||
"@cspell/eslint-plugin": "^8.19.4",
|
"@cspell/eslint-plugin": "^8.19.4",
|
||||||
"@cypress/code-coverage": "^3.12.49",
|
"@cypress/code-coverage": "^3.12.49",
|
||||||
"@eslint/js": "^9.26.0",
|
"@eslint/js": "^9.26.0",
|
||||||
"@rollup/plugin-typescript": "^12.1.4",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/cors": "^2.8.19",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/lodash": "^4.17.20",
|
"@types/lodash": "^4.17.15",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"@types/node": "^22.13.17",
|
"@types/node": "^22.13.5",
|
||||||
"@types/rollup-plugin-visualizer": "^5.0.3",
|
"@types/rollup-plugin-visualizer": "^5.0.3",
|
||||||
"@vitest/coverage-v8": "^3.0.9",
|
"@vitest/coverage-v8": "^3.0.6",
|
||||||
"@vitest/spy": "^3.0.9",
|
"@vitest/spy": "^3.0.6",
|
||||||
"@vitest/ui": "^3.0.9",
|
"@vitest/ui": "^3.0.6",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"chokidar": "3.6.0",
|
"chokidar": "3.6.0",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cpy-cli": "^5.0.0",
|
"cpy-cli": "^5.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cspell": "^9.1.5",
|
"cspell": "^9.1.3",
|
||||||
"cypress": "^14.5.4",
|
"cypress": "^14.5.1",
|
||||||
"cypress-image-snapshot": "^4.0.1",
|
"cypress-image-snapshot": "^4.0.1",
|
||||||
"cypress-split": "^1.24.21",
|
"cypress-split": "^1.24.14",
|
||||||
"esbuild": "^0.25.9",
|
"esbuild": "^0.25.0",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-cypress": "^4.3.0",
|
"eslint-plugin-cypress": "^4.3.0",
|
||||||
@@ -107,29 +107,29 @@
|
|||||||
"eslint-plugin-unicorn": "^59.0.1",
|
"eslint-plugin-unicorn": "^59.0.1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"globby": "^14.1.0",
|
"globby": "^14.0.2",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"jest": "^30.0.5",
|
"jest": "^30.0.4",
|
||||||
"jison": "^0.4.18",
|
"jison": "^0.4.18",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"langium-cli": "3.3.0",
|
"langium-cli": "3.3.0",
|
||||||
"lint-staged": "^16.1.5",
|
"lint-staged": "^16.1.2",
|
||||||
"markdown-table": "^3.0.4",
|
"markdown-table": "^3.0.4",
|
||||||
"nyc": "^17.1.0",
|
"nyc": "^17.1.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.2",
|
||||||
"prettier-plugin-jsdoc": "^1.3.3",
|
"prettier-plugin-jsdoc": "^1.3.2",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"rollup-plugin-visualizer": "^6.0.3",
|
"rollup-plugin-visualizer": "^6.0.3",
|
||||||
"start-server-and-test": "^2.0.13",
|
"start-server-and-test": "^2.0.10",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"tsx": "^4.7.3",
|
"tsx": "^4.7.3",
|
||||||
"typescript": "~5.7.3",
|
"typescript": "~5.7.3",
|
||||||
"typescript-eslint": "^8.38.0",
|
"typescript-eslint": "^8.38.0",
|
||||||
"vite": "^7.0.6",
|
"vite": "^7.0.3",
|
||||||
"vite-plugin-istanbul": "^7.0.0",
|
"vite-plugin-istanbul": "^7.0.0",
|
||||||
"vitest": "^3.0.9"
|
"vitest": "^3.0.6"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"report-dir": "coverage/cypress"
|
"report-dir": "coverage/cypress"
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^7.1.1",
|
"@braintree/sanitize-url": "^7.0.4",
|
||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
"khroma": "^2.1.0"
|
"khroma": "^2.1.0"
|
||||||
},
|
},
|
||||||
|
@@ -154,6 +154,7 @@
|
|||||||
### Minor Changes
|
### 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:
|
- [#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
|
- Config
|
||||||
- Init directive (%%{ init: { 'flowchart': { 'curve': '...' } } }%%)
|
- Init directive (%%{ init: { 'flowchart': { 'curve': '...' } } }%%)
|
||||||
- LinkStyle command (linkStyle default interpolate ...)
|
- LinkStyle command (linkStyle default interpolate ...)
|
||||||
@@ -172,12 +173,14 @@
|
|||||||
### Minor Changes
|
### 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
|
- [#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 `&`
|
- Incorrect label mapping for nodes when using `&`
|
||||||
- Syntax error when `}` with trailing spaces before new line
|
- 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
|
- [#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
|
- [#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 directions
|
||||||
- Added support for hand drawn look
|
- Added support for hand drawn look
|
||||||
|
|
||||||
@@ -226,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
|
- [#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:
|
- [#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.
|
- Updates the class diagram to the new unified way of rendering.
|
||||||
- Includes a new "classBox" shape to be used in diagrams
|
- Includes a new "classBox" shape to be used in diagrams
|
||||||
- Other updates such as:
|
- Other updates such as:
|
||||||
|
@@ -67,8 +67,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^7.1.1",
|
"@braintree/sanitize-url": "^7.0.4",
|
||||||
"@iconify/utils": "^2.3.0",
|
"@iconify/utils": "^2.1.33",
|
||||||
"@mermaid-js/parser": "workspace:^",
|
"@mermaid-js/parser": "workspace:^",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"cytoscape": "^3.29.3",
|
"cytoscape": "^3.29.3",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@adobe/jsonschema2md": "^8.0.5",
|
"@adobe/jsonschema2md": "^8.0.2",
|
||||||
"@iconify/types": "^2.0.0",
|
"@iconify/types": "^2.0.0",
|
||||||
"@types/cytoscape": "^3.21.9",
|
"@types/cytoscape": "^3.21.9",
|
||||||
"@types/cytoscape-fcose": "^2.2.4",
|
"@types/cytoscape-fcose": "^2.2.4",
|
||||||
@@ -105,30 +105,30 @@
|
|||||||
"@types/stylis": "^4.2.7",
|
"@types/stylis": "^4.2.7",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"canvas": "^3.1.2",
|
"canvas": "^3.1.0",
|
||||||
"chokidar": "3.6.0",
|
"chokidar": "3.6.0",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"csstree-validator": "^4.0.1",
|
"csstree-validator": "^4.0.1",
|
||||||
"globby": "^14.1.0",
|
"globby": "^14.0.2",
|
||||||
"jison": "^0.4.18",
|
"jison": "^0.4.18",
|
||||||
"js-base64": "^3.7.8",
|
"js-base64": "^3.7.7",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"json-schema-to-typescript": "^15.0.4",
|
"json-schema-to-typescript": "^15.0.4",
|
||||||
"micromatch": "^4.0.8",
|
"micromatch": "^4.0.8",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.2",
|
||||||
"remark": "^15.0.1",
|
"remark": "^15.0.1",
|
||||||
"remark-frontmatter": "^5.0.0",
|
"remark-frontmatter": "^5.0.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"start-server-and-test": "^2.0.13",
|
"start-server-and-test": "^2.0.10",
|
||||||
"type-fest": "^4.35.0",
|
"type-fest": "^4.35.0",
|
||||||
"typedoc": "^0.27.9",
|
"typedoc": "^0.27.8",
|
||||||
"typedoc-plugin-markdown": "^4.4.2",
|
"typedoc-plugin-markdown": "^4.4.2",
|
||||||
"typescript": "~5.7.3",
|
"typescript": "~5.7.3",
|
||||||
"unist-util-flatmap": "^1.0.0",
|
"unist-util-flatmap": "^1.0.0",
|
||||||
"unist-util-visit": "^5.0.0",
|
"unist-util-visit": "^5.0.0",
|
||||||
"vitepress": "^1.6.4",
|
"vitepress": "^1.0.2",
|
||||||
"vitepress-plugin-search": "1.0.4-alpha.22"
|
"vitepress-plugin-search": "1.0.4-alpha.22"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
// Special states for recognizing aliases
|
// Special states for recognizing aliases
|
||||||
// A special state for grabbing text up to the first comment/newline
|
// A special state for grabbing text up to the first comment/newline
|
||||||
%x ID ALIAS LINE
|
%x ID ALIAS LINE CONFIG CONFIG_DATA
|
||||||
|
|
||||||
%x acc_title
|
%x acc_title
|
||||||
%x acc_descr
|
%x acc_descr
|
||||||
@@ -28,6 +28,11 @@
|
|||||||
\%%(?!\{)[^\n]* /* skip comments */
|
\%%(?!\{)[^\n]* /* skip comments */
|
||||||
[^\}]\%\%[^\n]* /* skip comments */
|
[^\}]\%\%[^\n]* /* skip comments */
|
||||||
[0-9]+(?=[ \n]+) return 'NUM';
|
[0-9]+(?=[ \n]+) return 'NUM';
|
||||||
|
<ID>\@\{ { this.begin('CONFIG'); return 'CONFIG_START'; }
|
||||||
|
<CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; }
|
||||||
|
<CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; }
|
||||||
|
<ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; }
|
||||||
|
<ID>[^\<->\->:\n,;@]+?([\-]*[^\<->\->:\n,;@]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
|
||||||
"box" { this.begin('LINE'); return 'box'; }
|
"box" { this.begin('LINE'); return 'box'; }
|
||||||
"participant" { this.begin('ID'); return 'participant'; }
|
"participant" { this.begin('ID'); return 'participant'; }
|
||||||
"actor" { this.begin('ID'); return 'participant_actor'; }
|
"actor" { this.begin('ID'); return 'participant_actor'; }
|
||||||
@@ -231,6 +236,8 @@ participant_statement
|
|||||||
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
|
||||||
| 'participant_actor' actor 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant'; $$=$2;}
|
| 'participant_actor' actor 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant'; $$=$2;}
|
||||||
| 'destroy' actor 'NEWLINE' {$2.type='destroyParticipant'; $$=$2;}
|
| 'destroy' actor 'NEWLINE' {$2.type='destroyParticipant'; $$=$2;}
|
||||||
|
| 'participant' actor_with_config 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant'; $$=$2;}
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
note_statement
|
note_statement
|
||||||
@@ -301,6 +308,23 @@ signal
|
|||||||
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
{ $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
actor_with_config
|
||||||
|
: ACTOR config_object
|
||||||
|
{
|
||||||
|
$$ = {
|
||||||
|
type: 'addParticipant',
|
||||||
|
actor: $1,
|
||||||
|
config: $2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
config_object
|
||||||
|
: CONFIG_START CONFIG_CONTENT CONFIG_END
|
||||||
|
{
|
||||||
|
$$ = $2.trim();
|
||||||
|
}
|
||||||
|
;
|
||||||
// actor
|
// actor
|
||||||
// : actor_participant
|
// : actor_participant
|
||||||
// | actor_actor
|
// | actor_actor
|
||||||
@@ -313,7 +337,7 @@ signaltype
|
|||||||
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
: SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
|
||||||
| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
|
| DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
|
||||||
| SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
|
| SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
|
||||||
| BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; }
|
| BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; }
|
||||||
| DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
|
| DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
|
||||||
| BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; }
|
| BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; }
|
||||||
| SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
|
| SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
|
import * as yaml from 'js-yaml';
|
||||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import { ImperativeState } from '../../utils/imperativeState.js';
|
import { ImperativeState } from '../../utils/imperativeState.js';
|
||||||
@@ -13,6 +14,7 @@ import {
|
|||||||
setDiagramTitle,
|
setDiagramTitle,
|
||||||
} from '../common/commonDb.js';
|
} from '../common/commonDb.js';
|
||||||
import type { Actor, AddMessageParams, Box, Message, Note } from './types.js';
|
import type { Actor, AddMessageParams, Box, Message, Note } from './types.js';
|
||||||
|
import type { ParticipantMetaData } from '../../types.js';
|
||||||
|
|
||||||
interface SequenceState {
|
interface SequenceState {
|
||||||
prevActor?: string;
|
prevActor?: string;
|
||||||
@@ -75,6 +77,17 @@ const PLACEMENT = {
|
|||||||
OVER: 2,
|
OVER: 2,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const PARTICIPANT_TYPE = {
|
||||||
|
ACTOR: 'actor',
|
||||||
|
BOUNDARY: 'boundary',
|
||||||
|
COLLECTIONS: 'collections',
|
||||||
|
CONTROL: 'control',
|
||||||
|
DATABASE: 'database',
|
||||||
|
ENTITY: 'entity',
|
||||||
|
PARTICIPANT: 'participant',
|
||||||
|
QUEUE: 'queue',
|
||||||
|
} as const;
|
||||||
|
|
||||||
export class SequenceDB implements DiagramDB {
|
export class SequenceDB implements DiagramDB {
|
||||||
private readonly state = new ImperativeState<SequenceState>(() => ({
|
private readonly state = new ImperativeState<SequenceState>(() => ({
|
||||||
prevActor: undefined,
|
prevActor: undefined,
|
||||||
@@ -119,9 +132,22 @@ export class SequenceDB implements DiagramDB {
|
|||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
description: { text: string; wrap?: boolean | null; type: string },
|
description: { text: string; wrap?: boolean | null; type: string },
|
||||||
type: string
|
type: string,
|
||||||
|
metadata?: any
|
||||||
) {
|
) {
|
||||||
let assignedBox = this.state.records.currentBox;
|
let assignedBox = this.state.records.currentBox;
|
||||||
|
let doc;
|
||||||
|
if (metadata !== undefined) {
|
||||||
|
let yamlData;
|
||||||
|
// detect if shapeData contains a newline character
|
||||||
|
if (!metadata.includes('\n')) {
|
||||||
|
yamlData = '{\n' + metadata + '\n}';
|
||||||
|
} else {
|
||||||
|
yamlData = metadata + '\n';
|
||||||
|
}
|
||||||
|
doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as ParticipantMetaData;
|
||||||
|
}
|
||||||
|
type = doc?.type ?? type;
|
||||||
const old = this.state.records.actors.get(id);
|
const old = this.state.records.actors.get(id);
|
||||||
if (old) {
|
if (old) {
|
||||||
// If already set and trying to set to a new one throw error
|
// If already set and trying to set to a new one throw error
|
||||||
@@ -518,7 +544,7 @@ export class SequenceDB implements DiagramDB {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'addParticipant':
|
case 'addParticipant':
|
||||||
this.addActor(param.actor, param.actor, param.description, param.draw);
|
this.addActor(param.actor, param.actor, param.description, param.draw, param.config);
|
||||||
break;
|
break;
|
||||||
case 'createParticipant':
|
case 'createParticipant':
|
||||||
if (this.state.records.actors.has(param.actor)) {
|
if (this.state.records.actors.has(param.actor)) {
|
||||||
@@ -527,7 +553,7 @@ export class SequenceDB implements DiagramDB {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.state.records.lastCreated = param.actor;
|
this.state.records.lastCreated = param.actor;
|
||||||
this.addActor(param.actor, param.actor, param.description, param.draw);
|
this.addActor(param.actor, param.actor, param.description, param.draw, param.config);
|
||||||
this.state.records.createdActors.set(param.actor, this.state.records.messages.length);
|
this.state.records.createdActors.set(param.actor, this.state.records.messages.length);
|
||||||
break;
|
break;
|
||||||
case 'destroyParticipant':
|
case 'destroyParticipant':
|
||||||
|
@@ -2058,4 +2058,272 @@ Bob->>Alice:Got it!
|
|||||||
expect(messages[0].from).toBe('Alice');
|
expect(messages[0].from).toBe('Alice');
|
||||||
expect(messages[0].to).toBe('Bob');
|
expect(messages[0].to).toBe('Bob');
|
||||||
});
|
});
|
||||||
|
describe('when parsing extended participant syntax', () => {
|
||||||
|
it('should parse participants with different quote styles and whitespace', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant Alice@{ "type" : "database" }
|
||||||
|
participant Bob@{ "type" : "database" }
|
||||||
|
participant Carl@{ type: "database" }
|
||||||
|
participant David@{ "type" : 'database' }
|
||||||
|
participant Eve@{ type: 'database' }
|
||||||
|
participant Favela@{ "type" : "database" }
|
||||||
|
Bob->>+Alice: Hi Alice
|
||||||
|
Alice->>+Bob: Hi Bob
|
||||||
|
`);
|
||||||
|
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
|
||||||
|
expect(actors.get('Alice').type).toBe('database');
|
||||||
|
expect(actors.get('Alice').description).toBe('Alice');
|
||||||
|
|
||||||
|
expect(actors.get('Bob').type).toBe('database');
|
||||||
|
expect(actors.get('Bob').description).toBe('Bob');
|
||||||
|
|
||||||
|
expect(actors.get('Carl').type).toBe('database');
|
||||||
|
expect(actors.get('Carl').description).toBe('Carl');
|
||||||
|
|
||||||
|
expect(actors.get('David').type).toBe('database');
|
||||||
|
expect(actors.get('David').description).toBe('David');
|
||||||
|
|
||||||
|
expect(actors.get('Eve').type).toBe('database');
|
||||||
|
expect(actors.get('Eve').description).toBe('Eve');
|
||||||
|
|
||||||
|
expect(actors.get('Favela').type).toBe('database');
|
||||||
|
expect(actors.get('Favela').description).toBe('Favela');
|
||||||
|
|
||||||
|
// Verify messages were parsed correctly
|
||||||
|
const messages = diagram.db.getMessages();
|
||||||
|
expect(messages.length).toBe(4); // 2 messages + 2 activation messages
|
||||||
|
expect(messages[0].from).toBe('Bob');
|
||||||
|
expect(messages[0].to).toBe('Alice');
|
||||||
|
expect(messages[0].message).toBe('Hi Alice');
|
||||||
|
expect(messages[2].from).toBe('Alice'); // Second message (index 2 due to activation)
|
||||||
|
expect(messages[2].to).toBe('Bob');
|
||||||
|
expect(messages[2].message).toBe('Hi Bob');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse mixed participant types with extended syntax', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant lead
|
||||||
|
participant dsa@{ "type" : "queue" }
|
||||||
|
API->>+Database: getUserb
|
||||||
|
Database-->>-API: userb
|
||||||
|
dsa --> Database: hello
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Verify actors were created
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
|
||||||
|
expect(actors.get('lead').type).toBe('participant');
|
||||||
|
expect(actors.get('lead').description).toBe('lead');
|
||||||
|
|
||||||
|
// Participant with extended syntax
|
||||||
|
expect(actors.get('dsa').type).toBe('queue');
|
||||||
|
expect(actors.get('dsa').description).toBe('dsa');
|
||||||
|
|
||||||
|
// Implicitly created actors (from messages)
|
||||||
|
expect(actors.get('API').type).toBe('participant');
|
||||||
|
expect(actors.get('API').description).toBe('API');
|
||||||
|
|
||||||
|
expect(actors.get('Database').type).toBe('participant');
|
||||||
|
expect(actors.get('Database').description).toBe('Database');
|
||||||
|
|
||||||
|
// Verify messages were parsed correctly
|
||||||
|
const messages = diagram.db.getMessages();
|
||||||
|
expect(messages.length).toBe(5); // 3 messages + 2 activation messages
|
||||||
|
|
||||||
|
// First message with activation
|
||||||
|
expect(messages[0].from).toBe('API');
|
||||||
|
expect(messages[0].to).toBe('Database');
|
||||||
|
expect(messages[0].message).toBe('getUserb');
|
||||||
|
expect(messages[0].activate).toBe(true);
|
||||||
|
|
||||||
|
// Second message with deactivation
|
||||||
|
expect(messages[2].from).toBe('Database');
|
||||||
|
expect(messages[2].to).toBe('API');
|
||||||
|
expect(messages[2].message).toBe('userb');
|
||||||
|
|
||||||
|
// Third message
|
||||||
|
expect(messages[4].from).toBe('dsa');
|
||||||
|
expect(messages[4].to).toBe('Database');
|
||||||
|
expect(messages[4].message).toBe('hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for malformed JSON in participant definition', async () => {
|
||||||
|
const invalidDiagram = `
|
||||||
|
sequenceDiagram
|
||||||
|
participant D@{ "type: "entity" }
|
||||||
|
participant E@{ "type": "dat
|
||||||
|
abase }
|
||||||
|
`;
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
try {
|
||||||
|
await mermaidAPI.parse(invalidDiagram);
|
||||||
|
} catch (e) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
expect(error).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for missing colon separator', async () => {
|
||||||
|
const invalidDiagram = `
|
||||||
|
sequenceDiagram
|
||||||
|
participant C@{ "type" "control" }
|
||||||
|
C ->> C: action
|
||||||
|
`;
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
try {
|
||||||
|
await mermaidAPI.parse(invalidDiagram);
|
||||||
|
} catch (e) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
expect(error).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for missing closing brace', async () => {
|
||||||
|
const invalidDiagram = `
|
||||||
|
sequenceDiagram
|
||||||
|
participant E@{ "type": "entity"
|
||||||
|
E ->> E: process
|
||||||
|
`;
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
try {
|
||||||
|
await mermaidAPI.parse(invalidDiagram);
|
||||||
|
} catch (e) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
expect(error).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('participant type parsing', () => {
|
||||||
|
it('should parse boundary participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant boundary@{ "type" : "boundary" }
|
||||||
|
boundary->boundary: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('boundary').type).toBe('boundary');
|
||||||
|
expect(actors.get('boundary').description).toBe('boundary');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse control participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant C@{ "type" : "control" }
|
||||||
|
C->C: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('C').type).toBe('control');
|
||||||
|
expect(actors.get('C').description).toBe('C');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse entity participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant E@{ "type" : "entity" }
|
||||||
|
E->E: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('E').type).toBe('entity');
|
||||||
|
expect(actors.get('E').description).toBe('E');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse database participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant D@{ "type" : "database" }
|
||||||
|
D->D: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('D').type).toBe('database');
|
||||||
|
expect(actors.get('D').description).toBe('D');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse collections participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant L@{ "type" : "collections" }
|
||||||
|
L->L: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('L').type).toBe('collections');
|
||||||
|
expect(actors.get('L').description).toBe('L');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse queue participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant Q@{ "type" : "queue" }
|
||||||
|
Q->Q: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('Q').type).toBe('queue');
|
||||||
|
expect(actors.get('Q').description).toBe('Q');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('participant type parsing', () => {
|
||||||
|
it('should parse actor participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant A@{ "type" : "queue" }
|
||||||
|
A->A: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('A').type).toBe('queue');
|
||||||
|
expect(actors.get('A').description).toBe('A');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse participant participant', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant P@{ "type" : "database" }
|
||||||
|
P->P: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('P').type).toBe('database');
|
||||||
|
expect(actors.get('P').description).toBe('P');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse boundary using actor keyword', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant Alice@{ "type" : "collections" }
|
||||||
|
participant Bob@{ "type" : "control" }
|
||||||
|
Alice->>Bob: Hello Bob, how are you?
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('Alice').type).toBe('collections');
|
||||||
|
expect(actors.get('Bob').type).toBe('control');
|
||||||
|
expect(actors.get('Bob').description).toBe('Bob');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse control using participant keyword', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant C@{ "type" : "control" }
|
||||||
|
C->C: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('C').type).toBe('control');
|
||||||
|
expect(actors.get('C').description).toBe('C');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse entity using actor keyword', async () => {
|
||||||
|
const diagram = await Diagram.fromText(`
|
||||||
|
sequenceDiagram
|
||||||
|
participant E@{ "type" : "entity" }
|
||||||
|
E->E: test
|
||||||
|
`);
|
||||||
|
const actors = diagram.db.getActors();
|
||||||
|
expect(actors.get('E').type).toBe('entity');
|
||||||
|
expect(actors.get('E').description).toBe('E');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -10,6 +10,7 @@ import assignWithDepth from '../../assignWithDepth.js';
|
|||||||
import utils from '../../utils.js';
|
import utils from '../../utils.js';
|
||||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||||
import type { Diagram } from '../../Diagram.js';
|
import type { Diagram } from '../../Diagram.js';
|
||||||
|
import { PARTICIPANT_TYPE } from './sequenceDb.js';
|
||||||
|
|
||||||
let conf = {};
|
let conf = {};
|
||||||
|
|
||||||
@@ -746,11 +747,19 @@ function adjustCreatedDestroyedData(
|
|||||||
msgModel.startx = msgModel.startx - adjustment;
|
msgModel.startx = msgModel.startx - adjustment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const actorArray = [
|
||||||
|
PARTICIPANT_TYPE.ACTOR,
|
||||||
|
PARTICIPANT_TYPE.CONTROL,
|
||||||
|
PARTICIPANT_TYPE.ENTITY,
|
||||||
|
PARTICIPANT_TYPE.DATABASE,
|
||||||
|
];
|
||||||
|
|
||||||
// if it is a create message
|
// if it is a create message
|
||||||
if (createdActors.get(msg.to) == index) {
|
if (createdActors.get(msg.to) == index) {
|
||||||
const actor = actors.get(msg.to);
|
const actor = actors.get(msg.to);
|
||||||
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
|
const adjustment = actorArray.includes(actor.type)
|
||||||
|
? ACTOR_TYPE_WIDTH / 2 + 3
|
||||||
|
: actor.width / 2 + 3;
|
||||||
receiverAdjustment(actor, adjustment);
|
receiverAdjustment(actor, adjustment);
|
||||||
actor.starty = lineStartY - actor.height / 2;
|
actor.starty = lineStartY - actor.height / 2;
|
||||||
bounds.bumpVerticalPos(actor.height / 2);
|
bounds.bumpVerticalPos(actor.height / 2);
|
||||||
@@ -759,7 +768,7 @@ function adjustCreatedDestroyedData(
|
|||||||
else if (destroyedActors.get(msg.from) == index) {
|
else if (destroyedActors.get(msg.from) == index) {
|
||||||
const actor = actors.get(msg.from);
|
const actor = actors.get(msg.from);
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2;
|
const adjustment = actorArray.includes(actor.type) ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2;
|
||||||
senderAdjustment(actor, adjustment);
|
senderAdjustment(actor, adjustment);
|
||||||
}
|
}
|
||||||
actor.stopy = lineStartY - actor.height / 2;
|
actor.stopy = lineStartY - actor.height / 2;
|
||||||
@@ -769,7 +778,9 @@ function adjustCreatedDestroyedData(
|
|||||||
else if (destroyedActors.get(msg.to) == index) {
|
else if (destroyedActors.get(msg.to) == index) {
|
||||||
const actor = actors.get(msg.to);
|
const actor = actors.get(msg.to);
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
|
const adjustment = actorArray.includes(actor.type)
|
||||||
|
? ACTOR_TYPE_WIDTH / 2 + 3
|
||||||
|
: actor.width / 2 + 3;
|
||||||
receiverAdjustment(actor, adjustment);
|
receiverAdjustment(actor, adjustment);
|
||||||
}
|
}
|
||||||
actor.stopy = lineStartY - actor.height / 2;
|
actor.stopy = lineStartY - actor.height / 2;
|
||||||
@@ -1087,10 +1098,11 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
for (const box of bounds.models.boxes) {
|
for (const box of bounds.models.boxes) {
|
||||||
box.height = bounds.getVerticalPos() - box.y;
|
box.height = bounds.getVerticalPos() - box.y;
|
||||||
bounds.insert(box.x, box.y, box.x + box.width, box.height);
|
bounds.insert(box.x, box.y, box.x + box.width, box.height);
|
||||||
box.startx = box.x;
|
const boxPadding = conf.boxMargin * 2;
|
||||||
box.starty = box.y;
|
box.startx = box.x - boxPadding;
|
||||||
box.stopx = box.startx + box.width;
|
box.starty = box.y - boxPadding * 0.25;
|
||||||
box.stopy = box.starty + box.height;
|
box.stopx = box.startx + box.width + 2 * boxPadding;
|
||||||
|
box.stopy = box.starty + box.height + boxPadding * 0.75;
|
||||||
box.stroke = 'rgb(0,0,0, 0.5)';
|
box.stroke = 'rgb(0,0,0, 0.5)';
|
||||||
svgDraw.drawBox(diagram, box, conf);
|
svgDraw.drawBox(diagram, box, conf);
|
||||||
}
|
}
|
||||||
@@ -1355,6 +1367,9 @@ async function calculateActorMargins(
|
|||||||
return (total += actors.get(aKey).width + (actors.get(aKey).margin || 0));
|
return (total += actors.get(aKey).width + (actors.get(aKey).margin || 0));
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
const standardBoxPadding = conf.boxMargin * 8;
|
||||||
|
totalWidth += standardBoxPadding;
|
||||||
|
|
||||||
totalWidth -= 2 * conf.boxTextMargin;
|
totalWidth -= 2 * conf.boxTextMargin;
|
||||||
if (box.wrap) {
|
if (box.wrap) {
|
||||||
box.name = utils.wrapLabel(box.name, totalWidth - 2 * conf.wrapPadding, textFont);
|
box.name = utils.wrapLabel(box.name, totalWidth - 2 * conf.wrapPadding, textFont);
|
||||||
|
@@ -12,6 +12,11 @@ const getStyles = (options) =>
|
|||||||
.actor-line {
|
.actor-line {
|
||||||
stroke: ${options.actorLineColor};
|
stroke: ${options.actorLineColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.innerArc {
|
||||||
|
stroke-width: 1.5;
|
||||||
|
stroke-dasharray: none;
|
||||||
|
}
|
||||||
|
|
||||||
.messageLine0 {
|
.messageLine0 {
|
||||||
stroke-width: 1.5;
|
stroke-width: 1.5;
|
||||||
@@ -115,6 +120,7 @@ const getStyles = (options) =>
|
|||||||
fill: ${options.actorBkg};
|
fill: ${options.actorBkg};
|
||||||
stroke-width: 2px;
|
stroke-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default getStyles;
|
export default getStyles;
|
||||||
|
@@ -415,6 +415,600 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
|||||||
return height;
|
return height;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an actor in the diagram with the attached line
|
||||||
|
*
|
||||||
|
* @param {any} elem - The diagram we'll draw to.
|
||||||
|
* @param {any} actor - The actor to draw.
|
||||||
|
* @param {any} conf - DrawText implementation discriminator object
|
||||||
|
* @param {boolean} isFooter - If the actor is the footer one
|
||||||
|
*/
|
||||||
|
const drawActorTypeCollections = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
|
const center = actor.x + actor.width / 2;
|
||||||
|
const centerY = actorY + actor.height;
|
||||||
|
|
||||||
|
const boxplusLineGroup = elem.append('g').lower();
|
||||||
|
var g = boxplusLineGroup;
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actorCnt++;
|
||||||
|
if (Object.keys(actor.links || {}).length && !conf.forceMenus) {
|
||||||
|
g.attr('onclick', popupMenuToggle(`actor${actorCnt}_popup`)).attr('cursor', 'pointer');
|
||||||
|
}
|
||||||
|
g.append('line')
|
||||||
|
.attr('id', 'actor' + actorCnt)
|
||||||
|
.attr('x1', center)
|
||||||
|
.attr('y1', centerY)
|
||||||
|
.attr('x2', center)
|
||||||
|
.attr('y2', 2000)
|
||||||
|
.attr('class', 'actor-line 200')
|
||||||
|
.attr('stroke-width', '0.5px')
|
||||||
|
.attr('stroke', '#999')
|
||||||
|
.attr('name', actor.name);
|
||||||
|
|
||||||
|
g = boxplusLineGroup.append('g');
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
|
|
||||||
|
if (actor.links != null) {
|
||||||
|
g.attr('id', 'root-' + actorCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
|
var cssclass = 'actor';
|
||||||
|
if (actor.properties?.class) {
|
||||||
|
cssclass = actor.properties.class;
|
||||||
|
} else {
|
||||||
|
rect.fill = '#eaeaea';
|
||||||
|
}
|
||||||
|
if (isFooter) {
|
||||||
|
cssclass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||||
|
} else {
|
||||||
|
cssclass += ` ${TOP_ACTOR_CLASS}`;
|
||||||
|
}
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
rect.width = actor.width;
|
||||||
|
rect.height = actor.height;
|
||||||
|
rect.class = cssclass;
|
||||||
|
rect.name = actor.name;
|
||||||
|
|
||||||
|
// DRAW STACKED RECTANGLES
|
||||||
|
const offset = 6;
|
||||||
|
const shadowRect = {
|
||||||
|
...rect,
|
||||||
|
x: rect.x + (isFooter ? -offset : -offset),
|
||||||
|
y: rect.y + (isFooter ? +offset : +offset),
|
||||||
|
class: 'actor',
|
||||||
|
};
|
||||||
|
const rectElem = drawRect(g, rect); // draw main rectangle on top
|
||||||
|
drawRect(g, shadowRect);
|
||||||
|
actor.rectData = rect;
|
||||||
|
|
||||||
|
if (actor.properties?.icon) {
|
||||||
|
const iconSrc = actor.properties.icon.trim();
|
||||||
|
if (iconSrc.charAt(0) === '@') {
|
||||||
|
svgDrawCommon.drawEmbeddedImage(g, rect.x + rect.width - 20, rect.y + 10, iconSrc.substr(1));
|
||||||
|
} else {
|
||||||
|
svgDrawCommon.drawImage(g, rect.x + rect.width - 20, rect.y + 10, iconSrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
|
actor.description,
|
||||||
|
g,
|
||||||
|
rect.x - offset,
|
||||||
|
rect.y + offset,
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: `actor ${ACTOR_BOX_CLASS}` },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
let height = actor.height;
|
||||||
|
if (rectElem.node) {
|
||||||
|
const bounds = rectElem.node().getBBox();
|
||||||
|
actor.height = bounds.height;
|
||||||
|
height = bounds.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawActorTypeQueue = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
|
const center = actor.x + actor.width / 2;
|
||||||
|
const centerY = actorY + actor.height;
|
||||||
|
|
||||||
|
const boxplusLineGroup = elem.append('g').lower();
|
||||||
|
let g = boxplusLineGroup;
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actorCnt++;
|
||||||
|
if (Object.keys(actor.links || {}).length && !conf.forceMenus) {
|
||||||
|
g.attr('onclick', popupMenuToggle(`actor${actorCnt}_popup`)).attr('cursor', 'pointer');
|
||||||
|
}
|
||||||
|
g.append('line')
|
||||||
|
.attr('id', 'actor' + actorCnt)
|
||||||
|
.attr('x1', center)
|
||||||
|
.attr('y1', centerY)
|
||||||
|
.attr('x2', center)
|
||||||
|
.attr('y2', 2000)
|
||||||
|
.attr('class', 'actor-line 200')
|
||||||
|
.attr('stroke-width', '0.5px')
|
||||||
|
.attr('stroke', '#999')
|
||||||
|
.attr('name', actor.name);
|
||||||
|
|
||||||
|
g = boxplusLineGroup.append('g');
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
|
|
||||||
|
if (actor.links != null) {
|
||||||
|
g.attr('id', 'root-' + actorCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
|
let cssclass = 'actor';
|
||||||
|
if (actor.properties?.class) {
|
||||||
|
cssclass = actor.properties.class;
|
||||||
|
} else {
|
||||||
|
rect.fill = '#eaeaea';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFooter) {
|
||||||
|
cssclass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||||
|
} else {
|
||||||
|
cssclass += ` ${TOP_ACTOR_CLASS}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
rect.width = actor.width;
|
||||||
|
rect.height = actor.height;
|
||||||
|
rect.class = cssclass;
|
||||||
|
rect.name = actor.name;
|
||||||
|
|
||||||
|
// Cylinder dimensions
|
||||||
|
const ry = rect.height / 2;
|
||||||
|
const rx = ry / (2.5 + rect.height / 50);
|
||||||
|
|
||||||
|
// Cylinder base group
|
||||||
|
const cylinderGroup = g.append('g');
|
||||||
|
const cylinderArc = g.append('g');
|
||||||
|
|
||||||
|
// Main cylinder body
|
||||||
|
cylinderGroup
|
||||||
|
.append('path')
|
||||||
|
.attr(
|
||||||
|
'd',
|
||||||
|
`M ${rect.x},${rect.y + ry}
|
||||||
|
a ${rx},${ry} 0 0 0 0,${rect.height}
|
||||||
|
h ${rect.width - 2 * rx}
|
||||||
|
a ${rx},${ry} 0 0 0 0,-${rect.height}
|
||||||
|
Z
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.attr('class', cssclass);
|
||||||
|
cylinderArc
|
||||||
|
.append('path')
|
||||||
|
.attr(
|
||||||
|
'd',
|
||||||
|
`M ${rect.x},${rect.y + ry}
|
||||||
|
a ${rx},${ry} 0 0 0 0,${rect.height}`
|
||||||
|
)
|
||||||
|
.attr('stroke', '#666')
|
||||||
|
.attr('stroke-width', '1px')
|
||||||
|
.attr('class', cssclass);
|
||||||
|
|
||||||
|
cylinderGroup.attr('transform', `translate(${rx}, ${-(rect.height / 2)})`);
|
||||||
|
cylinderArc.attr('transform', `translate(${rect.width - rx}, ${-rect.height / 2})`);
|
||||||
|
|
||||||
|
actor.rectData = rect;
|
||||||
|
|
||||||
|
if (actor.properties?.icon) {
|
||||||
|
const iconSrc = actor.properties.icon.trim();
|
||||||
|
const iconX = rect.x + rect.width - 20;
|
||||||
|
const iconY = rect.y + 10;
|
||||||
|
if (iconSrc.charAt(0) === '@') {
|
||||||
|
svgDrawCommon.drawEmbeddedImage(g, iconX, iconY, iconSrc.substr(1));
|
||||||
|
} else {
|
||||||
|
svgDrawCommon.drawImage(g, iconX, iconY, iconSrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
|
actor.description,
|
||||||
|
g,
|
||||||
|
rect.x,
|
||||||
|
rect.y,
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: `actor ${ACTOR_BOX_CLASS}` },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
let height = actor.height;
|
||||||
|
const lastPath = cylinderGroup.select('path:last-child');
|
||||||
|
if (lastPath.node()) {
|
||||||
|
const bounds = lastPath.node().getBBox();
|
||||||
|
actor.height = bounds.height;
|
||||||
|
height = bounds.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawActorTypeControl = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
|
const center = actor.x + actor.width / 2;
|
||||||
|
const centerY = actorY + 75;
|
||||||
|
|
||||||
|
const line = elem.append('g').lower();
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actorCnt++;
|
||||||
|
line
|
||||||
|
.append('line')
|
||||||
|
.attr('id', 'actor' + actorCnt)
|
||||||
|
.attr('x1', center)
|
||||||
|
.attr('y1', centerY)
|
||||||
|
.attr('x2', center)
|
||||||
|
.attr('y2', 2000)
|
||||||
|
.attr('class', 'actor-line 200')
|
||||||
|
.attr('stroke-width', '0.5px')
|
||||||
|
.attr('stroke', '#999')
|
||||||
|
.attr('name', actor.name);
|
||||||
|
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
|
}
|
||||||
|
const actElem = elem.append('g');
|
||||||
|
let cssClass = ACTOR_MAN_FIGURE_CLASS;
|
||||||
|
if (isFooter) {
|
||||||
|
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||||
|
} else {
|
||||||
|
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||||
|
}
|
||||||
|
actElem.attr('class', cssClass);
|
||||||
|
actElem.attr('name', actor.name);
|
||||||
|
|
||||||
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
rect.fill = '#eaeaea';
|
||||||
|
rect.width = actor.width;
|
||||||
|
rect.height = actor.height;
|
||||||
|
rect.class = 'actor';
|
||||||
|
|
||||||
|
const cx = actor.x + actor.width / 2;
|
||||||
|
const cy = actorY + 30;
|
||||||
|
const r = 18;
|
||||||
|
|
||||||
|
actElem
|
||||||
|
.append('defs')
|
||||||
|
.append('marker')
|
||||||
|
.attr('id', 'filled-head-control')
|
||||||
|
.attr('refX', 11)
|
||||||
|
.attr('refY', 5.8)
|
||||||
|
.attr('markerWidth', 20)
|
||||||
|
.attr('markerHeight', 28)
|
||||||
|
.attr('orient', '172.5')
|
||||||
|
.append('path')
|
||||||
|
.attr('d', 'M 14.4 5.6 L 7.2 10.4 L 8.8 5.6 L 7.2 0.8 Z');
|
||||||
|
|
||||||
|
// Draw the base circle
|
||||||
|
actElem
|
||||||
|
.append('circle')
|
||||||
|
.attr('cx', cx)
|
||||||
|
.attr('cy', cy)
|
||||||
|
.attr('r', r)
|
||||||
|
.attr('fill', '#eaeaf7')
|
||||||
|
.attr('stroke', '#666')
|
||||||
|
.attr('stroke-width', 1.2);
|
||||||
|
|
||||||
|
// Draw looping arrow as arc path
|
||||||
|
actElem
|
||||||
|
.append('line')
|
||||||
|
.attr('marker-end', 'url(#filled-head-control)')
|
||||||
|
.attr('transform', `translate(${cx}, ${cy - r})`);
|
||||||
|
|
||||||
|
const bounds = actElem.node().getBBox();
|
||||||
|
actor.height = bounds.height + 2 * (conf?.sequence?.labelBoxHeight ?? 0);
|
||||||
|
|
||||||
|
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
|
actor.description,
|
||||||
|
actElem,
|
||||||
|
rect.x,
|
||||||
|
rect.y + r + (isFooter ? 5 : 10),
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
return actor.height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawActorTypeEntity = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
|
const center = actor.x + actor.width / 2;
|
||||||
|
const centerY = actorY + 75;
|
||||||
|
|
||||||
|
const line = elem.append('g').lower();
|
||||||
|
|
||||||
|
const actElem = elem.append('g');
|
||||||
|
let cssClass = ACTOR_MAN_FIGURE_CLASS;
|
||||||
|
if (isFooter) {
|
||||||
|
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||||
|
} else {
|
||||||
|
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||||
|
}
|
||||||
|
actElem.attr('class', cssClass);
|
||||||
|
actElem.attr('name', actor.name);
|
||||||
|
|
||||||
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
rect.fill = '#eaeaea';
|
||||||
|
rect.width = actor.width;
|
||||||
|
rect.height = actor.height;
|
||||||
|
rect.class = 'actor';
|
||||||
|
|
||||||
|
const cx = actor.x + actor.width / 2;
|
||||||
|
const cy = actorY + (!isFooter ? 25 : 10);
|
||||||
|
const r = 18;
|
||||||
|
|
||||||
|
actElem
|
||||||
|
.append('circle')
|
||||||
|
.attr('cx', cx)
|
||||||
|
.attr('cy', cy)
|
||||||
|
.attr('r', r)
|
||||||
|
.attr('width', actor.width)
|
||||||
|
.attr('height', actor.height);
|
||||||
|
|
||||||
|
actElem
|
||||||
|
.append('line')
|
||||||
|
.attr('x1', cx - r)
|
||||||
|
.attr('x2', cx + r)
|
||||||
|
.attr('y1', cy + r)
|
||||||
|
.attr('y2', cy + r)
|
||||||
|
.attr('stroke', '#333')
|
||||||
|
.attr('stroke-width', 2);
|
||||||
|
|
||||||
|
const bounds = actElem.node().getBBox();
|
||||||
|
actor.height = bounds.height + (conf?.sequence?.labelBoxHeight ?? 0);
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actorCnt++;
|
||||||
|
line
|
||||||
|
.append('line')
|
||||||
|
.attr('id', 'actor' + actorCnt)
|
||||||
|
.attr('x1', center)
|
||||||
|
.attr('y1', centerY)
|
||||||
|
.attr('x2', center)
|
||||||
|
.attr('y2', 2000)
|
||||||
|
.attr('class', 'actor-line 200')
|
||||||
|
.attr('stroke-width', '0.5px')
|
||||||
|
.attr('stroke', '#999')
|
||||||
|
.attr('name', actor.name);
|
||||||
|
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
|
actor.description,
|
||||||
|
actElem,
|
||||||
|
rect.x,
|
||||||
|
rect.y + (!isFooter ? (cy + r - actorY) / 2 : (cy - actorY + r - 5) / 2),
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actElem.attr('transform', `translate(${0}, ${r / 2})`);
|
||||||
|
} else {
|
||||||
|
actElem.attr('transform', `translate(${0}, ${r / 2})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actor.height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawActorTypeDatabase = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
|
const center = actor.x + actor.width / 2;
|
||||||
|
const centerY = actorY + actor.height + 2 * conf.boxTextMargin;
|
||||||
|
|
||||||
|
const boxplusLineGroup = elem.append('g').lower();
|
||||||
|
let g = boxplusLineGroup;
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actorCnt++;
|
||||||
|
if (Object.keys(actor.links || {}).length && !conf.forceMenus) {
|
||||||
|
g.attr('onclick', popupMenuToggle(`actor${actorCnt}_popup`)).attr('cursor', 'pointer');
|
||||||
|
}
|
||||||
|
g.append('line')
|
||||||
|
.attr('id', 'actor' + actorCnt)
|
||||||
|
.attr('x1', center)
|
||||||
|
.attr('y1', centerY)
|
||||||
|
.attr('x2', center)
|
||||||
|
.attr('y2', 2000)
|
||||||
|
.attr('class', 'actor-line 200')
|
||||||
|
.attr('stroke-width', '0.5px')
|
||||||
|
.attr('stroke', '#999')
|
||||||
|
.attr('name', actor.name);
|
||||||
|
|
||||||
|
g = boxplusLineGroup.append('g');
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
|
|
||||||
|
if (actor.links != null) {
|
||||||
|
g.attr('id', 'root-' + actorCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
|
|
||||||
|
let cssclass = 'actor';
|
||||||
|
if (actor.properties?.class) {
|
||||||
|
cssclass = actor.properties.class;
|
||||||
|
} else {
|
||||||
|
rect.fill = '#eaeaea';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFooter) {
|
||||||
|
cssclass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||||
|
} else {
|
||||||
|
cssclass += ` ${TOP_ACTOR_CLASS}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
rect.width = actor.width;
|
||||||
|
rect.height = actor.height;
|
||||||
|
rect.class = cssclass;
|
||||||
|
rect.name = actor.name;
|
||||||
|
|
||||||
|
// Cylinder dimensions
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
const w = rect.width / 4;
|
||||||
|
const h = rect.width / 4;
|
||||||
|
const rx = w / 2;
|
||||||
|
const ry = rx / (2.5 + w / 50);
|
||||||
|
|
||||||
|
// Cylinder base group
|
||||||
|
const cylinderGroup = g.append('g');
|
||||||
|
|
||||||
|
const d = `
|
||||||
|
M ${rect.x},${rect.y + ry}
|
||||||
|
a ${rx},${ry} 0 0 0 ${w},0
|
||||||
|
a ${rx},${ry} 0 0 0 -${w},0
|
||||||
|
l 0,${h - 2 * ry}
|
||||||
|
a ${rx},${ry} 0 0 0 ${w},0
|
||||||
|
l 0,-${h - 2 * ry}
|
||||||
|
`;
|
||||||
|
// Draw the main cylinder body
|
||||||
|
cylinderGroup
|
||||||
|
.append('path')
|
||||||
|
.attr('d', d)
|
||||||
|
.attr('fill', '#eaeaea')
|
||||||
|
.attr('stroke', '#000')
|
||||||
|
.attr('stroke-width', 1)
|
||||||
|
.attr('class', cssclass);
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
cylinderGroup.attr('transform', `translate(${w * 1.5}, ${(rect.height + ry) / 4})`);
|
||||||
|
} else {
|
||||||
|
cylinderGroup.attr('transform', `translate(${w * 1.5}, ${rect.height / 4 - 2 * ry})`);
|
||||||
|
}
|
||||||
|
actor.rectData = rect;
|
||||||
|
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
|
actor.description,
|
||||||
|
g,
|
||||||
|
rect.x,
|
||||||
|
rect.y + (!isFooter ? (rect.height + ry) / 2 : (rect.height + h) / 4),
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: `actor ${ACTOR_BOX_CLASS}` },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastPath = cylinderGroup.select('path:last-child');
|
||||||
|
if (lastPath.node()) {
|
||||||
|
const bounds = lastPath.node().getBBox();
|
||||||
|
actor.height = bounds.height + (conf.sequence.labelBoxHeight ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actor.height;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawActorTypeBoundary = function (elem, actor, conf, isFooter) {
|
||||||
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
|
const center = actor.x + actor.width / 2;
|
||||||
|
const centerY = actorY + 80;
|
||||||
|
const radius = 30;
|
||||||
|
const line = elem.append('g').lower();
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actorCnt++;
|
||||||
|
line
|
||||||
|
.append('line')
|
||||||
|
.attr('id', 'actor' + actorCnt)
|
||||||
|
.attr('x1', center)
|
||||||
|
.attr('y1', centerY)
|
||||||
|
.attr('x2', center)
|
||||||
|
.attr('y2', 2000)
|
||||||
|
.attr('class', 'actor-line 200')
|
||||||
|
.attr('stroke-width', '0.5px')
|
||||||
|
.attr('stroke', '#999')
|
||||||
|
.attr('name', actor.name);
|
||||||
|
|
||||||
|
actor.actorCnt = actorCnt;
|
||||||
|
}
|
||||||
|
const actElem = elem.append('g');
|
||||||
|
let cssClass = ACTOR_MAN_FIGURE_CLASS;
|
||||||
|
if (isFooter) {
|
||||||
|
cssClass += ` ${BOTTOM_ACTOR_CLASS}`;
|
||||||
|
} else {
|
||||||
|
cssClass += ` ${TOP_ACTOR_CLASS}`;
|
||||||
|
}
|
||||||
|
actElem.attr('class', cssClass);
|
||||||
|
actElem.attr('name', actor.name);
|
||||||
|
|
||||||
|
const rect = svgDrawCommon.getNoteRect();
|
||||||
|
rect.x = actor.x;
|
||||||
|
rect.y = actorY;
|
||||||
|
rect.fill = '#eaeaea';
|
||||||
|
rect.width = actor.width;
|
||||||
|
rect.height = actor.height;
|
||||||
|
rect.class = 'actor';
|
||||||
|
|
||||||
|
actElem
|
||||||
|
.append('line')
|
||||||
|
.attr('id', 'actor-man-torso' + actorCnt)
|
||||||
|
.attr('x1', actor.x + actor.width / 2 - radius * 2.5)
|
||||||
|
.attr('y1', actorY + 10)
|
||||||
|
.attr('x2', actor.x + actor.width / 2 - 15)
|
||||||
|
.attr('y2', actorY + 10);
|
||||||
|
|
||||||
|
actElem
|
||||||
|
.append('line')
|
||||||
|
.attr('id', 'actor-man-arms' + actorCnt)
|
||||||
|
.attr('x1', actor.x + actor.width / 2 - radius * 2.5)
|
||||||
|
.attr('y1', actorY + 0) // starting Y
|
||||||
|
.attr('x2', actor.x + actor.width / 2 - radius * 2.5)
|
||||||
|
.attr('y2', actorY + 20); // ending Y (26px long, adjust as needed)
|
||||||
|
|
||||||
|
actElem
|
||||||
|
.append('circle')
|
||||||
|
.attr('cx', actor.x + actor.width / 2)
|
||||||
|
.attr('cy', actorY + 10)
|
||||||
|
.attr('r', radius);
|
||||||
|
|
||||||
|
const bounds = actElem.node().getBBox();
|
||||||
|
actor.height = bounds.height + (conf.sequence.labelBoxHeight ?? 0);
|
||||||
|
|
||||||
|
_drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
|
actor.description,
|
||||||
|
actElem,
|
||||||
|
rect.x,
|
||||||
|
rect.y + (!isFooter ? radius / 2 + 3 : radius / 2 - 4),
|
||||||
|
rect.width,
|
||||||
|
rect.height,
|
||||||
|
{ class: `actor ${ACTOR_MAN_FIGURE_CLASS}` },
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isFooter) {
|
||||||
|
actElem.attr('transform', `translate(0,${radius / 2 + 7})`);
|
||||||
|
} else {
|
||||||
|
actElem.attr('transform', `translate(0,${radius / 2 + 7})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actor.height;
|
||||||
|
};
|
||||||
|
|
||||||
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
||||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
const center = actor.x + actor.width / 2;
|
const center = actor.x + actor.width / 2;
|
||||||
@@ -516,6 +1110,18 @@ export const drawActor = async function (elem, actor, conf, isFooter) {
|
|||||||
return await drawActorTypeActor(elem, actor, conf, isFooter);
|
return await drawActorTypeActor(elem, actor, conf, isFooter);
|
||||||
case 'participant':
|
case 'participant':
|
||||||
return await drawActorTypeParticipant(elem, actor, conf, isFooter);
|
return await drawActorTypeParticipant(elem, actor, conf, isFooter);
|
||||||
|
case 'boundary':
|
||||||
|
return await drawActorTypeBoundary(elem, actor, conf, isFooter);
|
||||||
|
case 'control':
|
||||||
|
return await drawActorTypeControl(elem, actor, conf, isFooter);
|
||||||
|
case 'entity':
|
||||||
|
return await drawActorTypeEntity(elem, actor, conf, isFooter);
|
||||||
|
case 'database':
|
||||||
|
return await drawActorTypeDatabase(elem, actor, conf, isFooter);
|
||||||
|
case 'collections':
|
||||||
|
return await drawActorTypeCollections(elem, actor, conf, isFooter);
|
||||||
|
case 'queue':
|
||||||
|
return await drawActorTypeQueue(elem, actor, conf, isFooter);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,6 +1,91 @@
|
|||||||
import { arc as d3arc, select } from 'd3';
|
import { arc as d3arc, select } from 'd3';
|
||||||
|
import { createText } from '../../rendering-util/createText.js';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
const MAX_SECTIONS = 12;
|
const MAX_SECTIONS = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process HTML content in node descriptions
|
||||||
|
* @param {object} textElem - The SVG element to append text to
|
||||||
|
* @param {object} node - The node object containing description and dimensions
|
||||||
|
* @param {object} conf - Configuration object
|
||||||
|
* @param {boolean} isVirtual - Whether this is for virtual height calculation
|
||||||
|
*/
|
||||||
|
const processHtmlContent = async function (textElem, node, conf, isVirtual = false) {
|
||||||
|
// Create temporary text to get initial dimensions
|
||||||
|
const sanitizedHtml = DOMPurify.sanitize(node.descr, { ALLOWED_TAGS: [] });
|
||||||
|
const tempText = textElem
|
||||||
|
.append('text')
|
||||||
|
.text(sanitizedHtml)
|
||||||
|
.attr('dy', '1em')
|
||||||
|
.attr('alignment-baseline', 'middle')
|
||||||
|
.attr('dominant-baseline', 'middle')
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.call(wrap, node.width);
|
||||||
|
|
||||||
|
if (!isVirtual) {
|
||||||
|
tempText.attr('visibility', 'hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
const bbox = tempText.node().getBBox();
|
||||||
|
tempText.remove();
|
||||||
|
|
||||||
|
// Create the actual HTML content
|
||||||
|
const textObj = await createText(
|
||||||
|
textElem,
|
||||||
|
node.descr,
|
||||||
|
{
|
||||||
|
useHtmlLabels: true,
|
||||||
|
width: node.width,
|
||||||
|
classes: 'timeline-node-label',
|
||||||
|
isNode: true,
|
||||||
|
},
|
||||||
|
conf
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isVirtual) {
|
||||||
|
select(textObj).attr('transform', 'translate(0, 0)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the foreign object
|
||||||
|
const foreignObject = textElem.select('foreignObject');
|
||||||
|
if (foreignObject.node()) {
|
||||||
|
foreignObject.attr('width', `${10 * node.width}px`).attr('height', `${10 * node.width}px`);
|
||||||
|
|
||||||
|
const div = foreignObject.select('div');
|
||||||
|
if (div.node()) {
|
||||||
|
div
|
||||||
|
.style('display', 'table-cell')
|
||||||
|
.style('white-space', 'nowrap')
|
||||||
|
.style('line-height', '1.5')
|
||||||
|
.style('max-width', node.width + 'px')
|
||||||
|
.style('text-align', 'center');
|
||||||
|
|
||||||
|
let divBBox = div.node().getBoundingClientRect();
|
||||||
|
|
||||||
|
if (divBBox.width === node.width) {
|
||||||
|
div
|
||||||
|
.style('display', 'table')
|
||||||
|
.style('white-space', 'break-spaces')
|
||||||
|
.style('width', node.width + 'px');
|
||||||
|
|
||||||
|
divBBox = div.node().getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreignObject.attr('width', node.width).attr('height', divBBox.height);
|
||||||
|
|
||||||
|
if (!isVirtual) {
|
||||||
|
foreignObject.attr('x', -node.width / 2).attr('y', 3);
|
||||||
|
|
||||||
|
div.style('width', node.width + 'px');
|
||||||
|
}
|
||||||
|
bbox.height = divBBox.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bbox;
|
||||||
|
};
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function (elem, rectData) {
|
||||||
const rectElem = elem.append('rect');
|
const rectElem = elem.append('rect');
|
||||||
rectElem.attr('x', rectData.x);
|
rectElem.attr('x', rectData.x);
|
||||||
@@ -409,6 +494,9 @@ const _drawTextCandidateFunc = (function () {
|
|||||||
.style('display', 'table-cell')
|
.style('display', 'table-cell')
|
||||||
.style('text-align', 'center')
|
.style('text-align', 'center')
|
||||||
.style('vertical-align', 'middle')
|
.style('vertical-align', 'middle')
|
||||||
|
.style('word-wrap', 'break-word')
|
||||||
|
.style('overflow-wrap', 'break-word')
|
||||||
|
.style('white-space', 'normal')
|
||||||
.text(content);
|
.text(content);
|
||||||
|
|
||||||
byTspan(content, body, x, y, width, height, textAttrs, conf);
|
byTspan(content, body, x, y, width, height, textAttrs, conf);
|
||||||
@@ -493,7 +581,7 @@ function wrap(text, width) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const drawNode = function (elem, node, fullSection, conf) {
|
export const drawNode = async function (elem, node, fullSection, conf) {
|
||||||
const section = (fullSection % MAX_SECTIONS) - 1;
|
const section = (fullSection % MAX_SECTIONS) - 1;
|
||||||
const nodeElem = elem.append('g');
|
const nodeElem = elem.append('g');
|
||||||
node.section = section;
|
node.section = section;
|
||||||
@@ -506,19 +594,28 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
|||||||
// Create the wrapped text element
|
// Create the wrapped text element
|
||||||
const textElem = nodeElem.append('g');
|
const textElem = nodeElem.append('g');
|
||||||
|
|
||||||
const txt = textElem
|
const hasHtml = /<[a-z][\S\s]*>/i.test(node.descr);
|
||||||
.append('text')
|
|
||||||
.text(node.descr)
|
if (hasHtml) {
|
||||||
.attr('dy', '1em')
|
const bbox = await processHtmlContent(textElem, node, conf, false);
|
||||||
.attr('alignment-baseline', 'middle')
|
node.height = bbox.height + node.padding;
|
||||||
.attr('dominant-baseline', 'middle')
|
node.height = Math.max(node.height, node.maxHeight);
|
||||||
.attr('text-anchor', 'middle')
|
node.width = node.width + 2 * node.padding;
|
||||||
.call(wrap, node.width);
|
} else {
|
||||||
const bbox = txt.node().getBBox();
|
const txt = textElem
|
||||||
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
.append('text')
|
||||||
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
.text(node.descr)
|
||||||
node.height = Math.max(node.height, node.maxHeight);
|
.attr('dy', '1em')
|
||||||
node.width = node.width + 2 * node.padding;
|
.attr('alignment-baseline', 'middle')
|
||||||
|
.attr('dominant-baseline', 'middle')
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.call(wrap, node.width);
|
||||||
|
const bbox = txt.node().getBBox();
|
||||||
|
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
||||||
|
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
||||||
|
node.height = Math.max(node.height, node.maxHeight);
|
||||||
|
node.width = node.width + 2 * node.padding;
|
||||||
|
}
|
||||||
|
|
||||||
textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
|
textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
|
||||||
|
|
||||||
@@ -528,17 +625,25 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
|||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getVirtualNodeHeight = function (elem, node, conf) {
|
export const getVirtualNodeHeight = async function (elem, node, conf) {
|
||||||
const textElem = elem.append('g');
|
const textElem = elem.append('g');
|
||||||
const txt = textElem
|
|
||||||
.append('text')
|
const hasHtml = /<[a-z][\S\s]*>/i.test(node.descr);
|
||||||
.text(node.descr)
|
|
||||||
.attr('dy', '1em')
|
let bbox;
|
||||||
.attr('alignment-baseline', 'middle')
|
if (hasHtml) {
|
||||||
.attr('dominant-baseline', 'middle')
|
bbox = await processHtmlContent(textElem, node, conf, true);
|
||||||
.attr('text-anchor', 'middle')
|
} else {
|
||||||
.call(wrap, node.width);
|
const txt = textElem
|
||||||
const bbox = txt.node().getBBox();
|
.append('text')
|
||||||
|
.text(node.descr)
|
||||||
|
.attr('dy', '1em')
|
||||||
|
.attr('alignment-baseline', 'middle')
|
||||||
|
.attr('dominant-baseline', 'middle')
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.call(wrap, node.width);
|
||||||
|
bbox = txt.node().getBBox();
|
||||||
|
}
|
||||||
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
||||||
textElem.remove();
|
textElem.remove();
|
||||||
return bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
return bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
||||||
|
@@ -25,7 +25,7 @@ interface TimelineTask {
|
|||||||
score: number;
|
score: number;
|
||||||
events: string[];
|
events: string[];
|
||||||
}
|
}
|
||||||
export const draw = function (text: string, id: string, version: string, diagObj: Diagram) {
|
export const draw = async function (text: string, id: string, version: string, diagObj: Diagram) {
|
||||||
//1. Fetch the configuration
|
//1. Fetch the configuration
|
||||||
const conf = getConfig();
|
const conf = getConfig();
|
||||||
const LEFT_MARGIN = conf.timeline?.leftMargin ?? 50;
|
const LEFT_MARGIN = conf.timeline?.leftMargin ?? 50;
|
||||||
@@ -76,7 +76,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
let hasSections = true;
|
let hasSections = true;
|
||||||
|
|
||||||
//Calculate the max height of the sections
|
//Calculate the max height of the sections
|
||||||
sections.forEach(function (section: string) {
|
for (const section of sections) {
|
||||||
const sectionNode: Block<string, number> = {
|
const sectionNode: Block<string, number> = {
|
||||||
number: sectionNumber,
|
number: sectionNumber,
|
||||||
descr: section,
|
descr: section,
|
||||||
@@ -85,10 +85,10 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
padding: 20,
|
padding: 20,
|
||||||
maxHeight: maxSectionHeight,
|
maxHeight: maxSectionHeight,
|
||||||
};
|
};
|
||||||
const sectionHeight = svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
|
const sectionHeight = await svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
|
||||||
log.debug('sectionHeight before draw', sectionHeight);
|
log.debug('sectionHeight before draw', sectionHeight);
|
||||||
maxSectionHeight = Math.max(maxSectionHeight, sectionHeight + 20);
|
maxSectionHeight = Math.max(maxSectionHeight, sectionHeight + 20);
|
||||||
});
|
}
|
||||||
|
|
||||||
//tasks length and maxEventCount
|
//tasks length and maxEventCount
|
||||||
let maxEventCount = 0;
|
let maxEventCount = 0;
|
||||||
@@ -106,7 +106,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
padding: 20,
|
padding: 20,
|
||||||
maxHeight: maxTaskHeight,
|
maxHeight: maxTaskHeight,
|
||||||
};
|
};
|
||||||
const taskHeight = svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
|
const taskHeight = await svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
|
||||||
log.debug('taskHeight before draw', taskHeight);
|
log.debug('taskHeight before draw', taskHeight);
|
||||||
maxTaskHeight = Math.max(maxTaskHeight, taskHeight + 20);
|
maxTaskHeight = Math.max(maxTaskHeight, taskHeight + 20);
|
||||||
|
|
||||||
@@ -123,9 +123,8 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
padding: 20,
|
padding: 20,
|
||||||
maxHeight: 50,
|
maxHeight: 50,
|
||||||
};
|
};
|
||||||
maxEventLineLengthTemp += svgDraw.getVirtualNodeHeight(svg, eventNode, conf);
|
maxEventLineLengthTemp += await svgDraw.getVirtualNodeHeight(svg, eventNode, conf);
|
||||||
}
|
}
|
||||||
// Add spacing between events (10px per event except the last one)
|
|
||||||
if (task.events.length > 0) {
|
if (task.events.length > 0) {
|
||||||
maxEventLineLengthTemp += (task.events.length - 1) * 10;
|
maxEventLineLengthTemp += (task.events.length - 1) * 10;
|
||||||
}
|
}
|
||||||
@@ -136,7 +135,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
log.debug('maxTaskHeight before draw', maxTaskHeight);
|
log.debug('maxTaskHeight before draw', maxTaskHeight);
|
||||||
|
|
||||||
if (sections && sections.length > 0) {
|
if (sections && sections.length > 0) {
|
||||||
sections.forEach((section) => {
|
for (const section of sections) {
|
||||||
//filter task where tasks.section == section
|
//filter task where tasks.section == section
|
||||||
const tasksForSection = tasks.filter((task) => task.section === section);
|
const tasksForSection = tasks.filter((task) => task.section === section);
|
||||||
|
|
||||||
@@ -150,7 +149,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
};
|
};
|
||||||
log.debug('sectionNode', sectionNode);
|
log.debug('sectionNode', sectionNode);
|
||||||
const sectionNodeWrapper = svg.append('g');
|
const sectionNodeWrapper = svg.append('g');
|
||||||
const node = svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
|
const node = await svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
|
||||||
log.debug('sectionNode output', node);
|
log.debug('sectionNode output', node);
|
||||||
|
|
||||||
sectionNodeWrapper.attr('transform', `translate(${masterX}, ${sectionBeginY})`);
|
sectionNodeWrapper.attr('transform', `translate(${masterX}, ${sectionBeginY})`);
|
||||||
@@ -159,7 +158,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
|
|
||||||
//draw tasks for this section
|
//draw tasks for this section
|
||||||
if (tasksForSection.length > 0) {
|
if (tasksForSection.length > 0) {
|
||||||
drawTasks(
|
await drawTasks(
|
||||||
svg,
|
svg,
|
||||||
tasksForSection,
|
tasksForSection,
|
||||||
sectionNumber,
|
sectionNumber,
|
||||||
@@ -178,11 +177,11 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
|
|
||||||
masterY = sectionBeginY;
|
masterY = sectionBeginY;
|
||||||
sectionNumber++;
|
sectionNumber++;
|
||||||
});
|
}
|
||||||
} else {
|
} else {
|
||||||
//draw tasks
|
//draw tasks
|
||||||
hasSections = false;
|
hasSections = false;
|
||||||
drawTasks(
|
await drawTasks(
|
||||||
svg,
|
svg,
|
||||||
tasks,
|
tasks,
|
||||||
sectionNumber,
|
sectionNumber,
|
||||||
@@ -236,7 +235,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
|||||||
// addSVGAccessibilityFields(diagObj.db, diagram, id);
|
// addSVGAccessibilityFields(diagObj.db, diagram, id);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawTasks = function (
|
export const drawTasks = async function (
|
||||||
diagram: Selection<SVGElement, unknown, null, undefined>,
|
diagram: Selection<SVGElement, unknown, null, undefined>,
|
||||||
tasks: TimelineTask[],
|
tasks: TimelineTask[],
|
||||||
sectionColor: number,
|
sectionColor: number,
|
||||||
@@ -265,7 +264,7 @@ export const drawTasks = function (
|
|||||||
// create task wrapper
|
// create task wrapper
|
||||||
|
|
||||||
const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
|
const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
|
||||||
const node = svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
|
const node = await svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
|
||||||
const taskHeight = node.height;
|
const taskHeight = node.height;
|
||||||
//log task height
|
//log task height
|
||||||
log.debug('taskHeight after draw', taskHeight);
|
log.debug('taskHeight after draw', taskHeight);
|
||||||
@@ -282,7 +281,7 @@ export const drawTasks = function (
|
|||||||
//add margin to task
|
//add margin to task
|
||||||
masterY += 100;
|
masterY += 100;
|
||||||
lineLength =
|
lineLength =
|
||||||
lineLength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
|
lineLength + (await drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf));
|
||||||
masterY -= 100;
|
masterY -= 100;
|
||||||
|
|
||||||
lineWrapper
|
lineWrapper
|
||||||
@@ -307,7 +306,7 @@ export const drawTasks = function (
|
|||||||
masterY = masterY - 10;
|
masterY = masterY - 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawEvents = function (
|
export const drawEvents = async function (
|
||||||
diagram: Selection<SVGElement, unknown, null, undefined>,
|
diagram: Selection<SVGElement, unknown, null, undefined>,
|
||||||
events: string[],
|
events: string[],
|
||||||
sectionColor: number,
|
sectionColor: number,
|
||||||
@@ -334,7 +333,7 @@ export const drawEvents = function (
|
|||||||
log.debug('eventNode', eventNode);
|
log.debug('eventNode', eventNode);
|
||||||
// create event wrapper
|
// create event wrapper
|
||||||
const eventWrapper = diagram.append('g').attr('class', 'eventWrapper');
|
const eventWrapper = diagram.append('g').attr('class', 'eventWrapper');
|
||||||
const node = svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf);
|
const node = await svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf);
|
||||||
const eventHeight = node.height;
|
const eventHeight = node.height;
|
||||||
maxEventHeight = maxEventHeight + eventHeight;
|
maxEventHeight = maxEventHeight + eventHeight;
|
||||||
eventWrapper.attr('transform', `translate(${masterX}, ${masterY})`);
|
eventWrapper.attr('transform', `translate(${masterX}, ${masterY})`);
|
||||||
|
@@ -17,6 +17,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.
|
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:
|
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
|
- theme
|
||||||
- fontFamily
|
- fontFamily
|
||||||
- logLevel
|
- logLevel
|
||||||
|
@@ -23,6 +23,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
|||||||
- **Plugins** - A plugin system for extending the functionality of Mermaid.
|
- **Plugins** - A plugin system for extending the functionality of Mermaid.
|
||||||
|
|
||||||
Official Mermaid Chart plugins:
|
Official Mermaid Chart plugins:
|
||||||
|
|
||||||
- [Mermaid Chart GPT](https://chatgpt.com/g/g-684cc36f30208191b21383b88650a45d-mermaid-chart-diagrams-and-charts)
|
- [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)
|
- [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)
|
- [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud)
|
||||||
|
@@ -33,11 +33,13 @@ The Mermaid Chart team is excited to introduce a new Visual Editor for Flowchart
|
|||||||
Learn more:
|
Learn more:
|
||||||
|
|
||||||
- Visual Editor For Flowcharts
|
- Visual Editor For Flowcharts
|
||||||
|
|
||||||
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-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)
|
- [Demo video](https://www.youtube.com/watch?v=5aja0gijoO0)
|
||||||
|
|
||||||
- Visual Editor For Sequence diagrams
|
- Visual Editor For Sequence diagrams
|
||||||
|
|
||||||
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-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)
|
- [Demo video](https://youtu.be/imc2u5_N6Dc)
|
||||||
|
@@ -21,21 +21,21 @@
|
|||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"jiti": "^2.4.2",
|
"jiti": "^2.4.2",
|
||||||
"mermaid": "workspace:^",
|
"mermaid": "workspace:^",
|
||||||
"vue": "^3.5.19"
|
"vue": "^3.4.38"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/carbon": "^1.2.13",
|
"@iconify-json/carbon": "^1.1.37",
|
||||||
"@unocss/reset": "^66.0.0",
|
"@unocss/reset": "^66.0.0",
|
||||||
"@vite-pwa/vitepress": "^1.0.0",
|
"@vite-pwa/vitepress": "^1.0.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"https-localhost": "^4.7.1",
|
"https-localhost": "^4.7.1",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"unocss": "^66.0.0",
|
"unocss": "^66.0.0",
|
||||||
"unplugin-vue-components": "^28.4.1",
|
"unplugin-vue-components": "^28.4.0",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
"vite-plugin-pwa": "^1.0.3",
|
"vite-plugin-pwa": "^1.0.0",
|
||||||
"vitepress": "1.6.4",
|
"vitepress": "1.6.3",
|
||||||
"workbox-window": "^7.3.0"
|
"workbox-window": "^7.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -83,6 +83,7 @@ The following unfinished features are not supported in the short term.
|
|||||||
- [ ] Legend
|
- [ ] Legend
|
||||||
|
|
||||||
- [x] System Context
|
- [x] System Context
|
||||||
|
|
||||||
- [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
|
- [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] Person_Ext
|
- [x] Person_Ext
|
||||||
- [x] System(alias, label, ?descr, ?sprite, ?tags, $link)
|
- [x] System(alias, label, ?descr, ?sprite, ?tags, $link)
|
||||||
@@ -96,6 +97,7 @@ The following unfinished features are not supported in the short term.
|
|||||||
- [x] System_Boundary
|
- [x] System_Boundary
|
||||||
|
|
||||||
- [x] Container diagram
|
- [x] Container diagram
|
||||||
|
|
||||||
- [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
- [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] ContainerDb
|
- [x] ContainerDb
|
||||||
- [x] ContainerQueue
|
- [x] ContainerQueue
|
||||||
@@ -105,6 +107,7 @@ The following unfinished features are not supported in the short term.
|
|||||||
- [x] Container_Boundary(alias, label, ?tags, $link)
|
- [x] Container_Boundary(alias, label, ?tags, $link)
|
||||||
|
|
||||||
- [x] Component diagram
|
- [x] Component diagram
|
||||||
|
|
||||||
- [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
- [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] ComponentDb
|
- [x] ComponentDb
|
||||||
- [x] ComponentQueue
|
- [x] ComponentQueue
|
||||||
@@ -113,15 +116,18 @@ The following unfinished features are not supported in the short term.
|
|||||||
- [x] ComponentQueue_Ext
|
- [x] ComponentQueue_Ext
|
||||||
|
|
||||||
- [x] Dynamic diagram
|
- [x] Dynamic diagram
|
||||||
|
|
||||||
- [x] RelIndex(index, from, to, label, ?tags, $link)
|
- [x] RelIndex(index, from, to, label, ?tags, $link)
|
||||||
|
|
||||||
- [x] Deployment diagram
|
- [x] Deployment diagram
|
||||||
|
|
||||||
- [x] Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link)
|
- [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(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_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] Node_R(alias, label, ?type, ?descr, ?sprite, ?tags, $link): right aligned Node()
|
||||||
|
|
||||||
- [x] Relationship Types
|
- [x] Relationship Types
|
||||||
|
|
||||||
- [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
- [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
|
||||||
- [x] BiRel (bidirectional relationship)
|
- [x] BiRel (bidirectional relationship)
|
||||||
- [x] Rel_U, Rel_Up
|
- [x] Rel_U, Rel_Up
|
||||||
|
@@ -590,11 +590,17 @@ flowchart TD
|
|||||||
- `b`
|
- `b`
|
||||||
- **w**: The width of the image. If not defined, this will default to the natural width of the image.
|
- **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.
|
- **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 height (`h`) accordingly to the width (`w`). 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 width (`w`) accordingly to the height (`h`). If not defined, this will default to `off` Possible values are:
|
||||||
- `on`
|
- `on`
|
||||||
- `off`
|
- `off`
|
||||||
|
|
||||||
These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging.
|
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
|
||||||
|
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" }
|
||||||
|
```
|
||||||
|
|
||||||
## Links between nodes
|
## Links between nodes
|
||||||
|
|
||||||
|
@@ -46,6 +46,78 @@ sequenceDiagram
|
|||||||
Bob->>Alice: Hi Alice
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
||||||
### Aliases
|
### Aliases
|
||||||
|
|
||||||
The actor can have a convenient identifier and a descriptive label.
|
The actor can have a convenient identifier and a descriptive label.
|
||||||
|
@@ -13,6 +13,18 @@ export interface NodeMetaData {
|
|||||||
ticket?: string;
|
ticket?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ParticipantMetaData {
|
||||||
|
type?:
|
||||||
|
| 'actor'
|
||||||
|
| 'participant'
|
||||||
|
| 'boundary'
|
||||||
|
| 'control'
|
||||||
|
| 'entity'
|
||||||
|
| 'database'
|
||||||
|
| 'collections'
|
||||||
|
| 'queue';
|
||||||
|
}
|
||||||
|
|
||||||
export interface EdgeMetaData {
|
export interface EdgeMetaData {
|
||||||
animation?: 'fast' | 'slow';
|
animation?: 'fast' | 'slow';
|
||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
|
@@ -44,16 +44,20 @@ ValueConverter -->> Package: Return AST
|
|||||||
```
|
```
|
||||||
|
|
||||||
- When to override `TokenBuilder`?
|
- When to override `TokenBuilder`?
|
||||||
|
|
||||||
- To override keyword rules.
|
- To override keyword rules.
|
||||||
- To override terminal rules that need a custom function.
|
- To override terminal rules that need a custom function.
|
||||||
- To manually reorder the list of rules.
|
- To manually reorder the list of rules.
|
||||||
|
|
||||||
- When to override `Lexer`?
|
- When to override `Lexer`?
|
||||||
|
|
||||||
- To modify input before tokenizing.
|
- To modify input before tokenizing.
|
||||||
- To insert/modify tokens that cannot or have not been parsed.
|
- To insert/modify tokens that cannot or have not been parsed.
|
||||||
|
|
||||||
- When to override `LangiumParser`?
|
- When to override `LangiumParser`?
|
||||||
|
|
||||||
- To insert or modify attributes that can't be parsed.
|
- To insert or modify attributes that can't be parsed.
|
||||||
|
|
||||||
- When to override `ValueConverter`?
|
- When to override `ValueConverter`?
|
||||||
|
|
||||||
- To modify the returned value from the parser.
|
- To modify the returned value from the parser.
|
||||||
|
@@ -154,6 +154,7 @@
|
|||||||
### Minor Changes
|
### 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:
|
- [#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
|
- Config
|
||||||
- Init directive (%%{ init: { 'flowchart': { 'curve': '...' } } }%%)
|
- Init directive (%%{ init: { 'flowchart': { 'curve': '...' } } }%%)
|
||||||
- LinkStyle command (linkStyle default interpolate ...)
|
- LinkStyle command (linkStyle default interpolate ...)
|
||||||
@@ -172,12 +173,14 @@
|
|||||||
### Minor Changes
|
### 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
|
- [#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 `&`
|
- Incorrect label mapping for nodes when using `&`
|
||||||
- Syntax error when `}` with trailing spaces before new line
|
- 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
|
- [#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
|
- [#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 directions
|
||||||
- Added support for hand drawn look
|
- Added support for hand drawn look
|
||||||
|
|
||||||
@@ -226,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
|
- [#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:
|
- [#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.
|
- Updates the class diagram to the new unified way of rendering.
|
||||||
- Includes a new "classBox" shape to be used in diagrams
|
- Includes a new "classBox" shape to be used in diagrams
|
||||||
- Other updates such as:
|
- Other updates such as:
|
||||||
|
3406
pnpm-lock.yaml
generated
3406
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user