mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-12 03:49:43 +02:00
Compare commits
3 Commits
6638-seque
...
renovate/p
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cf08ba0ef8 | ||
![]() |
e7811886c3 | ||
![]() |
32eda8565c |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Add IDs in architecture diagrams
|
|
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
feat: Add half-arrowheads (solid & stick) and central connection support
|
|
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
feat: Add IDs in architecture diagrams
|
|
@@ -8,7 +8,6 @@ compositTitleSize
|
|||||||
cose
|
cose
|
||||||
curv
|
curv
|
||||||
doublecircle
|
doublecircle
|
||||||
elem
|
|
||||||
elems
|
elems
|
||||||
gantt
|
gantt
|
||||||
gitgraph
|
gitgraph
|
||||||
|
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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||||
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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||||
|
|
||||||
# ℹ️ 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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||||
|
2
.github/workflows/e2e-applitools.yml
vendored
2
.github/workflows/e2e-applitools.yml
vendored
@@ -53,7 +53,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@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||||
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@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||||
with:
|
with:
|
||||||
runTests: false
|
runTests: false
|
||||||
|
|
||||||
- name: Cypress run
|
- name: Cypress run
|
||||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||||
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@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
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@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||||
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@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
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@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||||
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@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||||
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@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
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@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
|
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
|
||||||
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@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||||
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@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
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@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||||
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@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
branch: update-browserslist
|
branch: update-browserslist
|
||||||
title: Update Browserslist
|
title: Update Browserslist
|
||||||
|
@@ -98,12 +98,12 @@ describe('Configuration', () => {
|
|||||||
it('should handle arrowMarkerAbsolute set to true', () => {
|
it('should handle arrowMarkerAbsolute set to true', () => {
|
||||||
renderGraph(
|
renderGraph(
|
||||||
`flowchart TD
|
`flowchart TD
|
||||||
A[Christmas] -->|Get money| B(Go shopping)
|
A[Christmas] -->|Get money| B(Go shopping)
|
||||||
B --> C{Let me think}
|
B --> C{Let me think}
|
||||||
C -->|One| D[Laptop]
|
C -->|One| D[Laptop]
|
||||||
C -->|Two| E[iPhone]
|
C -->|Two| E[iPhone]
|
||||||
C -->|Three| F[fa:fa-car Car]
|
C -->|Three| F[fa:fa-car Car]
|
||||||
`,
|
`,
|
||||||
{
|
{
|
||||||
arrowMarkerAbsolute: true,
|
arrowMarkerAbsolute: true,
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,8 @@ describe('Configuration', () => {
|
|||||||
cy.get('path')
|
cy.get('path')
|
||||||
.first()
|
.first()
|
||||||
.should('have.attr', 'marker-end')
|
.should('have.attr', 'marker-end')
|
||||||
.and('include', 'url(http://localhost');
|
.should('exist')
|
||||||
|
.and('include', 'url(http\\:\\/\\/localhost');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should not taint the initial configuration when using multiple directives', () => {
|
it('should not taint the initial configuration when using multiple directives', () => {
|
||||||
|
@@ -109,7 +109,7 @@ describe('Flowchart ELK', () => {
|
|||||||
const style = svg.attr('style');
|
const style = svg.attr('style');
|
||||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||||
verifyNumber(maxWidthValue, 380, 15);
|
verifyNumber(maxWidthValue, 380);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
|
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
|
||||||
@@ -128,7 +128,7 @@ describe('Flowchart ELK', () => {
|
|||||||
const width = parseFloat(svg.attr('width'));
|
const width = parseFloat(svg.attr('width'));
|
||||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||||
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
|
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
|
||||||
verifyNumber(width, 380, 15);
|
verifyNumber(width, 380);
|
||||||
expect(svg).to.not.have.attr('style');
|
expect(svg).to.not.have.attr('style');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -655,126 +655,5 @@ describe('Sequence Diagram Special Cases', () => {
|
|||||||
expect(svg).to.not.have.attr('style');
|
expect(svg).to.not.have.attr('style');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Central Connection Rendering Tests', () => {
|
|
||||||
it('should render central connection circles on actor vertical lines', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Central connection
|
|
||||||
Bob ()-->> Charlie: Reverse central connection
|
|
||||||
Charlie ()<<-->>() Alice: Dual central connection`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with different arrow types', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()->>() Bob: Solid open arrow
|
|
||||||
Alice ()-->>() Bob: Dotted open arrow
|
|
||||||
Alice ()-x() Bob: Solid cross
|
|
||||||
Alice ()--x() Bob: Dotted cross
|
|
||||||
Alice ()->() Bob: Solid arrow`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with bidirectional arrows', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()<<->>() Bob: Bidirectional solid
|
|
||||||
Alice ()<<-->>() Bob: Bidirectional dotted`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with activations', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Activate Bob
|
|
||||||
activate Bob
|
|
||||||
Bob ()-->> Charlie: Message to Charlie
|
|
||||||
Bob ()->>() Alice: Response to Alice
|
|
||||||
deactivate Bob`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections mixed with normal messages', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ->> Bob: Normal message
|
|
||||||
Bob ()->>() Charlie: Central connection
|
|
||||||
Charlie -->> Alice: Normal dotted message
|
|
||||||
Alice ()<<-->>() Bob: Dual central connection
|
|
||||||
Bob -x Charlie: Normal cross message`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with notes', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Central connection
|
|
||||||
Note over Alice,Bob: Central connection note
|
|
||||||
Bob ()-->> Charlie: Reverse central connection
|
|
||||||
Note right of Charlie: Response note
|
|
||||||
Charlie ()<<-->>() Alice: Dual central connection`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with loops and alternatives', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
loop Every minute
|
|
||||||
Alice ()->>() Bob: Central heartbeat
|
|
||||||
Bob ()-->> Charlie: Forward heartbeat
|
|
||||||
end
|
|
||||||
alt Success
|
|
||||||
Charlie ()<<-->>() Alice: Success response
|
|
||||||
else Failure
|
|
||||||
Charlie ()-x() Alice: Failure response
|
|
||||||
end`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render central connections with different participant types', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
actor Bob
|
|
||||||
participant Charlie@{"type":"boundary"}
|
|
||||||
participant David@{"type":"control"}
|
|
||||||
participant Eve@{"type":"entity"}
|
|
||||||
Alice ()->>() Bob: To actor
|
|
||||||
Bob ()-->> Charlie: To boundary
|
|
||||||
Charlie ()->>() David: To control
|
|
||||||
David ()<<-->>() Eve: To entity
|
|
||||||
Eve ()-x() Alice: Back to participant`,
|
|
||||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1053,167 +1053,4 @@ describe('Sequence diagram', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('render new arrow type', () => {
|
|
||||||
it('should render Solid half arrow top', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice -|\\ John: Hello John, how are you?
|
|
||||||
Alice-|\\ John: Hi Alice, I can hear you!
|
|
||||||
Alice -|\\ John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render Solid half arrow bottom', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice-|/John: Hello John, how are you?
|
|
||||||
Alice-|/John: Hi Alice, I can hear you!
|
|
||||||
Alice-|/John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice-\\\\John: Hello John, how are you?
|
|
||||||
Alice-\\\\John: Hi Alice, I can hear you!
|
|
||||||
Alice-\\\\John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render Stick half arrow bottom ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice-//John: Hello John, how are you?
|
|
||||||
Alice-//John: Hi Alice, I can hear you!
|
|
||||||
Alice-//John: Test
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should render Solid half arrow top reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice/|-John: Hello Alice, how are you?
|
|
||||||
Alice/|-John: Hi Alice, I can hear you!
|
|
||||||
Alice/|-John: Test
|
|
||||||
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow bottom reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`sequenceDiagram
|
|
||||||
Alice \\|- John: Hello Alice, how are you?
|
|
||||||
Alice \\|- John: Hi Alice, I can hear you!
|
|
||||||
Alice \\|- John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice //-John: Hello Alice, how are you?
|
|
||||||
Alice //-John: Hi Alice, I can hear you!
|
|
||||||
Alice //-John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow bottom reverse ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice \\\\-John: Hello Alice, how are you?
|
|
||||||
Alice \\\\-John: Hi Alice, I can hear you!
|
|
||||||
Alice \\\\-John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow top dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice --|\\John: Hello John, how are you?
|
|
||||||
Alice --|\\John: Hi Alice, I can hear you!
|
|
||||||
Alice --|\\John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow bottom dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice --|/John: Hello John, how are you?
|
|
||||||
Alice --|/John: Hi Alice, I can hear you!
|
|
||||||
Alice --|/John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice--\\\\John: Hello John, how are you?
|
|
||||||
Alice--\\\\John: Hi Alice, I can hear you!
|
|
||||||
Alice--\\\\John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow bottom dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice--//John: Hello John, how are you?
|
|
||||||
Alice--//John: Hi Alice, I can hear you!
|
|
||||||
Alice--//John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow top reverse dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice/|--John: Hello Alice, how are you?
|
|
||||||
Alice/|--John: Hi Alice, I can hear you!
|
|
||||||
Alice/|--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Solid half arrow bottom reverse dotted', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice\\|--John: Hello Alice, how are you?
|
|
||||||
Alice\\|--John: Hi Alice, I can hear you!
|
|
||||||
Alice\\|--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow top reverse dotted ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice//--John: Hello Alice, how are you?
|
|
||||||
Alice//--John: Hi Alice, I can hear you!
|
|
||||||
Alice//--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render Stick half arrow bottom reverse dotted ', () => {
|
|
||||||
imgSnapshotTest(
|
|
||||||
`
|
|
||||||
sequenceDiagram
|
|
||||||
Alice\\\\--John: Hello Alice, how are you?
|
|
||||||
Alice\\\\--John: Hi Alice, I can hear you!
|
|
||||||
Alice\\\\--John: Test`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -32,8 +32,26 @@
|
|||||||
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Recursive:wght@300..1000&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.recursive-mermaid {
|
||||||
|
font-family: 'Recursive', sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-variation-settings:
|
||||||
|
'slnt' 0,
|
||||||
|
'CASL' 0,
|
||||||
|
'CRSV' 0.5,
|
||||||
|
'MONO' 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
/* background: rgb(221, 208, 208); */
|
/* background: rgb(221, 208, 208); */
|
||||||
/* background: #333; */
|
/* background: #333; */
|
||||||
@@ -45,7 +63,9 @@
|
|||||||
h1 {
|
h1 {
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
.mermaid {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
.mermaid2 {
|
.mermaid2 {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -83,6 +103,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.class2 {
|
||||||
|
fill: red;
|
||||||
|
fill-opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* tspan {
|
/* tspan {
|
||||||
font-size: 6px !important;
|
font-size: 6px !important;
|
||||||
} */
|
} */
|
||||||
@@ -105,194 +130,76 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: tidy-tree
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
|
||||||
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
flowchart-elk TB
|
mindmap
|
||||||
c1-->a2
|
root((mindmap is a long thing))
|
||||||
subgraph one
|
A
|
||||||
a1-->a2
|
B
|
||||||
end
|
C
|
||||||
subgraph two
|
D
|
||||||
b1-->b2
|
</pre
|
||||||
end
|
|
||||||
subgraph three
|
|
||||||
c1-->c2
|
|
||||||
end
|
|
||||||
one --> two
|
|
||||||
three --> two
|
|
||||||
two --> c2
|
|
||||||
|
|
||||||
</pre
|
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
flowchart TB
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
process_C
|
A
|
||||||
subgraph container_Alpha
|
B
|
||||||
subgraph process_B
|
</pre
|
||||||
pppB
|
|
||||||
end
|
|
||||||
subgraph process_A
|
|
||||||
pppA
|
|
||||||
end
|
|
||||||
process_B-->|via_AWSBatch|container_Beta
|
|
||||||
process_A-->|messages|container_Beta
|
|
||||||
end
|
|
||||||
|
|
||||||
</pre
|
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
flowchart TB
|
mindmap
|
||||||
subgraph container_Beta
|
root((mindmap))
|
||||||
process_C
|
A
|
||||||
end
|
a
|
||||||
subgraph container_Alpha
|
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
subgraph process_B
|
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
pppB
|
b
|
||||||
end
|
c
|
||||||
subgraph process_A
|
d
|
||||||
pppA
|
B
|
||||||
end
|
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
process_B-->|via_AWSBatch|container_Beta
|
D
|
||||||
process_A-->|messages|container_Beta
|
apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
end
|
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
|
||||||
</pre
|
</pre>
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
flowchart TB
|
|
||||||
subgraph container_Beta
|
|
||||||
process_C
|
|
||||||
end
|
|
||||||
|
|
||||||
process_B-->|via_AWSBatch|container_Beta
|
|
||||||
|
|
||||||
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
classDiagram
|
|
||||||
note "I love this diagram!\nDo you love it?"
|
|
||||||
Class01 <|-- AveryLongClass : Cool
|
|
||||||
<<interface>> Class01
|
|
||||||
Class03 "1" *-- "*" Class04
|
|
||||||
Class05 "1" o-- "many" Class06
|
|
||||||
Class07 "1" .. "*" Class08
|
|
||||||
Class09 "1" --> "*" C2 : Where am i?
|
|
||||||
Class09 "*" --* "*" C3
|
|
||||||
Class09 "1" --|> "1" Class07
|
|
||||||
Class12 <|.. Class08
|
|
||||||
Class11 ..>Class12
|
|
||||||
Class07 : equals()
|
|
||||||
Class07 : Object[] elementData
|
|
||||||
Class01 : size()
|
|
||||||
Class01 : int chimp
|
|
||||||
Class01 : int gorilla
|
|
||||||
Class01 : -int privateChimp
|
|
||||||
Class01 : +int publicGorilla
|
|
||||||
Class01 : #int protectedMarmoset
|
|
||||||
Class08 <--> C2: Cool label
|
|
||||||
class Class10 {
|
|
||||||
<<service>>
|
|
||||||
int id
|
|
||||||
test()
|
|
||||||
}
|
|
||||||
note for Class10 "Cool class\nI said it's very cool class!"
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
requirementDiagram
|
|
||||||
requirement test_req {
|
|
||||||
id: 1
|
|
||||||
text: the test text.
|
|
||||||
risk: high
|
|
||||||
verifymethod: test
|
|
||||||
}
|
|
||||||
|
|
||||||
element test_entity {
|
|
||||||
type: simulation
|
|
||||||
}
|
|
||||||
|
|
||||||
test_entity - satisfies -> test_req
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
flowchart-elk TB
|
|
||||||
internet
|
|
||||||
nat
|
|
||||||
router
|
|
||||||
compute1
|
|
||||||
|
|
||||||
subgraph project
|
|
||||||
router
|
|
||||||
nat
|
|
||||||
subgraph subnet1
|
|
||||||
compute1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% router --> subnet1
|
|
||||||
subnet1 --> nat
|
|
||||||
%% nat --> internet
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
---
|
|
||||||
flowchart-elk TB
|
|
||||||
internet
|
|
||||||
nat
|
|
||||||
router
|
|
||||||
lb1
|
|
||||||
lb2
|
|
||||||
compute1
|
|
||||||
compute2
|
|
||||||
subgraph project
|
|
||||||
router
|
|
||||||
nat
|
|
||||||
subgraph subnet1
|
|
||||||
compute1
|
|
||||||
lb1
|
|
||||||
end
|
|
||||||
subgraph subnet2
|
|
||||||
compute2
|
|
||||||
lb2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
internet --> router
|
|
||||||
router --> subnet1 & subnet2
|
|
||||||
subnet1 & subnet2 --> nat --> internet
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
@@ -320,27 +227,106 @@ treemap
|
|||||||
"Leaf 2.2": 25
|
"Leaf 2.2": 25
|
||||||
"Leaf 2.3": 12
|
"Leaf 2.3": 12
|
||||||
|
|
||||||
</pre>
|
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||||
<pre id="diagram5" class="mermaid">
|
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
curve: rounded
|
|
||||||
---
|
|
||||||
flowchart LR
|
|
||||||
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
|
|
||||||
I --> D & D
|
|
||||||
D@{ shape: question}
|
|
||||||
I@{ shape: question}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
treemap:
|
||||||
|
valueFormat: '$0,0'
|
||||||
|
---
|
||||||
|
treemap
|
||||||
|
"Budget"
|
||||||
|
"Operations"
|
||||||
|
"Salaries": 7000
|
||||||
|
"Equipment": 2000
|
||||||
|
"Supplies": 1000
|
||||||
|
"Marketing"
|
||||||
|
"Advertising": 4000
|
||||||
|
"Events": 1000
|
||||||
|
|
||||||
|
</pre
|
||||||
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
|
treemap
|
||||||
|
title Accessible Treemap Title
|
||||||
|
"Category A"
|
||||||
|
"Item A1": 10
|
||||||
|
"Item A2": 20
|
||||||
|
"Category B"
|
||||||
|
"Item B1": 15
|
||||||
|
"Item B2": 25
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: tidy-tree
|
layout: tidy-tree
|
||||||
---
|
---
|
||||||
mindmap
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
a
|
||||||
|
apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
b
|
||||||
|
apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
|
||||||
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: tidy-tree
|
||||||
|
---
|
||||||
|
flowchart TB
|
||||||
|
A --> n0["1"]
|
||||||
|
A --> n1["2"]
|
||||||
|
A --> n2["3"]
|
||||||
|
A --> n3["4"] --> Q & R & S & T
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart TB
|
||||||
|
A --> n0["1"]
|
||||||
|
A --> n1["2"]
|
||||||
|
A --> n2["3"]
|
||||||
|
A --> n3["4"] --> Q & R & S & T
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: dagre
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap is a long thing))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: cose-bilkent
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
root((mindmap))
|
root((mindmap))
|
||||||
Origins
|
Origins
|
||||||
Long history
|
Long history
|
||||||
@@ -348,7 +334,7 @@ treemap
|
|||||||
Popularisation
|
Popularisation
|
||||||
British popular psychology author Tony Buzan
|
British popular psychology author Tony Buzan
|
||||||
Research
|
Research
|
||||||
On effectiveness<br/>and features
|
On effectiveness<br/>and features
|
||||||
On Automatic creation
|
On Automatic creation
|
||||||
Uses
|
Uses
|
||||||
Creative techniques
|
Creative techniques
|
||||||
@@ -357,112 +343,105 @@ treemap
|
|||||||
Tools
|
Tools
|
||||||
Pen and paper
|
Pen and paper
|
||||||
Mermaid
|
Mermaid
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
flowchart:
|
---
|
||||||
curve: linear
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: cose-bilkent
|
||||||
---
|
---
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A[A] --> B[B]
|
root{mindmap} --- Origins --- Europe
|
||||||
A[A] --- B([C])
|
Origins --> Asia
|
||||||
A@{ shape: diamond}
|
root --- Background --- Rich
|
||||||
%%B@{ shape: diamond}
|
Background --- Poor
|
||||||
|
subgraph apa
|
||||||
|
Background
|
||||||
|
Poor
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
flowchart:
|
|
||||||
curve: linear
|
|
||||||
---
|
---
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A[A] -- Mermaid js --> B[B]
|
root{mindmap} --- Origins --- Europe
|
||||||
A[A] -- Mermaid js --- B[B]
|
Origins --> Asia
|
||||||
A@{ shape: diamond}
|
root --- Background --- Rich
|
||||||
B@{ shape: diamond}
|
Background --- Poor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
flowchart
|
||||||
config:
|
D(("for D"))
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
curve: rounded
|
|
||||||
---
|
|
||||||
flowchart LR
|
|
||||||
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
|
|
||||||
I --> D & D
|
|
||||||
D@{ shape: question}
|
|
||||||
I@{ shape: question}
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
curve: rounded
|
|
||||||
elk:
|
|
||||||
nodePlacementStrategy: NETWORK_SIMPLEX
|
|
||||||
---
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
|
A e1@==> B
|
||||||
D --> I & I
|
e1@{ animate: true}
|
||||||
a["a"]
|
|
||||||
D@{ shape: trap-b}
|
|
||||||
I@{ shape: lean-l}
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
|
|
||||||
---
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
%% subgraph s1["Untitled subgraph"]
|
A e1@--> B
|
||||||
C["Evaluate"]
|
classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round
|
||||||
%% end
|
class e1 animate
|
||||||
|
|
||||||
B --> C
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<h2>infinite</h2>
|
||||||
---
|
<pre id="diagram4" class="mermaid2">
|
||||||
config:
|
|
||||||
layout: elk
|
|
||||||
flowchart:
|
|
||||||
//curve: linear
|
|
||||||
---
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
%% A ==> B
|
A e1@--> B
|
||||||
%% A2 --> B2
|
classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
|
||||||
A{A} --> B((Bo boo)) & B & B & B
|
class e1 animate
|
||||||
|
</pre>
|
||||||
|
<h2>Mermaid - edge-animation-slow</h2>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
A e1@--> B
|
||||||
|
e1@{ animation: fast}
|
||||||
|
</pre>
|
||||||
|
<h2>Mermaid - edge-animation-fast</h2>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
A e1@--> B
|
||||||
|
classDef animate stroke-dasharray: 1000,stroke-dashoffset: 1000,animation: dash 10s linear;
|
||||||
|
class e1 edge-animation-fast
|
||||||
|
</pre>
|
||||||
|
|
||||||
</pre>
|
<pre id="diagram4" class="mermaid2">
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
info </pre
|
||||||
config:
|
>
|
||||||
layout: elk
|
<pre id="diagram4" class="mermaid2">
|
||||||
theme: default
|
|
||||||
look: classic
|
|
||||||
---
|
|
||||||
flowchart LR
|
|
||||||
subgraph s1["APA"]
|
|
||||||
D{"Use the editor"}
|
|
||||||
end
|
|
||||||
subgraph S2["S2"]
|
|
||||||
s1
|
|
||||||
I>"fa:fa-code Text"]
|
|
||||||
E["E"]
|
|
||||||
end
|
|
||||||
D -- Mermaid js --> I
|
|
||||||
D --> I & E
|
|
||||||
E --> I
|
|
||||||
</pre>
|
|
||||||
<pre id="diagram4" class="mermaid">
|
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -487,7 +466,7 @@ config:
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -500,7 +479,45 @@ config:
|
|||||||
D-->I
|
D-->I
|
||||||
D-->I
|
D-->I
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
a
|
||||||
|
subgraph s0["APA"]
|
||||||
|
subgraph s8["APA"]
|
||||||
|
subgraph s1["APA"]
|
||||||
|
D{"X"}
|
||||||
|
E[Q]
|
||||||
|
end
|
||||||
|
subgraph s3["BAPA"]
|
||||||
|
F[Q]
|
||||||
|
I
|
||||||
|
end
|
||||||
|
D --> I
|
||||||
|
D --> I
|
||||||
|
D --> I
|
||||||
|
|
||||||
|
I{"X"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
a
|
||||||
|
D{"Use the editor"}
|
||||||
|
|
||||||
|
D -- Mermaid js --> I{"fa:fa-code Text"}
|
||||||
|
D-->I
|
||||||
|
D-->I
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -539,7 +556,7 @@ flowchart LR
|
|||||||
n8@{ shape: rect}
|
n8@{ shape: rect}
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -555,7 +572,7 @@ flowchart LR
|
|||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -564,7 +581,7 @@ flowchart LR
|
|||||||
A{A} --> B & C
|
A{A} --> B & C
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -576,7 +593,7 @@ flowchart LR
|
|||||||
end
|
end
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
layout: elk
|
layout: elk
|
||||||
@@ -594,7 +611,7 @@ flowchart LR
|
|||||||
|
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
kanban:
|
kanban:
|
||||||
@@ -613,81 +630,81 @@ kanban
|
|||||||
task3[💻 Develop login feature]@{ ticket: 103 }
|
task3[💻 Develop login feature]@{ ticket: 103 }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||||
A:::AClass
|
A:::AClass
|
||||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
kanban
|
kanban
|
||||||
id2[In progress]
|
id2[In progress]
|
||||||
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
kanban:
|
kanban:
|
||||||
@@ -751,22 +768,18 @@ kanban
|
|||||||
alert('It worked');
|
alert('It worked');
|
||||||
}
|
}
|
||||||
await mermaid.initialize({
|
await mermaid.initialize({
|
||||||
// theme: 'base',
|
// theme: 'forest',
|
||||||
// theme: 'default',
|
// theme: 'default',
|
||||||
// theme: 'forest',
|
// theme: 'forest',
|
||||||
// handDrawnSeed: 12,
|
// handDrawnSeed: 12,
|
||||||
// look: 'handDrawn',
|
// look: 'handDrawn',
|
||||||
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
||||||
// layout: 'dagre',
|
// layout: 'dagre',
|
||||||
layout: 'elk',
|
// layout: 'elk',
|
||||||
// layout: 'fixed',
|
// layout: 'fixed',
|
||||||
// htmlLabels: false,
|
// htmlLabels: false,
|
||||||
flowchart: { titleTopMargin: 10 },
|
flowchart: { titleTopMargin: 10 },
|
||||||
|
fontFamily: "'Recursive', sans-serif",
|
||||||
// fontFamily: 'Caveat',
|
|
||||||
// fontFamily: 'Kalam',
|
|
||||||
// fontFamily: 'courier',
|
|
||||||
fontFamily: 'arial',
|
|
||||||
sequence: {
|
sequence: {
|
||||||
actorFontFamily: 'courier',
|
actorFontFamily: 'courier',
|
||||||
noteFontFamily: 'courier',
|
noteFontFamily: 'courier',
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
|
@@ -29,7 +29,8 @@ In GitHub, you first [**fork a mermaid repository**](https://github.com/mermaid-
|
|||||||
|
|
||||||
Then you **clone** a copy to your local development machine (e.g. where you code) to make a copy with all the files to work with.
|
Then you **clone** a copy to your local development machine (e.g. where you code) to make a copy with all the files to work with.
|
||||||
|
|
||||||
> **💡 Tip** > [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
> **💡 Tip**
|
||||||
|
> [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone git@github.com/your-fork/mermaid
|
git clone git@github.com/your-fork/mermaid
|
||||||
|
@@ -33,7 +33,8 @@ mindmap
|
|||||||
|
|
||||||
## Join the Development
|
## Join the Development
|
||||||
|
|
||||||
> **💡 Tip** > **Check out our** [**detailed contribution guide**](./contributing.md).
|
> **💡 Tip**
|
||||||
|
> **Check out our** [**detailed contribution guide**](./contributing.md).
|
||||||
|
|
||||||
Where to start:
|
Where to start:
|
||||||
|
|
||||||
@@ -47,7 +48,8 @@ Where to start:
|
|||||||
|
|
||||||
## A Question Or a Suggestion?
|
## A Question Or a Suggestion?
|
||||||
|
|
||||||
> **💡 Tip** > **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
|
> **💡 Tip**
|
||||||
|
> **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
|
||||||
|
|
||||||
If you have faced a vulnerability [report it to us](./security.md).
|
If you have faced a vulnerability [report it to us](./security.md).
|
||||||
|
|
||||||
|
@@ -22,7 +22,6 @@ While directives allow you to change most of the default configuration settings,
|
|||||||
Mermaid basically supports two types of configuration options to be overridden by directives.
|
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
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: LayoutLoaderDefinition
|
# Interface: LayoutLoaderDefinition
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:21](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L21)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.co
|
|||||||
|
|
||||||
> `optional` **algorithm**: `string`
|
> `optional` **algorithm**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L27)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.co
|
|||||||
|
|
||||||
> **loader**: `LayoutLoader`
|
> **loader**: `LayoutLoader`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L26)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:23](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L23)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.co
|
|||||||
|
|
||||||
> **name**: `string`
|
> **name**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:25](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L25)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:22](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L22)
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
# Interface: RenderOptions
|
# Interface: RenderOptions
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L10)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L7)
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
@@ -18,4 +18,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.co
|
|||||||
|
|
||||||
> `optional` **algorithm**: `string`
|
> `optional` **algorithm**: `string`
|
||||||
|
|
||||||
Defined in: [packages/mermaid/src/rendering-util/render.ts:11](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L11)
|
Defined in: [packages/mermaid/src/rendering-util/render.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L8)
|
||||||
|
@@ -29,7 +29,6 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
|||||||
- **Plugins** - A plugin system for extending the functionality of Mermaid.
|
- **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,13 +35,11 @@ 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,7 +139,6 @@ 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)
|
||||||
@@ -153,7 +152,6 @@ 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
|
||||||
@@ -163,7 +161,6 @@ 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
|
||||||
@@ -172,18 +169,15 @@ 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
|
||||||
|
@@ -360,7 +360,8 @@ gantt
|
|||||||
weekday monday
|
weekday monday
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Warning** > `millisecond` and `second` support was added in v10.3.0
|
> **Warning**
|
||||||
|
> `millisecond` and `second` support was added in v10.3.0
|
||||||
|
|
||||||
## Output in compact mode
|
## Output in compact mode
|
||||||
|
|
||||||
|
@@ -329,11 +329,7 @@ Messages can be of two displayed either solid or with a dotted line.
|
|||||||
[Actor][Arrow][Actor]:Message text
|
[Actor][Arrow][Actor]:Message text
|
||||||
```
|
```
|
||||||
|
|
||||||
Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows.
|
There are ten types of arrows currently supported:
|
||||||
|
|
||||||
#### Supported Arrow Types
|
|
||||||
|
|
||||||
**Standard Arrow Types**
|
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| -------- | ---------------------------------------------------- |
|
| -------- | ---------------------------------------------------- |
|
||||||
@@ -348,58 +344,6 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros
|
|||||||
| `-)` | Solid line with an open arrow at the end (async) |
|
| `-)` | Solid line with an open arrow at the end (async) |
|
||||||
| `--)` | Dotted line with a open arrow at the end (async) |
|
| `--)` | Dotted line with a open arrow at the end (async) |
|
||||||
|
|
||||||
**Half-Arrows (v\<MERMAID_RELEASE_VERSION>+)**
|
|
||||||
|
|
||||||
The following half-arrow types are supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
| ------- | ---------------------------------------------------- |
|
|
||||||
| `-\|\` | Solid line with top half arrowhead |
|
|
||||||
| `--\|\` | Dotted line with top half arrowhead |
|
|
||||||
| `-\|/` | Solid line with bottom half arrowhead |
|
|
||||||
| `--\|/` | Dotted line with bottom half arrowhead |
|
|
||||||
| `/\|-` | Solid line with reverse top half arrowhead |
|
|
||||||
| `/\|--` | Dotted line with reverse top half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom half arrowhead |
|
|
||||||
| `-\\` | Solid line with top stick half arrowhead |
|
|
||||||
| `--\\` | Dotted line with top stick half arrowhead |
|
|
||||||
| `-//` | Solid line with bottom stick half arrowhead |
|
|
||||||
| `--//` | Dotted line with bottom stick half arrowhead |
|
|
||||||
| `//-` | Solid line with reverse top stick half arrowhead |
|
|
||||||
| `//--` | Dotted line with reverse top stick half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom stick half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom stick half arrowhead |
|
|
||||||
|
|
||||||
## Central Connections (v\<MERMAID_RELEASE_VERSION>+)
|
|
||||||
|
|
||||||
Mermaid sequence diagrams support **central lifeline connections** using a `()`.
|
|
||||||
This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another.
|
|
||||||
|
|
||||||
To indicate a central connection, append `()` to the arrow syntax.
|
|
||||||
|
|
||||||
#### Basic Syntax
|
|
||||||
|
|
||||||
```mermaid-example
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant John
|
|
||||||
Alice->>()John: Hello John
|
|
||||||
Alice()->>John: How are you?
|
|
||||||
John()->>()Alice: Great!
|
|
||||||
```
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant John
|
|
||||||
Alice->>()John: Hello John
|
|
||||||
Alice()->>John: How are you?
|
|
||||||
John()->>()Alice: Great!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Activations
|
## Activations
|
||||||
|
|
||||||
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
||||||
|
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.2",
|
"@argos-ci/cypress": "^5.0.7",
|
||||||
"@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.2",
|
"@rollup/plugin-typescript": "^12.1.4",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.19",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.3",
|
||||||
"@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.15",
|
"@types/lodash": "^4.17.20",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.17",
|
||||||
"@types/rollup-plugin-visualizer": "^5.0.3",
|
"@types/rollup-plugin-visualizer": "^5.0.3",
|
||||||
"@vitest/coverage-v8": "^3.0.6",
|
"@vitest/coverage-v8": "^3.0.9",
|
||||||
"@vitest/spy": "^3.0.6",
|
"@vitest/spy": "^3.0.9",
|
||||||
"@vitest/ui": "^3.0.6",
|
"@vitest/ui": "^3.0.9",
|
||||||
"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.3",
|
"cspell": "^9.1.5",
|
||||||
"cypress": "^14.5.1",
|
"cypress": "^14.5.4",
|
||||||
"cypress-image-snapshot": "^4.0.1",
|
"cypress-image-snapshot": "^4.0.1",
|
||||||
"cypress-split": "^1.24.14",
|
"cypress-split": "^1.24.21",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.9",
|
||||||
"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.0.2",
|
"globby": "^14.1.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"jest": "^30.0.4",
|
"jest": "^30.0.5",
|
||||||
"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.2",
|
"lint-staged": "^16.1.6",
|
||||||
"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.2",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-jsdoc": "^1.3.2",
|
"prettier-plugin-jsdoc": "^1.3.3",
|
||||||
"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.10",
|
"start-server-and-test": "^2.0.13",
|
||||||
"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.3",
|
"vite": "^7.0.6",
|
||||||
"vite-plugin-istanbul": "^7.0.0",
|
"vite-plugin-istanbul": "^7.0.0",
|
||||||
"vitest": "^3.0.6"
|
"vitest": "^3.0.9"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"report-dir": "coverage/cypress"
|
"report-dir": "coverage/cypress"
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^7.0.4",
|
"@braintree/sanitize-url": "^7.1.1",
|
||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
"khroma": "^2.1.0"
|
"khroma": "^2.1.0"
|
||||||
},
|
},
|
||||||
|
@@ -1,67 +0,0 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import {
|
|
||||||
intersection,
|
|
||||||
ensureTrulyOutside,
|
|
||||||
makeInsidePoint,
|
|
||||||
tryNodeIntersect,
|
|
||||||
replaceEndpoint,
|
|
||||||
type RectLike,
|
|
||||||
type P,
|
|
||||||
} from '../geometry.js';
|
|
||||||
|
|
||||||
const approx = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps;
|
|
||||||
|
|
||||||
describe('geometry helpers', () => {
|
|
||||||
it('intersection: vertical approach hits bottom border', () => {
|
|
||||||
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
|
|
||||||
const h = rect.height / 2; // 25
|
|
||||||
const outside: P = { x: 0, y: 100 };
|
|
||||||
const inside: P = { x: 0, y: 0 };
|
|
||||||
const res = intersection(rect, outside, inside);
|
|
||||||
expect(approx(res.x, 0)).toBe(true);
|
|
||||||
expect(approx(res.y, h)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ensureTrulyOutside nudges near-boundary point outward', () => {
|
|
||||||
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
|
|
||||||
// near bottom boundary (y ~ h)
|
|
||||||
const near: P = { x: 0, y: rect.height / 2 - 0.2 };
|
|
||||||
const out = ensureTrulyOutside(rect, near, 10);
|
|
||||||
expect(out.y).toBeGreaterThan(rect.height / 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('makeInsidePoint keeps x for vertical and y from center', () => {
|
|
||||||
const rect: RectLike = { x: 10, y: 5, width: 100, height: 50 };
|
|
||||||
const outside: P = { x: 10, y: 40 };
|
|
||||||
const center: P = { x: 99, y: -123 }; // center y should be used
|
|
||||||
const inside = makeInsidePoint(rect, outside, center);
|
|
||||||
expect(inside.x).toBe(outside.x);
|
|
||||||
expect(inside.y).toBe(center.y);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tryNodeIntersect returns null for wrong-side intersections', () => {
|
|
||||||
const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 };
|
|
||||||
const outside: P = { x: -50, y: 0 };
|
|
||||||
const node = { intersect: () => ({ x: 10, y: 0 }) } as any; // right side of center
|
|
||||||
const res = tryNodeIntersect(node, rect, outside);
|
|
||||||
expect(res).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('replaceEndpoint dedup removes end/start appropriately', () => {
|
|
||||||
const pts: P[] = [
|
|
||||||
{ x: 0, y: 0 },
|
|
||||||
{ x: 1, y: 1 },
|
|
||||||
];
|
|
||||||
// remove duplicate end
|
|
||||||
replaceEndpoint(pts, 'end', { x: 1, y: 1 });
|
|
||||||
expect(pts.length).toBe(1);
|
|
||||||
|
|
||||||
const pts2: P[] = [
|
|
||||||
{ x: 0, y: 0 },
|
|
||||||
{ x: 1, y: 1 },
|
|
||||||
];
|
|
||||||
// remove duplicate start
|
|
||||||
replaceEndpoint(pts2, 'start', { x: 0, y: 0 });
|
|
||||||
expect(pts2.length).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,209 +0,0 @@
|
|||||||
/* Geometry utilities extracted from render.ts for reuse and testing */
|
|
||||||
|
|
||||||
export interface P {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RectLike {
|
|
||||||
x: number; // center x
|
|
||||||
y: number; // center y
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
padding?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NodeLike {
|
|
||||||
intersect?: (p: P) => P | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EPS = 1;
|
|
||||||
export const PUSH_OUT = 10;
|
|
||||||
|
|
||||||
export const onBorder = (bounds: RectLike, p: P, tol = 0.5): boolean => {
|
|
||||||
const halfW = bounds.width / 2;
|
|
||||||
const halfH = bounds.height / 2;
|
|
||||||
const left = bounds.x - halfW;
|
|
||||||
const right = bounds.x + halfW;
|
|
||||||
const top = bounds.y - halfH;
|
|
||||||
const bottom = bounds.y + halfH;
|
|
||||||
|
|
||||||
const onLeft = Math.abs(p.x - left) <= tol && p.y >= top - tol && p.y <= bottom + tol;
|
|
||||||
const onRight = Math.abs(p.x - right) <= tol && p.y >= top - tol && p.y <= bottom + tol;
|
|
||||||
const onTop = Math.abs(p.y - top) <= tol && p.x >= left - tol && p.x <= right + tol;
|
|
||||||
const onBottom = Math.abs(p.y - bottom) <= tol && p.x >= left - tol && p.x <= right + tol;
|
|
||||||
return onLeft || onRight || onTop || onBottom;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute intersection between a rectangle (center x/y, width/height) and the line
|
|
||||||
* segment from insidePoint -\> outsidePoint. Returns the point on the rectangle border.
|
|
||||||
*
|
|
||||||
* This version avoids snapping to outsidePoint when certain variables evaluate to 0
|
|
||||||
* (previously caused vertical top/bottom cases to miss the border). It only enforces
|
|
||||||
* axis-constant behavior for purely vertical/horizontal approaches.
|
|
||||||
*/
|
|
||||||
export const intersection = (node: RectLike, outsidePoint: P, insidePoint: P): P => {
|
|
||||||
const x = node.x;
|
|
||||||
const y = node.y;
|
|
||||||
|
|
||||||
const dx = Math.abs(x - insidePoint.x);
|
|
||||||
const w = node.width / 2;
|
|
||||||
let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
|
|
||||||
const h = node.height / 2;
|
|
||||||
|
|
||||||
const Q = Math.abs(outsidePoint.y - insidePoint.y);
|
|
||||||
const R = Math.abs(outsidePoint.x - insidePoint.x);
|
|
||||||
|
|
||||||
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {
|
|
||||||
// Intersection is top or bottom of rect.
|
|
||||||
const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
|
|
||||||
r = (R * q) / Q;
|
|
||||||
const res = {
|
|
||||||
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,
|
|
||||||
y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Keep axis-constant special-cases only
|
|
||||||
if (R === 0) {
|
|
||||||
res.x = outsidePoint.x;
|
|
||||||
}
|
|
||||||
if (Q === 0) {
|
|
||||||
res.y = outsidePoint.y;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
// Intersection on sides of rect
|
|
||||||
if (insidePoint.x < outsidePoint.x) {
|
|
||||||
r = outsidePoint.x - w - x;
|
|
||||||
} else {
|
|
||||||
r = x - w - outsidePoint.x;
|
|
||||||
}
|
|
||||||
const q = (Q * r) / R;
|
|
||||||
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
|
||||||
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
|
||||||
|
|
||||||
// Only handle axis-constant cases
|
|
||||||
if (R === 0) {
|
|
||||||
_x = outsidePoint.x;
|
|
||||||
}
|
|
||||||
if (Q === 0) {
|
|
||||||
_y = outsidePoint.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { x: _x, y: _y };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const outsideNode = (node: RectLike, point: P): boolean => {
|
|
||||||
const x = node.x;
|
|
||||||
const y = node.y;
|
|
||||||
const dx = Math.abs(point.x - x);
|
|
||||||
const dy = Math.abs(point.y - y);
|
|
||||||
const w = node.width / 2;
|
|
||||||
const h = node.height / 2;
|
|
||||||
return dx >= w || dy >= h;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ensureTrulyOutside = (bounds: RectLike, p: P, push = PUSH_OUT): P => {
|
|
||||||
const dx = Math.abs(p.x - bounds.x);
|
|
||||||
const dy = Math.abs(p.y - bounds.y);
|
|
||||||
const w = bounds.width / 2;
|
|
||||||
const h = bounds.height / 2;
|
|
||||||
if (Math.abs(dx - w) < EPS || Math.abs(dy - h) < EPS) {
|
|
||||||
const dirX = p.x - bounds.x;
|
|
||||||
const dirY = p.y - bounds.y;
|
|
||||||
const len = Math.sqrt(dirX * dirX + dirY * dirY);
|
|
||||||
if (len > 0) {
|
|
||||||
return {
|
|
||||||
x: bounds.x + (dirX / len) * (len + push),
|
|
||||||
y: bounds.y + (dirY / len) * (len + push),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeInsidePoint = (bounds: RectLike, outside: P, center: P): P => {
|
|
||||||
const isVertical = Math.abs(outside.x - bounds.x) < EPS;
|
|
||||||
const isHorizontal = Math.abs(outside.y - bounds.y) < EPS;
|
|
||||||
return {
|
|
||||||
x: isVertical
|
|
||||||
? outside.x
|
|
||||||
: outside.x < bounds.x
|
|
||||||
? bounds.x - bounds.width / 4
|
|
||||||
: bounds.x + bounds.width / 4,
|
|
||||||
y: isHorizontal ? outside.y : center.y,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tryNodeIntersect = (node: NodeLike, bounds: RectLike, outside: P): P | null => {
|
|
||||||
if (!node?.intersect) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const res = node.intersect(outside);
|
|
||||||
if (!res) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const wrongSide =
|
|
||||||
(outside.x < bounds.x && res.x > bounds.x) || (outside.x > bounds.x && res.x < bounds.x);
|
|
||||||
if (wrongSide) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const dist = Math.hypot(outside.x - res.x, outside.y - res.y);
|
|
||||||
if (dist <= EPS) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fallbackIntersection = (bounds: RectLike, outside: P, center: P): P => {
|
|
||||||
const inside = makeInsidePoint(bounds, outside, center);
|
|
||||||
return intersection(bounds, outside, inside);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const computeNodeIntersection = (
|
|
||||||
node: NodeLike,
|
|
||||||
bounds: RectLike,
|
|
||||||
outside: P,
|
|
||||||
center: P
|
|
||||||
): P => {
|
|
||||||
const outside2 = ensureTrulyOutside(bounds, outside);
|
|
||||||
return tryNodeIntersect(node, bounds, outside2) ?? fallbackIntersection(bounds, outside2, center);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const replaceEndpoint = (
|
|
||||||
points: P[],
|
|
||||||
which: 'start' | 'end',
|
|
||||||
value: P | null | undefined,
|
|
||||||
tol = 0.1
|
|
||||||
) => {
|
|
||||||
if (!value || points.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (which === 'start') {
|
|
||||||
if (
|
|
||||||
points.length > 0 &&
|
|
||||||
Math.abs(points[0].x - value.x) < tol &&
|
|
||||||
Math.abs(points[0].y - value.y) < tol
|
|
||||||
) {
|
|
||||||
// duplicate start remove it
|
|
||||||
points.shift();
|
|
||||||
} else {
|
|
||||||
points[0] = value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const last = points.length - 1;
|
|
||||||
if (
|
|
||||||
points.length > 0 &&
|
|
||||||
Math.abs(points[last].x - value.x) < tol &&
|
|
||||||
Math.abs(points[last].y - value.y) < tol
|
|
||||||
) {
|
|
||||||
// duplicate end remove it
|
|
||||||
points.pop();
|
|
||||||
} else {
|
|
||||||
points[last] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,26 +1,11 @@
|
|||||||
import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid';
|
|
||||||
// @ts-ignore TODO: Investigate D3 issue
|
|
||||||
import { curveLinear } from 'd3';
|
import { curveLinear } from 'd3';
|
||||||
import ELK from 'elkjs/lib/elk.bundled.js';
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
||||||
|
import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid';
|
||||||
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
||||||
|
|
||||||
import {
|
|
||||||
type P,
|
|
||||||
type RectLike,
|
|
||||||
outsideNode,
|
|
||||||
computeNodeIntersection,
|
|
||||||
replaceEndpoint,
|
|
||||||
onBorder,
|
|
||||||
} from './geometry.js';
|
|
||||||
|
|
||||||
type Node = LayoutData['nodes'][number];
|
type Node = LayoutData['nodes'][number];
|
||||||
|
// Used to calculate distances in order to avoid floating number rounding issues when comparing floating numbers
|
||||||
// Minimal structural type to avoid depending on d3 Selection typings
|
const epsilon = 0.0001;
|
||||||
interface D3Selection<T extends Element> {
|
|
||||||
node(): T | null;
|
|
||||||
attr(name: string, value: string): D3Selection<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LabelData {
|
interface LabelData {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -31,9 +16,18 @@ interface LabelData {
|
|||||||
interface NodeWithVertex extends Omit<Node, 'domId'> {
|
interface NodeWithVertex extends Omit<Node, 'domId'> {
|
||||||
children?: LayoutData['nodes'];
|
children?: LayoutData['nodes'];
|
||||||
labelData?: LabelData;
|
labelData?: LabelData;
|
||||||
domId?: D3Selection<SVGAElement | SVGGElement>;
|
domId?: Node['domId'] | SVGGroup | d3.Selection<SVGAElement, unknown, Element | null, unknown>;
|
||||||
|
}
|
||||||
|
interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
function distance(p1?: Point, p2?: Point): number {
|
||||||
|
if (!p1 || !p2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const render = async (
|
export const render = async (
|
||||||
data4Layout: LayoutData,
|
data4Layout: LayoutData,
|
||||||
svg: SVG,
|
svg: SVG,
|
||||||
@@ -67,26 +61,39 @@ export const render = async (
|
|||||||
|
|
||||||
// Add the element to the DOM
|
// Add the element to the DOM
|
||||||
if (!node.isGroup) {
|
if (!node.isGroup) {
|
||||||
const child = node as NodeWithVertex;
|
// Create a clean node object for ELK with only the properties it expects
|
||||||
|
const child: NodeWithVertex = {
|
||||||
|
id: node.id,
|
||||||
|
width: node.width,
|
||||||
|
height: node.height,
|
||||||
|
// Store the original node data for later use
|
||||||
|
label: node.label,
|
||||||
|
isGroup: node.isGroup,
|
||||||
|
shape: node.shape,
|
||||||
|
padding: node.padding,
|
||||||
|
cssClasses: node.cssClasses,
|
||||||
|
cssStyles: node.cssStyles,
|
||||||
|
look: node.look,
|
||||||
|
// Include parentId for subgraph processing
|
||||||
|
parentId: node.parentId,
|
||||||
|
};
|
||||||
graph.children.push(child);
|
graph.children.push(child);
|
||||||
nodeDb[node.id] = node;
|
nodeDb[node.id] = child;
|
||||||
|
|
||||||
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
||||||
const boundingBox = childNodeEl.node()!.getBBox();
|
const boundingBox = childNodeEl.node()!.getBBox();
|
||||||
// Store the domId separately for rendering, not in the ELK graph
|
// Store the domId separately for rendering, not in the ELK graph
|
||||||
child.domId = childNodeEl;
|
child.domId = childNodeEl;
|
||||||
|
child.calcIntersect = node.calcIntersect;
|
||||||
child.width = boundingBox.width;
|
child.width = boundingBox.width;
|
||||||
child.height = boundingBox.height;
|
child.height = boundingBox.height;
|
||||||
} else {
|
} else {
|
||||||
// A subgraph
|
// A subgraph
|
||||||
const child: NodeWithVertex & { children: NodeWithVertex[] } = {
|
const child: NodeWithVertex & { children: NodeWithVertex[] } = {
|
||||||
...node,
|
...node,
|
||||||
domId: undefined,
|
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
// Let elk render with the copy
|
|
||||||
graph.children.push(child);
|
graph.children.push(child);
|
||||||
// Save the original containing the intersection function
|
|
||||||
nodeDb[node.id] = child;
|
nodeDb[node.id] = child;
|
||||||
await addVertices(nodeEl, nodeArr, child, node.id);
|
await addVertices(nodeEl, nodeArr, child, node.id);
|
||||||
|
|
||||||
@@ -161,7 +168,7 @@ export const render = async (
|
|||||||
height: node.height,
|
height: node.height,
|
||||||
};
|
};
|
||||||
if (node.isGroup) {
|
if (node.isGroup) {
|
||||||
log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData);
|
log.debug('id abc88 subgraph = ', node.id, node.x, node.y, node.labelData);
|
||||||
const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph');
|
const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph');
|
||||||
// TODO use faster way of cloning
|
// TODO use faster way of cloning
|
||||||
const clusterNode = JSON.parse(JSON.stringify(node));
|
const clusterNode = JSON.parse(JSON.stringify(node));
|
||||||
@@ -170,10 +177,10 @@ export const render = async (
|
|||||||
clusterNode.width = Math.max(clusterNode.width, node.labelData.width);
|
clusterNode.width = Math.max(clusterNode.width, node.labelData.width);
|
||||||
await insertCluster(subgraphEl, clusterNode);
|
await insertCluster(subgraphEl, clusterNode);
|
||||||
|
|
||||||
log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels);
|
log.debug('id (UIO)= ', node.id, node.width, node.shape, node.labels);
|
||||||
} else {
|
} else {
|
||||||
log.info(
|
log.info(
|
||||||
'Id NODE = ',
|
'id NODE = ',
|
||||||
node.id,
|
node.id,
|
||||||
node.x,
|
node.x,
|
||||||
node.y,
|
node.y,
|
||||||
@@ -215,19 +222,25 @@ export const render = async (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
subgraphs.forEach(function (subgraph: { id: string | number }) {
|
||||||
|
const data: any = { id: subgraph.id };
|
||||||
|
if (parentLookupDb.parentById[subgraph.id] !== undefined) {
|
||||||
|
data.parent = parentLookupDb.parentById[subgraph.id];
|
||||||
|
}
|
||||||
|
});
|
||||||
return parentLookupDb;
|
return parentLookupDb;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEdgeStartEndPoint = (edge: any) => {
|
const getEdgeStartEndPoint = (edge: any) => {
|
||||||
// edge.start and edge.end are IDs (string/number) in our layout data
|
const source: any = edge.start;
|
||||||
const sourceId: string | number = edge.start;
|
const target: any = edge.end;
|
||||||
const targetId: string | number = edge.end;
|
|
||||||
|
|
||||||
const source = sourceId;
|
// Save the original source and target
|
||||||
const target = targetId;
|
const sourceId = source;
|
||||||
|
const targetId = target;
|
||||||
|
|
||||||
const startNode = nodeDb[sourceId];
|
const startNode = nodeDb[edge.start.id];
|
||||||
const endNode = nodeDb[targetId];
|
const endNode = nodeDb[edge.end.id];
|
||||||
|
|
||||||
if (!startNode || !endNode) {
|
if (!startNode || !endNode) {
|
||||||
return { source, target };
|
return { source, target };
|
||||||
@@ -250,112 +263,6 @@ export const render = async (
|
|||||||
/**
|
/**
|
||||||
* Add edges to graph based on parsed graph definition
|
* Add edges to graph based on parsed graph definition
|
||||||
*/
|
*/
|
||||||
// Edge helper maps and utilities (de-duplicated)
|
|
||||||
const ARROW_MAP: Record<string, [string, string]> = {
|
|
||||||
arrow_open: ['arrow_open', 'arrow_open'],
|
|
||||||
arrow_cross: ['arrow_open', 'arrow_cross'],
|
|
||||||
double_arrow_cross: ['arrow_cross', 'arrow_cross'],
|
|
||||||
arrow_point: ['arrow_open', 'arrow_point'],
|
|
||||||
double_arrow_point: ['arrow_point', 'arrow_point'],
|
|
||||||
arrow_circle: ['arrow_open', 'arrow_circle'],
|
|
||||||
double_arrow_circle: ['arrow_circle', 'arrow_circle'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const computeStroke = (
|
|
||||||
stroke: string | undefined,
|
|
||||||
defaultStyle?: string,
|
|
||||||
defaultLabelStyle?: string
|
|
||||||
) => {
|
|
||||||
// Defaults correspond to 'normal'
|
|
||||||
let thickness = 'normal';
|
|
||||||
let pattern = 'solid';
|
|
||||||
let style = '';
|
|
||||||
let labelStyle = '';
|
|
||||||
|
|
||||||
if (stroke === 'dotted') {
|
|
||||||
pattern = 'dotted';
|
|
||||||
style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
|
|
||||||
} else if (stroke === 'thick') {
|
|
||||||
thickness = 'thick';
|
|
||||||
style = 'stroke-width: 3.5px;fill:none;';
|
|
||||||
} else {
|
|
||||||
// normal
|
|
||||||
style = defaultStyle ?? 'fill:none;';
|
|
||||||
if (defaultLabelStyle !== undefined) {
|
|
||||||
labelStyle = defaultLabelStyle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { thickness, pattern, style, labelStyle };
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurve = (edgeInterpolate: any, edgesDefaultInterpolate: any, confCurve: any) => {
|
|
||||||
if (edgeInterpolate !== undefined) {
|
|
||||||
return interpolateToCurve(edgeInterpolate, curveLinear);
|
|
||||||
}
|
|
||||||
if (edgesDefaultInterpolate !== undefined) {
|
|
||||||
return interpolateToCurve(edgesDefaultInterpolate, curveLinear);
|
|
||||||
}
|
|
||||||
// @ts-ignore TODO: fix this
|
|
||||||
return interpolateToCurve(confCurve, curveLinear);
|
|
||||||
};
|
|
||||||
const buildEdgeData = (
|
|
||||||
edge: any,
|
|
||||||
defaults: {
|
|
||||||
defaultStyle?: string;
|
|
||||||
defaultLabelStyle?: string;
|
|
||||||
defaultInterpolate?: any;
|
|
||||||
confCurve: any;
|
|
||||||
},
|
|
||||||
common: any
|
|
||||||
) => {
|
|
||||||
const edgeData: any = { style: '', labelStyle: '' };
|
|
||||||
edgeData.minlen = edge.length || 1;
|
|
||||||
// maintain legacy behavior
|
|
||||||
edge.text = edge.label;
|
|
||||||
|
|
||||||
// Arrowhead fill vs none
|
|
||||||
edgeData.arrowhead = edge.type === 'arrow_open' ? 'none' : 'normal';
|
|
||||||
|
|
||||||
// Arrow types
|
|
||||||
const arrowMap = ARROW_MAP[edge.type] ?? ARROW_MAP.arrow_open;
|
|
||||||
edgeData.arrowTypeStart = arrowMap[0];
|
|
||||||
edgeData.arrowTypeEnd = arrowMap[1];
|
|
||||||
|
|
||||||
// Optional edge label positioning flags
|
|
||||||
edgeData.startLabelRight = edge.startLabelRight;
|
|
||||||
edgeData.endLabelLeft = edge.endLabelLeft;
|
|
||||||
|
|
||||||
// Stroke
|
|
||||||
const strokeRes = computeStroke(edge.stroke, defaults.defaultStyle, defaults.defaultLabelStyle);
|
|
||||||
edgeData.thickness = strokeRes.thickness;
|
|
||||||
edgeData.pattern = strokeRes.pattern;
|
|
||||||
edgeData.style = (edgeData.style || '') + (strokeRes.style || '');
|
|
||||||
edgeData.labelStyle = (edgeData.labelStyle || '') + (strokeRes.labelStyle || '');
|
|
||||||
|
|
||||||
// Curve
|
|
||||||
// @ts-ignore - defaults.confCurve is present at runtime but missing in type
|
|
||||||
edgeData.curve = getCurve(edge.interpolate, defaults.defaultInterpolate, defaults.confCurve);
|
|
||||||
|
|
||||||
// Arrowhead style + labelpos when we have label text
|
|
||||||
const hasText = (edge?.text ?? '') !== '';
|
|
||||||
if (hasText) {
|
|
||||||
edgeData.arrowheadStyle = 'fill: #333';
|
|
||||||
edgeData.labelpos = 'c';
|
|
||||||
} else if (edge.style !== undefined) {
|
|
||||||
edgeData.arrowheadStyle = 'fill: #333';
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeData.labelType = edge.labelType;
|
|
||||||
edgeData.label = (edge?.text ?? '').replace(common.lineBreakRegex, '\n');
|
|
||||||
|
|
||||||
if (edge.style === undefined) {
|
|
||||||
edgeData.style = edgeData.style ?? 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
|
|
||||||
return edgeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addEdges = async function (
|
const addEdges = async function (
|
||||||
dataForLayout: { edges: any; direction?: string },
|
dataForLayout: { edges: any; direction?: string },
|
||||||
graph: {
|
graph: {
|
||||||
@@ -377,6 +284,7 @@ export const render = async (
|
|||||||
const edges = dataForLayout.edges;
|
const edges = dataForLayout.edges;
|
||||||
const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
|
const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
|
||||||
const linkIdCnt: any = {};
|
const linkIdCnt: any = {};
|
||||||
|
const dir = dataForLayout.direction || 'DOWN';
|
||||||
let defaultStyle: string | undefined;
|
let defaultStyle: string | undefined;
|
||||||
let defaultLabelStyle: string | undefined;
|
let defaultLabelStyle: string | undefined;
|
||||||
|
|
||||||
@@ -406,24 +314,105 @@ export const render = async (
|
|||||||
linkIdCnt[linkIdBase]++;
|
linkIdCnt[linkIdBase]++;
|
||||||
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
|
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
|
||||||
}
|
}
|
||||||
const linkId = linkIdBase; // + '_' + linkIdCnt[linkIdBase];
|
const linkId = linkIdBase + '_' + linkIdCnt[linkIdBase];
|
||||||
edge.id = linkId;
|
edge.id = linkId;
|
||||||
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
|
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
|
||||||
const linkNameStart = 'LS_' + edge.start;
|
const linkNameStart = 'LS_' + edge.start;
|
||||||
const linkNameEnd = 'LE_' + edge.end;
|
const linkNameEnd = 'LE_' + edge.end;
|
||||||
|
|
||||||
|
const edgeData: any = { style: '', labelStyle: '' };
|
||||||
|
edgeData.minlen = edge.length || 1;
|
||||||
|
edge.text = edge.label;
|
||||||
|
// Set link type for rendering
|
||||||
|
if (edge.type === 'arrow_open') {
|
||||||
|
edgeData.arrowhead = 'none';
|
||||||
|
} else {
|
||||||
|
edgeData.arrowhead = 'normal';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check of arrow types, placed here in order not to break old rendering
|
||||||
|
edgeData.arrowTypeStart = 'arrow_open';
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_open';
|
||||||
|
|
||||||
|
/* eslint-disable no-fallthrough */
|
||||||
|
switch (edge.type) {
|
||||||
|
case 'double_arrow_cross':
|
||||||
|
edgeData.arrowTypeStart = 'arrow_cross';
|
||||||
|
case 'arrow_cross':
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_cross';
|
||||||
|
break;
|
||||||
|
case 'double_arrow_point':
|
||||||
|
edgeData.arrowTypeStart = 'arrow_point';
|
||||||
|
case 'arrow_point':
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_point';
|
||||||
|
break;
|
||||||
|
case 'double_arrow_circle':
|
||||||
|
edgeData.arrowTypeStart = 'arrow_circle';
|
||||||
|
case 'arrow_circle':
|
||||||
|
edgeData.arrowTypeEnd = 'arrow_circle';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = '';
|
||||||
|
let labelStyle = '';
|
||||||
|
|
||||||
|
edgeData.startLabelRight = edge.startLabelRight;
|
||||||
|
edgeData.endLabelLeft = edge.endLabelLeft;
|
||||||
|
|
||||||
|
switch (edge.stroke) {
|
||||||
|
case 'normal':
|
||||||
|
style = 'fill:none;';
|
||||||
|
if (defaultStyle !== undefined) {
|
||||||
|
style = defaultStyle;
|
||||||
|
}
|
||||||
|
if (defaultLabelStyle !== undefined) {
|
||||||
|
labelStyle = defaultLabelStyle;
|
||||||
|
}
|
||||||
|
edgeData.thickness = 'normal';
|
||||||
|
edgeData.pattern = 'solid';
|
||||||
|
break;
|
||||||
|
case 'dotted':
|
||||||
|
edgeData.thickness = 'normal';
|
||||||
|
edgeData.pattern = 'dotted';
|
||||||
|
edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
|
||||||
|
break;
|
||||||
|
case 'thick':
|
||||||
|
edgeData.thickness = 'thick';
|
||||||
|
edgeData.pattern = 'solid';
|
||||||
|
edgeData.style = 'stroke-width: 3.5px;fill:none;';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeData.style = edgeData.style += style;
|
||||||
|
edgeData.labelStyle = edgeData.labelStyle += labelStyle;
|
||||||
|
|
||||||
const conf = getConfig();
|
const conf = getConfig();
|
||||||
const edgeData = buildEdgeData(
|
if (edge.interpolate !== undefined) {
|
||||||
edge,
|
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
|
||||||
{
|
} else if (edges.defaultInterpolate !== undefined) {
|
||||||
defaultStyle,
|
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
|
||||||
defaultLabelStyle,
|
} else {
|
||||||
defaultInterpolate: edges.defaultInterpolate,
|
// @ts-ignore TODO: fix this
|
||||||
// @ts-ignore - conf.curve exists at runtime but is missing from typing
|
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
|
||||||
confCurve: conf.curve,
|
}
|
||||||
},
|
|
||||||
common
|
if (edge.text === undefined) {
|
||||||
);
|
if (edge.style !== undefined) {
|
||||||
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
|
edgeData.labelpos = 'c';
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeData.labelType = edge.labelType;
|
||||||
|
edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n');
|
||||||
|
|
||||||
|
if (edge.style === undefined) {
|
||||||
|
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
|
||||||
|
|
||||||
edgeData.id = linkId;
|
edgeData.id = linkId;
|
||||||
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
|
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
|
||||||
@@ -432,11 +421,13 @@ export const render = async (
|
|||||||
|
|
||||||
// calculate start and end points of the edge, note that the source and target
|
// calculate start and end points of the edge, note that the source and target
|
||||||
// can be modified for shapes that have ports
|
// can be modified for shapes that have ports
|
||||||
|
// @ts-ignore TODO: fix this
|
||||||
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge);
|
const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir);
|
||||||
log.debug('abc78 source and target', source, target);
|
log.debug('abc78 source and target', source, target);
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
|
// @ts-ignore TODO: fix this
|
||||||
|
id: 'e' + edge.start + edge.end,
|
||||||
...edge,
|
...edge,
|
||||||
sources: [source],
|
sources: [source],
|
||||||
targets: [target],
|
targets: [target],
|
||||||
@@ -470,7 +461,6 @@ export const render = async (
|
|||||||
case 'RL':
|
case 'RL':
|
||||||
return 'LEFT';
|
return 'LEFT';
|
||||||
case 'TB':
|
case 'TB':
|
||||||
case 'TD': // TD is an alias for TB in Mermaid
|
|
||||||
return 'DOWN';
|
return 'DOWN';
|
||||||
case 'BT':
|
case 'BT':
|
||||||
return 'UP';
|
return 'UP';
|
||||||
@@ -494,203 +484,6 @@ export const render = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node bounds helpers (global)
|
|
||||||
const getEffectiveGroupWidth = (node: any): number => {
|
|
||||||
const labelW = node?.labels?.[0]?.width ?? 0;
|
|
||||||
const padding = node?.padding ?? 0;
|
|
||||||
return Math.max(node.width ?? 0, labelW + padding);
|
|
||||||
};
|
|
||||||
|
|
||||||
const boundsFor = (node: any): RectLike => {
|
|
||||||
const width = node?.isGroup ? getEffectiveGroupWidth(node) : node.width;
|
|
||||||
return {
|
|
||||||
x: node.offset.posX + node.width / 2,
|
|
||||||
y: node.offset.posY + node.height / 2,
|
|
||||||
width,
|
|
||||||
height: node.height,
|
|
||||||
padding: node.padding,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
// Helper utilities for endpoint handling around cutter2
|
|
||||||
type Side = 'start' | 'end';
|
|
||||||
const approxEq = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps;
|
|
||||||
const isCenterApprox = (pt: P, node: { x: number; y: number }) =>
|
|
||||||
approxEq(pt.x, node.x) && approxEq(pt.y, node.y);
|
|
||||||
|
|
||||||
const getCandidateBorderPoint = (
|
|
||||||
points: P[],
|
|
||||||
node: any,
|
|
||||||
side: Side
|
|
||||||
): { candidate: P; centerApprox: boolean } => {
|
|
||||||
if (!points?.length) {
|
|
||||||
return { candidate: { x: node.x, y: node.y } as P, centerApprox: true };
|
|
||||||
}
|
|
||||||
if (side === 'start') {
|
|
||||||
const first = points[0];
|
|
||||||
const centerApprox = isCenterApprox(first, node);
|
|
||||||
const candidate = centerApprox && points.length > 1 ? points[1] : first;
|
|
||||||
return { candidate, centerApprox };
|
|
||||||
} else {
|
|
||||||
const last = points[points.length - 1];
|
|
||||||
const centerApprox = isCenterApprox(last, node);
|
|
||||||
const candidate = centerApprox && points.length > 1 ? points[points.length - 2] : last;
|
|
||||||
return { candidate, centerApprox };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropAutoCenterPoint = (points: P[], side: Side, doDrop: boolean) => {
|
|
||||||
if (!doDrop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (side === 'start') {
|
|
||||||
if (points.length > 0) {
|
|
||||||
points.shift();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (points.length > 0) {
|
|
||||||
points.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyStartIntersectionIfNeeded = (points: P[], startNode: any, startBounds: RectLike) => {
|
|
||||||
let firstOutsideStartIndex = -1;
|
|
||||||
for (const [i, p] of points.entries()) {
|
|
||||||
if (outsideNode(startBounds, p)) {
|
|
||||||
firstOutsideStartIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (firstOutsideStartIndex !== -1) {
|
|
||||||
const outsidePointForStart = points[firstOutsideStartIndex];
|
|
||||||
const startCenter = points[0];
|
|
||||||
const startIntersection = computeNodeIntersection(
|
|
||||||
startNode,
|
|
||||||
startBounds,
|
|
||||||
outsidePointForStart,
|
|
||||||
startCenter
|
|
||||||
);
|
|
||||||
replaceEndpoint(points, 'start', startIntersection);
|
|
||||||
log.debug('UIO cutter2: start-only intersection applied', { startIntersection });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyEndIntersectionIfNeeded = (points: P[], endNode: any, endBounds: RectLike) => {
|
|
||||||
let outsideIndexForEnd = -1;
|
|
||||||
for (let i = points.length - 1; i >= 0; i--) {
|
|
||||||
if (outsideNode(endBounds, points[i])) {
|
|
||||||
outsideIndexForEnd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outsideIndexForEnd !== -1) {
|
|
||||||
const outsidePointForEnd = points[outsideIndexForEnd];
|
|
||||||
const endCenter = points[points.length - 1];
|
|
||||||
const endIntersection = computeNodeIntersection(
|
|
||||||
endNode,
|
|
||||||
endBounds,
|
|
||||||
outsidePointForEnd,
|
|
||||||
endCenter
|
|
||||||
);
|
|
||||||
replaceEndpoint(points, 'end', endIntersection);
|
|
||||||
log.debug('UIO cutter2: end-only intersection applied', { endIntersection });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const cutter2 = (startNode: any, endNode: any, _points: any[]) => {
|
|
||||||
const startBounds = boundsFor(startNode);
|
|
||||||
const endBounds = boundsFor(endNode);
|
|
||||||
|
|
||||||
if (_points.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the original points array
|
|
||||||
const points: P[] = [..._points] as P[];
|
|
||||||
|
|
||||||
// The first point is the center of sNode, the last point is the center of eNode
|
|
||||||
const startCenter = points[0];
|
|
||||||
const endCenter = points[points.length - 1];
|
|
||||||
|
|
||||||
// Minimal, structured logging for diagnostics
|
|
||||||
log.debug('PPP cutter2: bounds', { startBounds, endBounds });
|
|
||||||
log.debug('PPP cutter2: original points', _points);
|
|
||||||
|
|
||||||
let firstOutsideStartIndex = -1;
|
|
||||||
|
|
||||||
// Single iteration through the array
|
|
||||||
for (const [i, point] of points.entries()) {
|
|
||||||
if (firstOutsideStartIndex === -1 && outsideNode(startBounds, point)) {
|
|
||||||
firstOutsideStartIndex = i;
|
|
||||||
}
|
|
||||||
if (outsideNode(endBounds, point)) {
|
|
||||||
// keep scanning; we'll also scan from the end for the last outside point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate intersection with start node if we found a point outside it
|
|
||||||
if (firstOutsideStartIndex !== -1) {
|
|
||||||
const outsidePointForStart = points[firstOutsideStartIndex];
|
|
||||||
const startIntersection = computeNodeIntersection(
|
|
||||||
startNode,
|
|
||||||
startBounds,
|
|
||||||
outsidePointForStart,
|
|
||||||
startCenter
|
|
||||||
);
|
|
||||||
log.debug('UIO cutter2: start intersection', startIntersection);
|
|
||||||
replaceEndpoint(points, 'start', startIntersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate intersection with end node
|
|
||||||
let outsidePointForEnd = null;
|
|
||||||
let outsideIndexForEnd = -1;
|
|
||||||
|
|
||||||
for (let i = points.length - 1; i >= 0; i--) {
|
|
||||||
if (outsideNode(endBounds, points[i])) {
|
|
||||||
outsidePointForEnd = points[i];
|
|
||||||
outsideIndexForEnd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!outsidePointForEnd && points.length > 1) {
|
|
||||||
outsidePointForEnd = points[points.length - 2];
|
|
||||||
outsideIndexForEnd = points.length - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outsidePointForEnd) {
|
|
||||||
const endIntersection = computeNodeIntersection(
|
|
||||||
endNode,
|
|
||||||
endBounds,
|
|
||||||
outsidePointForEnd,
|
|
||||||
endCenter
|
|
||||||
);
|
|
||||||
log.debug('UIO cutter2: end intersection', { endIntersection, outsideIndexForEnd });
|
|
||||||
replaceEndpoint(points, 'end', endIntersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final cleanup: Check if the last point is too close to the previous point
|
|
||||||
if (points.length > 1) {
|
|
||||||
const lastPoint = points[points.length - 1];
|
|
||||||
const secondLastPoint = points[points.length - 2];
|
|
||||||
const distance = Math.sqrt(
|
|
||||||
(lastPoint.x - secondLastPoint.x) ** 2 + (lastPoint.y - secondLastPoint.y) ** 2
|
|
||||||
);
|
|
||||||
if (distance < 2) {
|
|
||||||
log.debug('UIO cutter2: trimming tail point (too close)', {
|
|
||||||
distance,
|
|
||||||
lastPoint,
|
|
||||||
secondLastPoint,
|
|
||||||
});
|
|
||||||
points.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug('UIO cutter2: final points', points);
|
|
||||||
|
|
||||||
return points;
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-ignore - ELK is not typed
|
// @ts-ignore - ELK is not typed
|
||||||
const elk = new ELK();
|
const elk = new ELK();
|
||||||
const element = svg.select('g');
|
const element = svg.select('g');
|
||||||
@@ -702,19 +495,17 @@ export const render = async (
|
|||||||
id: 'root',
|
id: 'root',
|
||||||
layoutOptions: {
|
layoutOptions: {
|
||||||
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
||||||
|
'elk.layered.crossingMinimization.forceNodeModelOrder':
|
||||||
|
data4Layout.config.elk?.forceNodeModelOrder,
|
||||||
|
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
|
||||||
|
|
||||||
'elk.algorithm': algorithm,
|
'elk.algorithm': algorithm,
|
||||||
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
|
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
|
||||||
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
|
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
|
||||||
'elk.direction': 'DOWN',
|
'elk.direction': 'DOWN',
|
||||||
'spacing.baseValue': 40,
|
'spacing.baseValue': 35,
|
||||||
'elk.layered.crossingMinimization.forceNodeModelOrder':
|
|
||||||
data4Layout.config.elk?.forceNodeModelOrder,
|
|
||||||
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
|
|
||||||
'elk.layered.unnecessaryBendpoints': true,
|
'elk.layered.unnecessaryBendpoints': true,
|
||||||
'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy,
|
'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy,
|
||||||
|
|
||||||
// 'elk.layered.cycleBreaking.strategy': 'GREEDY_MODEL_ORDER',
|
|
||||||
// 'elk.layered.cycleBreaking.strategy': 'MODEL_ORDER',
|
|
||||||
// 'spacing.nodeNode': 20,
|
// 'spacing.nodeNode': 20,
|
||||||
// 'spacing.nodeNodeBetweenLayers': 25,
|
// 'spacing.nodeNodeBetweenLayers': 25,
|
||||||
// 'spacing.edgeNode': 20,
|
// 'spacing.edgeNode': 20,
|
||||||
@@ -722,28 +513,22 @@ export const render = async (
|
|||||||
// 'spacing.edgeEdge': 10,
|
// 'spacing.edgeEdge': 10,
|
||||||
// 'spacing.edgeEdgeBetweenLayers': 20,
|
// 'spacing.edgeEdgeBetweenLayers': 20,
|
||||||
// 'spacing.nodeSelfLoop': 20,
|
// 'spacing.nodeSelfLoop': 20,
|
||||||
|
|
||||||
// Tweaking options
|
// Tweaking options
|
||||||
// 'nodePlacement.favorStraightEdges': true,
|
|
||||||
// 'elk.layered.nodePlacement.favorStraightEdges': true,
|
// 'elk.layered.nodePlacement.favorStraightEdges': true,
|
||||||
// 'nodePlacement.feedbackEdges': true,
|
// 'nodePlacement.feedbackEdges': true,
|
||||||
'elk.layered.wrapping.multiEdge.improveCuts': true,
|
// 'elk.layered.wrapping.multiEdge.improveCuts': true,
|
||||||
'elk.layered.wrapping.multiEdge.improveWrappedEdges': true,
|
// 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true,
|
||||||
// 'elk.layered.wrapping.strategy': 'MULTI_EDGE',
|
// 'elk.layered.wrapping.strategy': 'MULTI_EDGE',
|
||||||
// 'elk.layered.wrapping.strategy': 'SINGLE_EDGE',
|
// 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',
|
||||||
'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',
|
// 'elk.layered.mergeHierarchyEdges': true,
|
||||||
'elk.layered.mergeHierarchyEdges': true,
|
|
||||||
|
|
||||||
// 'elk.layered.feedbackEdges': true,
|
// 'elk.layered.feedbackEdges': true,
|
||||||
// 'elk.layered.crossingMinimization.semiInteractive': true,
|
// 'elk.layered.crossingMinimization.semiInteractive': true,
|
||||||
// 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,
|
// 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,
|
||||||
// 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,
|
// 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,
|
||||||
// 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK',
|
// 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK',
|
||||||
// 'elk.insideSelfLoops.activate': true,
|
// 'elk.insideSelfLoops.activate': true,
|
||||||
// 'elk.separateConnectedComponents': true,
|
|
||||||
// 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE',
|
// 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE',
|
||||||
// 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES
|
// 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES
|
||||||
// 'elk.layered.considerModelOrder.strategy': 'EDGES', // NODES_AND_EDGES
|
|
||||||
// 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES
|
// 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES
|
||||||
},
|
},
|
||||||
children: [],
|
children: [],
|
||||||
@@ -753,7 +538,7 @@ export const render = async (
|
|||||||
log.info('Drawing flowchart using v4 renderer', elk);
|
log.info('Drawing flowchart using v4 renderer', elk);
|
||||||
|
|
||||||
// Set the direction of the graph based on the parsed information
|
// Set the direction of the graph based on the parsed information
|
||||||
const dir = data4Layout.direction ?? 'DOWN';
|
const dir = data4Layout.direction || 'DOWN';
|
||||||
elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir);
|
elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir);
|
||||||
|
|
||||||
// Create the lookup db for the subgraphs and their children to used when creating
|
// Create the lookup db for the subgraphs and their children to used when creating
|
||||||
@@ -784,16 +569,15 @@ export const render = async (
|
|||||||
|
|
||||||
// Subgraph
|
// Subgraph
|
||||||
if (parentLookupDb.childrenById[node.id] !== undefined) {
|
if (parentLookupDb.childrenById[node.id] !== undefined) {
|
||||||
// Set label and adjust node width separately (avoid side effects in labels array)
|
|
||||||
node.labels = [
|
node.labels = [
|
||||||
{
|
{
|
||||||
text: node.label,
|
text: node.label,
|
||||||
width: node?.labelData?.width ?? 50,
|
width: node?.labelData?.width || 50,
|
||||||
height: node?.labelData?.height ?? 50,
|
height: node?.labelData?.height || 50,
|
||||||
},
|
},
|
||||||
|
(node.width = node.width + 2 * node.padding),
|
||||||
|
log.debug('UIO node label', node?.labelData?.width, node.padding),
|
||||||
];
|
];
|
||||||
node.width = node.width + 2 * node.padding;
|
|
||||||
log.debug('UIO node label', node?.labelData?.width, node.padding);
|
|
||||||
node.layoutOptions = {
|
node.layoutOptions = {
|
||||||
'spacing.baseValue': 30,
|
'spacing.baseValue': 30,
|
||||||
'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',
|
'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]',
|
||||||
@@ -857,7 +641,7 @@ export const render = async (
|
|||||||
try {
|
try {
|
||||||
g = await elk.layout(elkGraph);
|
g = await elk.layout(elkGraph);
|
||||||
log.debug('APA01 after - success');
|
log.debug('APA01 after - success');
|
||||||
log.info('APA01 layout result:', JSON.stringify(g, null, 2));
|
log.debug('APA01 layout result:', JSON.stringify(g, null, 2));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('APA01 ELK layout error:', error);
|
log.error('APA01 ELK layout error:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -918,10 +702,10 @@ export const render = async (
|
|||||||
// sw = Math.max(bbox.width, startNode.width, startNode.labels[0].width);
|
// sw = Math.max(bbox.width, startNode.width, startNode.labels[0].width);
|
||||||
sw = Math.max(startNode.width, startNode.labels[0].width + startNode.padding);
|
sw = Math.max(startNode.width, startNode.labels[0].width + startNode.padding);
|
||||||
// sw = startNode.width;
|
// sw = startNode.width;
|
||||||
log.info(
|
log.debug(
|
||||||
'UIO width',
|
'UIO width',
|
||||||
startNode.id,
|
startNode.id,
|
||||||
startNode.width,
|
startNode.with,
|
||||||
'bbox.width=',
|
'bbox.width=',
|
||||||
bbox.width,
|
bbox.width,
|
||||||
'lw=',
|
'lw=',
|
||||||
@@ -941,7 +725,7 @@ export const render = async (
|
|||||||
log.debug(
|
log.debug(
|
||||||
'UIO width',
|
'UIO width',
|
||||||
startNode.id,
|
startNode.id,
|
||||||
startNode.width,
|
startNode.with,
|
||||||
bbox.width,
|
bbox.width,
|
||||||
'EW = ',
|
'EW = ',
|
||||||
ew,
|
ew,
|
||||||
@@ -949,109 +733,38 @@ export const render = async (
|
|||||||
startNode.innerHTML
|
startNode.innerHTML
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
startNode.x = startNode.offset.posX + startNode.width / 2;
|
|
||||||
startNode.y = startNode.offset.posY + startNode.height / 2;
|
|
||||||
endNode.x = endNode.offset.posX + endNode.width / 2;
|
|
||||||
endNode.y = endNode.offset.posY + endNode.height / 2;
|
|
||||||
|
|
||||||
// Only add center points for non-subgraph nodes or when the edge path doesn't already end near the target
|
if (startNode.calcIntersect) {
|
||||||
const shouldAddStartCenter = startNode.shape !== 'rect33';
|
const intersection = startNode.calcIntersect(
|
||||||
const shouldAddEndCenter = endNode.shape !== 'rect33';
|
{
|
||||||
|
x: startNode.offset.posX + startNode.width / 2,
|
||||||
if (shouldAddStartCenter) {
|
y: startNode.offset.posY + startNode.height / 2,
|
||||||
edge.points.unshift({
|
width: startNode.width,
|
||||||
x: startNode.x,
|
height: startNode.height,
|
||||||
y: startNode.y,
|
},
|
||||||
});
|
edge.points[0]
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldAddEndCenter) {
|
|
||||||
edge.points.push({
|
|
||||||
x: endNode.x,
|
|
||||||
y: endNode.y,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug and sanitize points around cutter2
|
|
||||||
const prevPoints = Array.isArray(edge.points) ? [...edge.points] : [];
|
|
||||||
const endBounds = boundsFor(endNode);
|
|
||||||
log.debug(
|
|
||||||
'PPP cutter2: Points before cutter2:',
|
|
||||||
JSON.stringify(edge.points),
|
|
||||||
'endBounds:',
|
|
||||||
endBounds,
|
|
||||||
onBorder(endBounds, edge.points[edge.points.length - 1])
|
|
||||||
);
|
|
||||||
// Block for reducing variable scope and guardrails for the cutter function
|
|
||||||
{
|
|
||||||
const startBounds = boundsFor(startNode);
|
|
||||||
const endBounds = boundsFor(endNode);
|
|
||||||
|
|
||||||
const startIsGroup = !!startNode?.isGroup;
|
|
||||||
const endIsGroup = !!endNode?.isGroup;
|
|
||||||
|
|
||||||
const { candidate: startCandidate, centerApprox: startCenterApprox } =
|
|
||||||
getCandidateBorderPoint(prevPoints as P[], startNode, 'start');
|
|
||||||
const { candidate: endCandidate, centerApprox: endCenterApprox } =
|
|
||||||
getCandidateBorderPoint(prevPoints as P[], endNode, 'end');
|
|
||||||
|
|
||||||
const skipStart = startIsGroup && onBorder(startBounds, startCandidate);
|
|
||||||
const skipEnd = endIsGroup && onBorder(endBounds, endCandidate);
|
|
||||||
|
|
||||||
dropAutoCenterPoint(prevPoints as P[], 'start', skipStart && startCenterApprox);
|
|
||||||
dropAutoCenterPoint(prevPoints as P[], 'end', skipEnd && endCenterApprox);
|
|
||||||
|
|
||||||
if (skipStart || skipEnd) {
|
|
||||||
if (!skipStart) {
|
|
||||||
applyStartIntersectionIfNeeded(prevPoints as P[], startNode, startBounds);
|
|
||||||
}
|
|
||||||
if (!skipEnd) {
|
|
||||||
applyEndIntersectionIfNeeded(prevPoints as P[], endNode, endBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug('PPP cutter2: skipping cutter2 due to on-border group endpoint(s)', {
|
|
||||||
skipStart,
|
|
||||||
skipEnd,
|
|
||||||
startCenterApprox,
|
|
||||||
endCenterApprox,
|
|
||||||
startCandidate,
|
|
||||||
endCandidate,
|
|
||||||
});
|
|
||||||
edge.points = prevPoints;
|
|
||||||
} else {
|
|
||||||
edge.points = cutter2(startNode, endNode, prevPoints);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.debug('PPP cutter2: Points after cutter2:', JSON.stringify(edge.points));
|
|
||||||
const hasNaN = (pts: { x: number; y: number }[]) =>
|
|
||||||
pts?.some((p) => !Number.isFinite(p?.x) || !Number.isFinite(p?.y));
|
|
||||||
if (!Array.isArray(edge.points) || edge.points.length < 2 || hasNaN(edge.points)) {
|
|
||||||
log.warn(
|
|
||||||
'POI cutter2: Invalid points from cutter2, falling back to prevPoints',
|
|
||||||
edge.points
|
|
||||||
);
|
);
|
||||||
// Fallback to previous points and strip any invalid ones just in case
|
|
||||||
const cleaned = prevPoints.filter((p) => Number.isFinite(p?.x) && Number.isFinite(p?.y));
|
if (distance(intersection, edge.points[0]) > epsilon) {
|
||||||
edge.points = cleaned.length >= 2 ? cleaned : prevPoints;
|
edge.points.unshift(intersection);
|
||||||
}
|
|
||||||
log.debug('UIO cutter2: Points after cutter2 (sanitized):', edge.points);
|
|
||||||
// Remove consecutive duplicate points to avoid zero-length segments in path builders
|
|
||||||
const deduped = edge.points.filter(
|
|
||||||
(p: { x: number; y: number }, i: number, arr: { x: number; y: number }[]) => {
|
|
||||||
if (i === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const prev = arr[i - 1];
|
|
||||||
return Math.abs(p.x - prev.x) > 1e-6 || Math.abs(p.y - prev.y) > 1e-6;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
if (deduped.length !== edge.points.length) {
|
|
||||||
log.debug('UIO cutter2: removed consecutive duplicate points', {
|
|
||||||
before: edge.points,
|
|
||||||
after: deduped,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
edge.points = deduped;
|
if (endNode.calcIntersect) {
|
||||||
|
const intersection = endNode.calcIntersect(
|
||||||
|
{
|
||||||
|
x: endNode.offset.posX + endNode.width / 2,
|
||||||
|
y: endNode.offset.posY + endNode.height / 2,
|
||||||
|
width: endNode.width,
|
||||||
|
height: endNode.height,
|
||||||
|
},
|
||||||
|
edge.points[edge.points.length - 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distance(intersection, edge.points[edge.points.length - 1]) > epsilon) {
|
||||||
|
edge.points.push(intersection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const paths = insertEdge(
|
const paths = insertEdge(
|
||||||
edgesEl,
|
edgesEl,
|
||||||
edge,
|
edge,
|
||||||
@@ -1059,10 +772,8 @@ export const render = async (
|
|||||||
data4Layout.type,
|
data4Layout.type,
|
||||||
startNode,
|
startNode,
|
||||||
endNode,
|
endNode,
|
||||||
data4Layout.diagramId,
|
data4Layout.diagramId
|
||||||
true
|
|
||||||
);
|
);
|
||||||
log.info('APA12 edge points after insert', JSON.stringify(edge.points));
|
|
||||||
|
|
||||||
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
|
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
|
||||||
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
|
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
|
||||||
|
@@ -154,7 +154,6 @@
|
|||||||
### 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 ...)
|
||||||
@@ -173,14 +172,12 @@
|
|||||||
### 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
|
||||||
|
|
||||||
|
@@ -67,7 +67,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^7.0.4",
|
"@braintree/sanitize-url": "^7.1.1",
|
||||||
"@iconify/utils": "^3.0.1",
|
"@iconify/utils": "^3.0.1",
|
||||||
"@mermaid-js/parser": "workspace:^",
|
"@mermaid-js/parser": "workspace:^",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
@@ -77,19 +77,19 @@
|
|||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
"d3-sankey": "^0.12.3",
|
"d3-sankey": "^0.12.3",
|
||||||
"dagre-d3-es": "7.0.11",
|
"dagre-d3-es": "7.0.11",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.18",
|
||||||
"dompurify": "^3.2.5",
|
"dompurify": "^3.2.5",
|
||||||
"katex": "^0.16.22",
|
"katex": "^0.16.22",
|
||||||
"khroma": "^2.1.0",
|
"khroma": "^2.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"marked": "^16.0.0",
|
"marked": "^16.2.1",
|
||||||
"roughjs": "^4.6.6",
|
"roughjs": "^4.6.6",
|
||||||
"stylis": "^4.3.6",
|
"stylis": "^4.3.6",
|
||||||
"ts-dedent": "^2.2.0",
|
"ts-dedent": "^2.2.0",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@adobe/jsonschema2md": "^8.0.2",
|
"@adobe/jsonschema2md": "^8.0.5",
|
||||||
"@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.0",
|
"canvas": "^3.1.2",
|
||||||
"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.0.2",
|
"globby": "^14.1.0",
|
||||||
"jison": "^0.4.18",
|
"jison": "^0.4.18",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.8",
|
||||||
"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.2",
|
"prettier": "^3.5.3",
|
||||||
"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.10",
|
"start-server-and-test": "^2.0.13",
|
||||||
"type-fest": "^4.35.0",
|
"type-fest": "^4.35.0",
|
||||||
"typedoc": "^0.28.9",
|
"typedoc": "^0.28.12",
|
||||||
"typedoc-plugin-markdown": "^4.8.0",
|
"typedoc-plugin-markdown": "^4.8.1",
|
||||||
"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.0.2",
|
"vitepress": "^1.6.4",
|
||||||
"vitepress-plugin-search": "1.0.4-alpha.22"
|
"vitepress-plugin-search": "1.0.4-alpha.22"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
@@ -1,48 +0,0 @@
|
|||||||
import { describe } from 'vitest';
|
|
||||||
import { draw } from './architectureRenderer.js';
|
|
||||||
import { Diagram } from '../../Diagram.js';
|
|
||||||
import { addDetector } from '../../diagram-api/detectType.js';
|
|
||||||
import architectureDetector from './architectureDetector.js';
|
|
||||||
import { ensureNodeFromSelector, jsdomIt } from '../../tests/util.js';
|
|
||||||
|
|
||||||
const { id, detector, loader } = architectureDetector;
|
|
||||||
|
|
||||||
addDetector(id, detector, loader); // Add architecture schemas to Mermaid
|
|
||||||
|
|
||||||
describe('architecture diagram SVGs', () => {
|
|
||||||
jsdomIt('should add ids', async () => {
|
|
||||||
const svgNode = await drawDiagram(`
|
|
||||||
architecture-beta
|
|
||||||
group api(cloud)[API]
|
|
||||||
|
|
||||||
service db(database)[Database] in api
|
|
||||||
service disk1(disk)[Storage] in api
|
|
||||||
service disk2(disk)[Storage] in api
|
|
||||||
service server(server)[Server] in api
|
|
||||||
|
|
||||||
db:L -- R:server
|
|
||||||
disk1:T -- B:server
|
|
||||||
disk2:T -- B:db
|
|
||||||
`);
|
|
||||||
|
|
||||||
const nodesForGroup = svgNode.querySelectorAll(`#group-api`);
|
|
||||||
expect(nodesForGroup.length).toBe(1);
|
|
||||||
|
|
||||||
const serviceIds = [...svgNode.querySelectorAll(`[id^=service-]`)].map(({ id }) => id).sort();
|
|
||||||
expect(serviceIds).toStrictEqual([
|
|
||||||
'service-db',
|
|
||||||
'service-disk1',
|
|
||||||
'service-disk2',
|
|
||||||
'service-server',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const edgeIds = [...svgNode.querySelectorAll(`.edge[id^=L_]`)].map(({ id }) => id).sort();
|
|
||||||
expect(edgeIds).toStrictEqual(['L_db_server_0', 'L_disk1_server_0', 'L_disk2_db_0']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
async function drawDiagram(diagramText: string): Promise<Element> {
|
|
||||||
const diagram = await Diagram.fromText(diagramText, {});
|
|
||||||
await draw('NOT_USED', 'svg', '1.0.0', diagram);
|
|
||||||
return ensureNodeFromSelector('#svg');
|
|
||||||
}
|
|
@@ -20,7 +20,6 @@ import {
|
|||||||
type ArchitectureJunction,
|
type ArchitectureJunction,
|
||||||
type ArchitectureService,
|
type ArchitectureService,
|
||||||
} from './architectureTypes.js';
|
} from './architectureTypes.js';
|
||||||
import { getEdgeId } from '../../utils.js';
|
|
||||||
|
|
||||||
export const drawEdges = async function (
|
export const drawEdges = async function (
|
||||||
edgesEl: D3Element,
|
edgesEl: D3Element,
|
||||||
@@ -92,8 +91,7 @@ export const drawEdges = async function (
|
|||||||
|
|
||||||
g.insert('path')
|
g.insert('path')
|
||||||
.attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `)
|
.attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `)
|
||||||
.attr('class', 'edge')
|
.attr('class', 'edge');
|
||||||
.attr('id', getEdgeId(source, target, { prefix: 'L' }));
|
|
||||||
|
|
||||||
if (sourceArrow) {
|
if (sourceArrow) {
|
||||||
const xShift = isArchitectureDirectionX(sourceDir)
|
const xShift = isArchitectureDirectionX(sourceDir)
|
||||||
@@ -208,9 +206,8 @@ export const drawGroups = async function (
|
|||||||
if (data.type === 'group') {
|
if (data.type === 'group') {
|
||||||
const { h, w, x1, y1 } = node.boundingBox();
|
const { h, w, x1, y1 } = node.boundingBox();
|
||||||
|
|
||||||
const groupsNode = groupsEl.append('rect');
|
groupsEl
|
||||||
groupsNode
|
.append('rect')
|
||||||
.attr('id', `group-${data.id}`)
|
|
||||||
.attr('x', x1 + halfIconSize)
|
.attr('x', x1 + halfIconSize)
|
||||||
.attr('y', y1 + halfIconSize)
|
.attr('y', y1 + halfIconSize)
|
||||||
.attr('width', w)
|
.attr('width', w)
|
||||||
@@ -265,7 +262,6 @@ export const drawGroups = async function (
|
|||||||
')'
|
')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
db.setElementForId(data.id, groupsNode);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -346,9 +342,9 @@ export const drawServices = async function (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceElem.attr('id', `service-${service.id}`).attr('class', 'architecture-service');
|
serviceElem.attr('class', 'architecture-service');
|
||||||
|
|
||||||
const { width, height } = serviceElem.node().getBBox();
|
const { width, height } = serviceElem._groups[0][0].getBBox();
|
||||||
service.width = width;
|
service.width = width;
|
||||||
service.height = height;
|
service.height = height;
|
||||||
db.setElementForId(service.id, serviceElem);
|
db.setElementForId(service.id, serviceElem);
|
||||||
|
@@ -78,7 +78,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
"off" return 'off';
|
"off" return 'off';
|
||||||
"," return ',';
|
"," return ',';
|
||||||
";" return 'NEWLINE';
|
";" return 'NEWLINE';
|
||||||
[^\/\\\+\()\+<\->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)|\-\|\\|\-\\|\-\/|\-\/\/|\-\|\/|\/\|\-|\\\|\-|\/\/\-|\\\\\-|\/\|\-|\-\-\|\\|\-\-|\(\)))[\-]*[^\+<\->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; } //final_4.11
|
[^+<\->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)))[\-]*[^\+<\->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; }
|
||||||
"->>" return 'SOLID_ARROW';
|
"->>" return 'SOLID_ARROW';
|
||||||
"<<->>" return 'BIDIRECTIONAL_SOLID_ARROW';
|
"<<->>" return 'BIDIRECTIONAL_SOLID_ARROW';
|
||||||
"-->>" return 'DOTTED_ARROW';
|
"-->>" return 'DOTTED_ARROW';
|
||||||
@@ -89,36 +89,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
\-\-[x] return 'DOTTED_CROSS';
|
\-\-[x] return 'DOTTED_CROSS';
|
||||||
\-[\)] return 'SOLID_POINT';
|
\-[\)] return 'SOLID_POINT';
|
||||||
\-\-[\)] return 'DOTTED_POINT';
|
\-\-[\)] return 'DOTTED_POINT';
|
||||||
|
|
||||||
//normal-dotted
|
|
||||||
\-\-\|\\ return 'SOLID_ARROW_TOP_DOTTED';
|
|
||||||
\-\-\|\/ return 'SOLID_ARROW_BOTTOM_DOTTED';
|
|
||||||
\-\-\\\\ return 'STICK_ARROW_TOP_DOTTED';
|
|
||||||
\-\-\/\/ return 'STICK_ARROW_BOTTOM_DOTTED';
|
|
||||||
|
|
||||||
//reverse-dotted
|
|
||||||
\/\|\-\- return 'SOLID_ARROW_TOP_REVERSE_DOTTED';
|
|
||||||
\\\|\-\- return 'SOLID_ARROW_BOTTOM_REVERSE_DOTTED';
|
|
||||||
\/\/\-\- return 'STICK_ARROW_TOP_REVERSE_DOTTED';
|
|
||||||
\\\\\-\- return 'STICK_ARROW_BOTTOM_REVERSE_DOTTED';
|
|
||||||
|
|
||||||
//normal
|
|
||||||
\-\|\\ return 'SOLID_ARROW_TOP';
|
|
||||||
\-\|\/ return 'SOLID_ARROW_BOTTOM';
|
|
||||||
\-\\\\ return 'STICK_ARROW_TOP';
|
|
||||||
\-\/\/ return 'STICK_ARROW_BOTTOM';
|
|
||||||
|
|
||||||
//reverse
|
|
||||||
\/\|\- return 'SOLID_ARROW_TOP_REVERSE';
|
|
||||||
\\\|\- return 'SOLID_ARROW_BOTTOM_REVERSE';
|
|
||||||
\/\/\- return 'STICK_ARROW_TOP_REVERSE';
|
|
||||||
\\\\\- return 'STICK_ARROW_BOTTOM_REVERSE';
|
|
||||||
|
|
||||||
":"(?:(?:no)?wrap:)?[^#\n;]* return 'TXT';
|
":"(?:(?:no)?wrap:)?[^#\n;]* return 'TXT';
|
||||||
":" return 'TXT';
|
":" return 'TXT';
|
||||||
"+" return '+';
|
"+" return '+';
|
||||||
"-" return '-';
|
"-" return '-';
|
||||||
"()" return '()';
|
|
||||||
<<EOF>> return 'NEWLINE';
|
<<EOF>> return 'NEWLINE';
|
||||||
. return 'INVALID';
|
. return 'INVALID';
|
||||||
|
|
||||||
@@ -330,20 +304,6 @@ signal
|
|||||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
|
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
|
||||||
{type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1.actor}
|
{type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1.actor}
|
||||||
]}
|
]}
|
||||||
| actor signaltype '()' actor text2
|
|
||||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5, activate: true, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION},
|
|
||||||
{type: 'centralConnection', signalType: yy.LINETYPE.CENTRAL_CONNECTION, actor: $4.actor, }
|
|
||||||
]}
|
|
||||||
|
|
||||||
| actor '()' signaltype actor text2
|
|
||||||
{ $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$3, msg:$5, activate: false, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE},
|
|
||||||
{type: 'centralConnectionReverse', signalType: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE, actor: $1.actor}
|
|
||||||
]}
|
|
||||||
| actor '()' signaltype '()' actor text2
|
|
||||||
{ $$ = [$1,$5,{type: 'addMessage', from:$1.actor, to:$5.actor, signalType:$3, msg:$6, activate: true, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION_DUAL},
|
|
||||||
{type: 'centralConnection', signalType: yy.LINETYPE.CENTRAL_CONNECTION, actor: $5.actor, },
|
|
||||||
{type: 'centralConnectionReverse', signalType: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE, actor: $1.actor}
|
|
||||||
]}
|
|
||||||
| actor signaltype actor text2
|
| actor signaltype actor text2
|
||||||
{ $$ = [$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}]}
|
||||||
;
|
;
|
||||||
@@ -377,28 +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; }
|
||||||
| SOLID_ARROW_TOP { $$ = yy.LINETYPE.SOLID_TOP; }
|
|
||||||
| SOLID_ARROW_BOTTOM { $$ = yy.LINETYPE.SOLID_BOTTOM; }
|
|
||||||
| STICK_ARROW_TOP { $$ = yy.LINETYPE.STICK_TOP; }
|
|
||||||
| STICK_ARROW_BOTTOM { $$ = yy.LINETYPE.STICK_BOTTOM; }
|
|
||||||
|
|
||||||
| SOLID_ARROW_TOP_DOTTED { $$ = yy.LINETYPE.SOLID_TOP_DOTTED; }
|
|
||||||
| SOLID_ARROW_BOTTOM_DOTTED { $$ = yy.LINETYPE.SOLID_BOTTOM_DOTTED; }
|
|
||||||
| STICK_ARROW_TOP_DOTTED { $$ = yy.LINETYPE.STICK_TOP_DOTTED; }
|
|
||||||
| STICK_ARROW_BOTTOM_DOTTED { $$ = yy.LINETYPE.STICK_BOTTOM_DOTTED; }
|
|
||||||
|
|
||||||
| SOLID_ARROW_TOP_REVERSE { $$ = yy.LINETYPE.SOLID_ARROW_TOP_REVERSE; }
|
|
||||||
| SOLID_ARROW_BOTTOM_REVERSE { $$ = yy.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE; }
|
|
||||||
| STICK_ARROW_TOP_REVERSE { $$ = yy.LINETYPE.STICK_ARROW_TOP_REVERSE; }
|
|
||||||
| STICK_ARROW_BOTTOM_REVERSE { $$ = yy.LINETYPE.STICK_ARROW_BOTTOM_REVERSE; }
|
|
||||||
|
|
||||||
| SOLID_ARROW_TOP_REVERSE_DOTTED { $$ = yy.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED; }
|
|
||||||
| SOLID_ARROW_BOTTOM_REVERSE_DOTTED { $$ = yy.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED; }
|
|
||||||
| STICK_ARROW_TOP_REVERSE_DOTTED { $$ = yy.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED; }
|
|
||||||
| STICK_ARROW_BOTTOM_REVERSE_DOTTED { $$ = yy.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED; }
|
|
||||||
|
|
||||||
| 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; }
|
||||||
|
@@ -64,30 +64,6 @@ const LINETYPE = {
|
|||||||
PAR_OVER_START: 32,
|
PAR_OVER_START: 32,
|
||||||
BIDIRECTIONAL_SOLID: 33,
|
BIDIRECTIONAL_SOLID: 33,
|
||||||
BIDIRECTIONAL_DOTTED: 34,
|
BIDIRECTIONAL_DOTTED: 34,
|
||||||
|
|
||||||
SOLID_TOP: 41,
|
|
||||||
SOLID_BOTTOM: 42,
|
|
||||||
STICK_TOP: 43,
|
|
||||||
STICK_BOTTOM: 44,
|
|
||||||
|
|
||||||
SOLID_ARROW_TOP_REVERSE: 45,
|
|
||||||
SOLID_ARROW_BOTTOM_REVERSE: 46,
|
|
||||||
STICK_ARROW_TOP_REVERSE: 47,
|
|
||||||
STICK_ARROW_BOTTOM_REVERSE: 48,
|
|
||||||
|
|
||||||
SOLID_TOP_DOTTED: 51,
|
|
||||||
SOLID_BOTTOM_DOTTED: 52,
|
|
||||||
STICK_TOP_DOTTED: 53,
|
|
||||||
STICK_BOTTOM_DOTTED: 54,
|
|
||||||
|
|
||||||
SOLID_ARROW_TOP_REVERSE_DOTTED: 55,
|
|
||||||
SOLID_ARROW_BOTTOM_REVERSE_DOTTED: 56,
|
|
||||||
STICK_ARROW_TOP_REVERSE_DOTTED: 57,
|
|
||||||
STICK_ARROW_BOTTOM_REVERSE_DOTTED: 58,
|
|
||||||
|
|
||||||
CENTRAL_CONNECTION: 59,
|
|
||||||
CENTRAL_CONNECTION_REVERSE: 60,
|
|
||||||
CENTRAL_CONNECTION_DUAL: 61,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const ARROWTYPE = {
|
const ARROWTYPE = {
|
||||||
@@ -268,8 +244,7 @@ export class SequenceDB implements DiagramDB {
|
|||||||
idTo?: Message['to'],
|
idTo?: Message['to'],
|
||||||
message?: { text: string; wrap: boolean },
|
message?: { text: string; wrap: boolean },
|
||||||
messageType?: number,
|
messageType?: number,
|
||||||
activate = false,
|
activate = false
|
||||||
centralConnection?: number
|
|
||||||
) {
|
) {
|
||||||
if (messageType === this.LINETYPE.ACTIVE_END) {
|
if (messageType === this.LINETYPE.ACTIVE_END) {
|
||||||
const cnt = this.activationCount(idFrom ?? '');
|
const cnt = this.activationCount(idFrom ?? '');
|
||||||
@@ -296,7 +271,6 @@ export class SequenceDB implements DiagramDB {
|
|||||||
wrap: message?.wrap ?? this.autoWrap(),
|
wrap: message?.wrap ?? this.autoWrap(),
|
||||||
type: messageType,
|
type: messageType,
|
||||||
activate,
|
activate,
|
||||||
centralConnection: centralConnection ?? 0,
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -589,12 +563,6 @@ export class SequenceDB implements DiagramDB {
|
|||||||
case 'activeStart':
|
case 'activeStart':
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
break;
|
break;
|
||||||
case 'centralConnection':
|
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
|
||||||
break;
|
|
||||||
case 'centralConnectionReverse':
|
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
|
||||||
break;
|
|
||||||
case 'activeEnd':
|
case 'activeEnd':
|
||||||
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
this.addSignal(param.actor, undefined, undefined, param.signalType);
|
||||||
break;
|
break;
|
||||||
@@ -638,14 +606,7 @@ export class SequenceDB implements DiagramDB {
|
|||||||
this.state.records.lastDestroyed = undefined;
|
this.state.records.lastDestroyed = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.addSignal(
|
this.addSignal(param.from, param.to, param.msg, param.signalType, param.activate);
|
||||||
param.from,
|
|
||||||
param.to,
|
|
||||||
param.msg,
|
|
||||||
param.signalType,
|
|
||||||
param.activate,
|
|
||||||
param.centralConnection
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case 'boxStart':
|
case 'boxStart':
|
||||||
this.addBox(param.boxData);
|
this.addBox(param.boxData);
|
||||||
|
@@ -104,7 +104,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Alice",
|
"from": "Alice",
|
||||||
"id": "0",
|
"id": "0",
|
||||||
"message": "Hello Bob, how are you?",
|
"message": "Hello Bob, how are you?",
|
||||||
@@ -114,7 +113,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Bob",
|
"from": "Bob",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"message": "I am good thanks!",
|
"message": "I am good thanks!",
|
||||||
@@ -133,7 +131,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Alice",
|
"from": "Alice",
|
||||||
"id": "0",
|
"id": "0",
|
||||||
"message": "Hello Bob, how are you?",
|
"message": "Hello Bob, how are you?",
|
||||||
@@ -143,7 +140,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Bob",
|
"from": "Bob",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"message": "I am good thanks!",
|
"message": "I am good thanks!",
|
||||||
@@ -164,7 +160,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "Alice",
|
"from": "Alice",
|
||||||
"id": "0",
|
"id": "0",
|
||||||
"message": "Hello John, how are you?",
|
"message": "Hello John, how are you?",
|
||||||
@@ -174,7 +169,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"activate": false,
|
"activate": false,
|
||||||
"centralConnection": 0,
|
|
||||||
"from": "John",
|
"from": "John",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"message": "I am good thanks!",
|
"message": "I am good thanks!",
|
||||||
@@ -187,254 +181,6 @@ describe('more than one sequence diagram', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Central Connection Parsing', () => {
|
|
||||||
describe('when parsing central connection syntax', () => {
|
|
||||||
it('should parse actor ()->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
// Find the actual message (type: 'addMessage')
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 0, // SOLID (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()-->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()-->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 1, // DOTTED (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ->>() actor syntax as CENTRAL_CONNECTION', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(2); // addMessage + centralConnection (no activation for this pattern)
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 59, // CENTRAL_CONNECTION
|
|
||||||
activate: true,
|
|
||||||
type: 0, // SOLID (based on actual parsing)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()-->> actor syntax as CENTRAL_CONNECTION_REVERSE', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()-->> Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(2); // addMessage + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 60, // CENTRAL_CONNECTION_REVERSE
|
|
||||||
activate: false,
|
|
||||||
type: 1, // DOTTED (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()->> actor syntax as CENTRAL_CONNECTION_REVERSE', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()->> Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(2); // addMessage + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 60, // CENTRAL_CONNECTION_REVERSE
|
|
||||||
activate: false,
|
|
||||||
type: 0, // SOLID (based on test output)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()<<-->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()<<-->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 34, // BIDIRECTIONAL_DOTTED
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse actor ()<<->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()<<->>() Bob: Hello Bob, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse
|
|
||||||
|
|
||||||
const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessage).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
message: 'Hello Bob, how are you?',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL
|
|
||||||
activate: true,
|
|
||||||
type: 33, // BIDIRECTIONAL_SOLID
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple central connection types in one diagram', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
participant Charlie
|
|
||||||
Alice ()->>() Bob: Message 1
|
|
||||||
Bob ()-->> Charlie: Message 2
|
|
||||||
Charlie ()<<-->>() Alice: Message 3
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(8); // 3 addMessages + 5 central connection markers
|
|
||||||
|
|
||||||
// Filter to get only the actual messages
|
|
||||||
const actualMessages = messages.filter((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessages).toHaveLength(3);
|
|
||||||
|
|
||||||
expect(actualMessages[0]).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()->>())
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(actualMessages[1]).toMatchObject({
|
|
||||||
from: 'Bob',
|
|
||||||
to: 'Charlie',
|
|
||||||
centralConnection: 60, // CENTRAL_CONNECTION_REVERSE (()-->>)
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(actualMessages[2]).toMatchObject({
|
|
||||||
from: 'Charlie',
|
|
||||||
to: 'Alice',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()<<-->>())
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle central connections with different arrow types', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ()-x() Bob: Cross message
|
|
||||||
Alice ()--x() Bob: Dotted cross message
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(6); // 2 addMessages + 4 central connection markers
|
|
||||||
|
|
||||||
const actualMessages = messages.filter((msg) => msg.type !== undefined && msg.from && msg.to);
|
|
||||||
expect(actualMessages).toHaveLength(2);
|
|
||||||
|
|
||||||
expect(actualMessages[0]).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()-x())
|
|
||||||
type: 3, // SOLID_CROSS
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(actualMessages[1]).toMatchObject({
|
|
||||||
from: 'Alice',
|
|
||||||
to: 'Bob',
|
|
||||||
centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()--x())
|
|
||||||
type: 4, // DOTTED_CROSS
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not break existing parsing without central connections', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice ->> Bob: Normal message
|
|
||||||
Bob -->> Alice: Normal dotted message
|
|
||||||
Alice -x Bob: Normal cross message
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
expect(messages).toHaveLength(3);
|
|
||||||
|
|
||||||
messages.forEach((msg) => {
|
|
||||||
expect(msg.centralConnection).toBe(0); // No central connection
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(messages[0].type).toBe(0); // SOLID (based on actual parsing)
|
|
||||||
expect(messages[1].type).toBe(1); // DOTTED (based on actual parsing)
|
|
||||||
expect(messages[2].type).toBe(3); // SOLID_CROSS
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when parsing a sequenceDiagram', function () {
|
describe('when parsing a sequenceDiagram', function () {
|
||||||
let diagram;
|
let diagram;
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
@@ -2312,36 +2058,6 @@ 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');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1 should parse ', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
actor Bob
|
|
||||||
actor Alice
|
|
||||||
Bob -|\\ Alice: Hello Alice, how are you?
|
|
||||||
Bob -|/ Alice: Hello Alice, how are you?
|
|
||||||
Bob -// Alice: Hello Alice, how are you?
|
|
||||||
Bob -\\\\ Alice: Hello Alice, how are you?
|
|
||||||
|
|
||||||
Bob \\|- Alice: Hello Alice, how are you?
|
|
||||||
Bob /|- Alice: Hello Alice, how are you?
|
|
||||||
Bob //- Alice: Hello Alice, how are you?
|
|
||||||
Bob \\\\- Alice: Hello Alice, how are you?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('2 should parse ', async () => {
|
|
||||||
const diagram = await Diagram.fromText(`
|
|
||||||
sequenceDiagram
|
|
||||||
actor Bob
|
|
||||||
actor Alice
|
|
||||||
Alice ()<<->>() Bob: hey?
|
|
||||||
`);
|
|
||||||
|
|
||||||
const messages = diagram.db.getMessages();
|
|
||||||
});
|
|
||||||
describe('when parsing extended participant syntax', () => {
|
describe('when parsing extended participant syntax', () => {
|
||||||
it('should parse participants with different quote styles and whitespace', async () => {
|
it('should parse participants with different quote styles and whitespace', async () => {
|
||||||
const diagram = await Diagram.fromText(`
|
const diagram = await Diagram.fromText(`
|
||||||
|
@@ -282,49 +282,6 @@ const drawNote = async function (elem: any, noteModel: NoteModel) {
|
|||||||
bounds.models.addNote(noteModel);
|
bounds.models.addNote(noteModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawCentralConnection = function (
|
|
||||||
elem: any,
|
|
||||||
msg: any,
|
|
||||||
msgModel: any,
|
|
||||||
diagObj: Diagram,
|
|
||||||
startx: number,
|
|
||||||
stopx: number,
|
|
||||||
lineStartY: number
|
|
||||||
) {
|
|
||||||
const actors = diagObj.db.getActors();
|
|
||||||
const fromActor = actors.get(msg.from);
|
|
||||||
const toActor = actors.get(msg.to);
|
|
||||||
const fromCenter = fromActor.x + fromActor.width / 2;
|
|
||||||
const toCenter = toActor.x + toActor.width / 2;
|
|
||||||
|
|
||||||
const g = elem.append('g');
|
|
||||||
|
|
||||||
const drawCircle = (cx: number) => {
|
|
||||||
g.append('circle')
|
|
||||||
.attr('cx', cx)
|
|
||||||
.attr('cy', lineStartY)
|
|
||||||
.attr('r', 5)
|
|
||||||
.attr('width', 10)
|
|
||||||
.attr('height', 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } =
|
|
||||||
diagObj.db.LINETYPE;
|
|
||||||
|
|
||||||
switch (msg.centralConnection) {
|
|
||||||
case CENTRAL_CONNECTION:
|
|
||||||
drawCircle(toCenter);
|
|
||||||
break;
|
|
||||||
case CENTRAL_CONNECTION_REVERSE:
|
|
||||||
drawCircle(fromCenter);
|
|
||||||
break;
|
|
||||||
case CENTRAL_CONNECTION_DUAL:
|
|
||||||
drawCircle(fromCenter);
|
|
||||||
drawCircle(toCenter);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const messageFont = (cnf) => {
|
const messageFont = (cnf) => {
|
||||||
return {
|
return {
|
||||||
fontFamily: cnf.messageFontFamily,
|
fontFamily: cnf.messageFontFamily,
|
||||||
@@ -410,7 +367,7 @@ async function boundMessage(_diagram, msgModel): Promise<number> {
|
|||||||
* @param lineStartY - The Y coordinate at which the message line starts
|
* @param lineStartY - The Y coordinate at which the message line starts
|
||||||
* @param diagObj - The diagram object.
|
* @param diagObj - The diagram object.
|
||||||
*/
|
*/
|
||||||
const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram, msg) {
|
const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram) {
|
||||||
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
||||||
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
||||||
const textObj = svgDrawCommon.getTextObj();
|
const textObj = svgDrawCommon.getTextObj();
|
||||||
@@ -476,9 +433,6 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
line.attr('y1', lineStartY);
|
line.attr('y1', lineStartY);
|
||||||
line.attr('x2', stopx);
|
line.attr('x2', stopx);
|
||||||
line.attr('y2', lineStartY);
|
line.attr('y2', lineStartY);
|
||||||
if (hasCentralConnection(msg, diagObj)) {
|
|
||||||
drawCentralConnection(diagram, msg, msgModel, diagObj, startx, stopx, lineStartY);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Make an SVG Container
|
// Make an SVG Container
|
||||||
// Draw the line
|
// Draw the line
|
||||||
@@ -487,15 +441,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
type === diagObj.db.LINETYPE.DOTTED_CROSS ||
|
type === diagObj.db.LINETYPE.DOTTED_CROSS ||
|
||||||
type === diagObj.db.LINETYPE.DOTTED_POINT ||
|
type === diagObj.db.LINETYPE.DOTTED_POINT ||
|
||||||
type === diagObj.db.LINETYPE.DOTTED_OPEN ||
|
type === diagObj.db.LINETYPE.DOTTED_OPEN ||
|
||||||
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED ||
|
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED
|
||||||
type === diagObj.db.LINETYPE.SOLID_TOP_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_TOP_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED
|
|
||||||
) {
|
) {
|
||||||
line.style('stroke-dasharray', '3, 3');
|
line.style('stroke-dasharray', '3, 3');
|
||||||
line.attr('class', 'messageLine1');
|
line.attr('class', 'messageLine1');
|
||||||
@@ -511,51 +457,6 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
line.attr('stroke-width', 2);
|
line.attr('stroke-width', 2);
|
||||||
line.attr('stroke', 'none'); // handled by theme/css anyway
|
line.attr('stroke', 'none'); // handled by theme/css anyway
|
||||||
line.style('fill', 'none'); // remove any fill colour
|
line.style('fill', 'none'); // remove any fill colour
|
||||||
|
|
||||||
if (type === diagObj.db.LINETYPE.SOLID_TOP || type === diagObj.db.LINETYPE.SOLID_TOP_DOTTED) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#solidTopArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_BOTTOM ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#solidBottomArrowHead)');
|
|
||||||
}
|
|
||||||
if (type === diagObj.db.LINETYPE.STICK_TOP || type === diagObj.db.LINETYPE.STICK_TOP_DOTTED) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#stickTopArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.STICK_BOTTOM ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-end', 'url(' + url + '#stickBottomArrowHead)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#solidBottomArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#solidTopArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#stickBottomArrowHead)');
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED
|
|
||||||
) {
|
|
||||||
line.attr('marker-start', 'url(' + url + '#stickTopArrowHead)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) {
|
if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) {
|
||||||
line.attr('marker-end', 'url(' + url + '#arrowhead)');
|
line.attr('marker-end', 'url(' + url + '#arrowhead)');
|
||||||
}
|
}
|
||||||
@@ -580,18 +481,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
type === diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID ||
|
type === diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID ||
|
||||||
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED;
|
type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED;
|
||||||
|
|
||||||
const isReverseArrowType =
|
if (isBidirectional) {
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE ||
|
|
||||||
type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED;
|
|
||||||
|
|
||||||
let x = 0;
|
|
||||||
if (isBidirectional || isReverseArrowType) {
|
|
||||||
const SEQUENCE_NUMBER_RADIUS = 6;
|
const SEQUENCE_NUMBER_RADIUS = 6;
|
||||||
|
|
||||||
if (startx < stopx) {
|
if (startx < stopx) {
|
||||||
@@ -599,7 +489,6 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
} else {
|
} else {
|
||||||
line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS);
|
line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS);
|
||||||
}
|
}
|
||||||
x = 3.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
diagram
|
diagram
|
||||||
@@ -609,8 +498,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
.attr('x2', startx)
|
.attr('x2', startx)
|
||||||
.attr('y2', lineStartY)
|
.attr('y2', lineStartY)
|
||||||
.attr('stroke-width', 0)
|
.attr('stroke-width', 0)
|
||||||
.attr('marker-start', 'url(' + url + '#sequencenumber)')
|
.attr('marker-start', 'url(' + url + '#sequencenumber)');
|
||||||
.attr('transform', `translate(-${x}, 0)`);
|
|
||||||
|
|
||||||
diagram
|
diagram
|
||||||
.append('text')
|
.append('text')
|
||||||
@@ -620,8 +508,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO
|
|||||||
.attr('font-size', '12px')
|
.attr('font-size', '12px')
|
||||||
.attr('text-anchor', 'middle')
|
.attr('text-anchor', 'middle')
|
||||||
.attr('class', 'sequenceNumber')
|
.attr('class', 'sequenceNumber')
|
||||||
.text(sequenceIndex)
|
.text(sequenceIndex);
|
||||||
.attr('transform', `translate(-${x}, 0)`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -970,10 +857,6 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
svgDraw.insertArrowCrossHead(diagram);
|
svgDraw.insertArrowCrossHead(diagram);
|
||||||
svgDraw.insertArrowFilledHead(diagram);
|
svgDraw.insertArrowFilledHead(diagram);
|
||||||
svgDraw.insertSequenceNumber(diagram);
|
svgDraw.insertSequenceNumber(diagram);
|
||||||
svgDraw.insertSolidTopArrowHead(diagram);
|
|
||||||
svgDraw.insertSolidBottomArrowHead(diagram);
|
|
||||||
svgDraw.insertStickTopArrowHead(diagram);
|
|
||||||
svgDraw.insertStickBottomArrowHead(diagram);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param msg - The message to draw.
|
* @param msg - The message to draw.
|
||||||
@@ -1014,12 +897,6 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
case diagObj.db.LINETYPE.ACTIVE_START:
|
case diagObj.db.LINETYPE.ACTIVE_START:
|
||||||
bounds.newActivation(msg, diagram, actors);
|
bounds.newActivation(msg, diagram, actors);
|
||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.CENTRAL_CONNECTION:
|
|
||||||
bounds.newActivation(msg, diagram, actors);
|
|
||||||
break;
|
|
||||||
case diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE:
|
|
||||||
bounds.newActivation(msg, diagram, actors);
|
|
||||||
break;
|
|
||||||
case diagObj.db.LINETYPE.ACTIVE_END:
|
case diagObj.db.LINETYPE.ACTIVE_END:
|
||||||
activeEnd(msg, bounds.getVerticalPos());
|
activeEnd(msg, bounds.getVerticalPos());
|
||||||
break;
|
break;
|
||||||
@@ -1178,7 +1055,7 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
createdActors,
|
createdActors,
|
||||||
destroyedActors
|
destroyedActors
|
||||||
);
|
);
|
||||||
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY, msg });
|
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY });
|
||||||
bounds.models.addMessage(msgModel);
|
bounds.models.addMessage(msgModel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('error while drawing message', e);
|
log.error('error while drawing message', e);
|
||||||
@@ -1191,27 +1068,6 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
diagObj.db.LINETYPE.SOLID_OPEN,
|
diagObj.db.LINETYPE.SOLID_OPEN,
|
||||||
diagObj.db.LINETYPE.DOTTED_OPEN,
|
diagObj.db.LINETYPE.DOTTED_OPEN,
|
||||||
diagObj.db.LINETYPE.SOLID,
|
diagObj.db.LINETYPE.SOLID,
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.DOTTED,
|
diagObj.db.LINETYPE.DOTTED,
|
||||||
diagObj.db.LINETYPE.SOLID_CROSS,
|
diagObj.db.LINETYPE.SOLID_CROSS,
|
||||||
diagObj.db.LINETYPE.DOTTED_CROSS,
|
diagObj.db.LINETYPE.DOTTED_CROSS,
|
||||||
@@ -1231,7 +1087,7 @@ export const draw = async function (_text: string, id: string, _version: string,
|
|||||||
await drawActors(diagram, actors, actorKeys, false);
|
await drawActors(diagram, actors, actorKeys, false);
|
||||||
|
|
||||||
for (const e of messagesToDraw) {
|
for (const e of messagesToDraw) {
|
||||||
await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj, e.msg);
|
await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj);
|
||||||
}
|
}
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
await drawActors(diagram, actors, actorKeys, true);
|
await drawActors(diagram, actors, actorKeys, true);
|
||||||
@@ -1605,85 +1461,12 @@ const buildNoteModel = async function (msg, actors, diagObj) {
|
|||||||
return noteModel;
|
return noteModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Central connection positioning constants
|
|
||||||
const CENTRAL_CONNECTION_BASE_OFFSET = 4;
|
|
||||||
const CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET = 6;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a message has central connection
|
|
||||||
* @param msg - The message object
|
|
||||||
* @param diagObj - The diagram object containing LINETYPE constants
|
|
||||||
* @returns True if the message has any type of central connection
|
|
||||||
*/
|
|
||||||
const hasCentralConnection = function (msg, diagObj) {
|
|
||||||
const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } =
|
|
||||||
diagObj.db.LINETYPE;
|
|
||||||
return [CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL].includes(
|
|
||||||
msg.centralConnection
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the positioning offset for central connection arrows
|
|
||||||
* @param msg - The message object
|
|
||||||
* @param diagObj - The diagram object containing LINETYPE constants
|
|
||||||
* @param isArrowToRight - Whether the arrow is pointing to the right
|
|
||||||
* @returns The offset to apply to startx position
|
|
||||||
*/
|
|
||||||
const calculateCentralConnectionOffset = function (msg, diagObj, isArrowToRight) {
|
|
||||||
const {
|
|
||||||
CENTRAL_CONNECTION_REVERSE,
|
|
||||||
CENTRAL_CONNECTION_DUAL,
|
|
||||||
BIDIRECTIONAL_SOLID,
|
|
||||||
BIDIRECTIONAL_DOTTED,
|
|
||||||
} = diagObj.db.LINETYPE;
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.centralConnection === CENTRAL_CONNECTION_REVERSE ||
|
|
||||||
msg.centralConnection === CENTRAL_CONNECTION_DUAL
|
|
||||||
) {
|
|
||||||
offset += CENTRAL_CONNECTION_BASE_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.centralConnection === CENTRAL_CONNECTION_DUAL &&
|
|
||||||
(msg.type === BIDIRECTIONAL_SOLID || msg.type === BIDIRECTIONAL_DOTTED)
|
|
||||||
) {
|
|
||||||
offset += isArrowToRight ? 0 : -CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildMessageModel = function (msg, actors, diagObj) {
|
const buildMessageModel = function (msg, actors, diagObj) {
|
||||||
if (
|
if (
|
||||||
![
|
![
|
||||||
diagObj.db.LINETYPE.SOLID_OPEN,
|
diagObj.db.LINETYPE.SOLID_OPEN,
|
||||||
diagObj.db.LINETYPE.DOTTED_OPEN,
|
diagObj.db.LINETYPE.DOTTED_OPEN,
|
||||||
diagObj.db.LINETYPE.SOLID,
|
diagObj.db.LINETYPE.SOLID,
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.DOTTED,
|
diagObj.db.LINETYPE.DOTTED,
|
||||||
diagObj.db.LINETYPE.SOLID_CROSS,
|
diagObj.db.LINETYPE.SOLID_CROSS,
|
||||||
diagObj.db.LINETYPE.DOTTED_CROSS,
|
diagObj.db.LINETYPE.DOTTED_CROSS,
|
||||||
@@ -1701,8 +1484,6 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
let startx = isArrowToRight ? fromRight : fromLeft;
|
let startx = isArrowToRight ? fromRight : fromLeft;
|
||||||
let stopx = isArrowToRight ? toLeft : toRight;
|
let stopx = isArrowToRight ? toLeft : toRight;
|
||||||
|
|
||||||
// Apply central connection positioning adjustments
|
|
||||||
startx += calculateCentralConnectionOffset(msg, diagObj, isArrowToRight);
|
|
||||||
// As the line width is considered, the left and right values will be off by 2.
|
// As the line width is considered, the left and right values will be off by 2.
|
||||||
const isArrowToActivation = Math.abs(toLeft - toRight) > 2;
|
const isArrowToActivation = Math.abs(toLeft - toRight) > 2;
|
||||||
|
|
||||||
@@ -1736,30 +1517,7 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
* Shorten the length of arrow at the end and move the marker forward (using refX) to have a clean arrowhead
|
* Shorten the length of arrow at the end and move the marker forward (using refX) to have a clean arrowhead
|
||||||
* This is not required for open arrows that don't have arrowheads
|
* This is not required for open arrows that don't have arrowheads
|
||||||
*/
|
*/
|
||||||
if (
|
if (![diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN].includes(msg.type)) {
|
||||||
![
|
|
||||||
diagObj.db.LINETYPE.SOLID_OPEN,
|
|
||||||
diagObj.db.LINETYPE.DOTTED_OPEN,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_TOP_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
].includes(msg.type)
|
|
||||||
) {
|
|
||||||
stopx += adjustValue(3);
|
stopx += adjustValue(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1767,14 +1525,9 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
* Shorten start position of bidirectional arrow to accommodate for second arrowhead
|
* Shorten start position of bidirectional arrow to accommodate for second arrowhead
|
||||||
*/
|
*/
|
||||||
if (
|
if (
|
||||||
[
|
[diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID, diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED].includes(
|
||||||
diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID,
|
msg.type
|
||||||
diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED,
|
)
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE,
|
|
||||||
diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE,
|
|
||||||
].includes(msg.type)
|
|
||||||
) {
|
) {
|
||||||
startx -= adjustValue(3);
|
startx -= adjustValue(3);
|
||||||
}
|
}
|
||||||
|
@@ -1709,77 +1709,6 @@ const _drawMenuItemTextCandidateFunc = (function () {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup arrow head and define the marker. The result is appended to the svg.
|
|
||||||
*
|
|
||||||
* @param elem
|
|
||||||
*/
|
|
||||||
export const insertSolidTopArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'solidTopArrowHead')
|
|
||||||
.attr('refX', 7.9)
|
|
||||||
.attr('refY', 7.25)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 0 L 10 8 L 0 8 z'); // this is actual shape for arrowhead
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertSolidBottomArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'solidBottomArrowHead')
|
|
||||||
.attr('refX', 7.9)
|
|
||||||
.attr('refY', 0.75)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 0 L 10 0 L 0 8 z');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertStickTopArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'stickTopArrowHead')
|
|
||||||
.attr('refX', 7.5)
|
|
||||||
.attr('refY', 7)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 0 L 7 7')
|
|
||||||
.attr('stroke', 'black')
|
|
||||||
.attr('stroke-width', 1.5)
|
|
||||||
.attr('fill', 'none');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertStickBottomArrowHead = function (elem) {
|
|
||||||
elem
|
|
||||||
.append('defs')
|
|
||||||
.append('marker')
|
|
||||||
.attr('id', 'stickBottomArrowHead')
|
|
||||||
.attr('refX', 7.5)
|
|
||||||
.attr('refY', 0)
|
|
||||||
.attr('markerUnits', 'userSpaceOnUse')
|
|
||||||
.attr('markerWidth', 12)
|
|
||||||
.attr('markerHeight', 12)
|
|
||||||
.attr('orient', 'auto-start-reverse')
|
|
||||||
.append('path')
|
|
||||||
.attr('d', 'M 0 7 L 7 0')
|
|
||||||
.attr('stroke', 'black')
|
|
||||||
.attr('stroke-width', 1.5)
|
|
||||||
.attr('fill', 'none');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
drawRect,
|
drawRect,
|
||||||
drawText,
|
drawText,
|
||||||
@@ -1802,8 +1731,4 @@ export default {
|
|||||||
getNoteRect,
|
getNoteRect,
|
||||||
fixLifeLineHeights,
|
fixLifeLineHeights,
|
||||||
sanitizeUrl,
|
sanitizeUrl,
|
||||||
insertSolidTopArrowHead,
|
|
||||||
insertSolidBottomArrowHead,
|
|
||||||
insertStickTopArrowHead,
|
|
||||||
insertStickBottomArrowHead,
|
|
||||||
};
|
};
|
||||||
|
@@ -35,7 +35,6 @@ export interface Message {
|
|||||||
type?: number;
|
type?: number;
|
||||||
activate?: boolean;
|
activate?: boolean;
|
||||||
placement?: string;
|
placement?: string;
|
||||||
centralConnection?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddMessageParams {
|
export interface AddMessageParams {
|
||||||
@@ -51,8 +50,6 @@ export interface AddMessageParams {
|
|||||||
| 'destroyParticipant'
|
| 'destroyParticipant'
|
||||||
| 'activeStart'
|
| 'activeStart'
|
||||||
| 'activeEnd'
|
| 'activeEnd'
|
||||||
| 'centralConnection'
|
|
||||||
| 'centralConnectionReverse'
|
|
||||||
| 'addNote'
|
| 'addNote'
|
||||||
| 'addLinks'
|
| 'addLinks'
|
||||||
| 'addALink'
|
| 'addALink'
|
||||||
|
@@ -17,7 +17,6 @@ While directives allow you to change most of the default configuration settings,
|
|||||||
Mermaid basically supports two types of configuration options to be overridden by directives.
|
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,7 +23,6 @@ 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,13 +33,11 @@ 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.4.38"
|
"vue": "^3.5.21"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/carbon": "^1.1.37",
|
"@iconify-json/carbon": "^1.2.13",
|
||||||
"@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.0",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"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.4.2",
|
"unocss": "^66.4.2",
|
||||||
"unplugin-vue-components": "^28.4.0",
|
"unplugin-vue-components": "^28.4.1",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
"vite-plugin-pwa": "^1.0.0",
|
"vite-plugin-pwa": "^1.0.3",
|
||||||
"vitepress": "1.6.3",
|
"vitepress": "1.6.4",
|
||||||
"workbox-window": "^7.3.0"
|
"workbox-window": "^7.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -83,7 +83,6 @@ 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)
|
||||||
@@ -97,7 +96,6 @@ 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
|
||||||
@@ -107,7 +105,6 @@ 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
|
||||||
@@ -116,18 +113,15 @@ 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
|
||||||
|
@@ -216,11 +216,7 @@ Messages can be of two displayed either solid or with a dotted line.
|
|||||||
[Actor][Arrow][Actor]:Message text
|
[Actor][Arrow][Actor]:Message text
|
||||||
```
|
```
|
||||||
|
|
||||||
Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows.
|
There are ten types of arrows currently supported:
|
||||||
|
|
||||||
#### Supported Arrow Types
|
|
||||||
|
|
||||||
**Standard Arrow Types**
|
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| -------- | ---------------------------------------------------- |
|
| -------- | ---------------------------------------------------- |
|
||||||
@@ -235,49 +231,6 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros
|
|||||||
| `-)` | Solid line with an open arrow at the end (async) |
|
| `-)` | Solid line with an open arrow at the end (async) |
|
||||||
| `--)` | Dotted line with a open arrow at the end (async) |
|
| `--)` | Dotted line with a open arrow at the end (async) |
|
||||||
|
|
||||||
**Half-Arrows (v<MERMAID_RELEASE_VERSION>+)**
|
|
||||||
|
|
||||||
The following half-arrow types are supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
| ------- | ---------------------------------------------------- |
|
|
||||||
| `-\|\` | Solid line with top half arrowhead |
|
|
||||||
| `--\|\` | Dotted line with top half arrowhead |
|
|
||||||
| `-\|/` | Solid line with bottom half arrowhead |
|
|
||||||
| `--\|/` | Dotted line with bottom half arrowhead |
|
|
||||||
| `/\|-` | Solid line with reverse top half arrowhead |
|
|
||||||
| `/\|--` | Dotted line with reverse top half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom half arrowhead |
|
|
||||||
| `-\\` | Solid line with top stick half arrowhead |
|
|
||||||
| `--\\` | Dotted line with top stick half arrowhead |
|
|
||||||
| `-//` | Solid line with bottom stick half arrowhead |
|
|
||||||
| `--//` | Dotted line with bottom stick half arrowhead |
|
|
||||||
| `//-` | Solid line with reverse top stick half arrowhead |
|
|
||||||
| `//--` | Dotted line with reverse top stick half arrowhead |
|
|
||||||
| `\\-` | Solid line with reverse bottom stick half arrowhead |
|
|
||||||
| `\\--` | Dotted line with reverse bottom stick half arrowhead |
|
|
||||||
|
|
||||||
## Central Connections (v<MERMAID_RELEASE_VERSION>+)
|
|
||||||
|
|
||||||
Mermaid sequence diagrams support **central lifeline connections** using a `()`.
|
|
||||||
This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another.
|
|
||||||
|
|
||||||
To indicate a central connection, append `()` to the arrow syntax.
|
|
||||||
|
|
||||||
#### Basic Syntax
|
|
||||||
|
|
||||||
```mermaid-example
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant John
|
|
||||||
Alice->>()John: Hello John
|
|
||||||
Alice()->>John: How are you?
|
|
||||||
John()->>()Alice: Great!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Activations
|
## Activations
|
||||||
|
|
||||||
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:
|
||||||
|
@@ -207,7 +207,7 @@ describe('when using mermaid and ', () => {
|
|||||||
[Error: Parse error on line 2:
|
[Error: Parse error on line 2:
|
||||||
...equenceDiagramAlice:->Bob: Hello Bob, h...
|
...equenceDiagramAlice:->Bob: Hello Bob, h...
|
||||||
----------------------^
|
----------------------^
|
||||||
Expecting '()', 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'SOLID_ARROW_TOP', 'SOLID_ARROW_BOTTOM', 'STICK_ARROW_TOP', 'STICK_ARROW_BOTTOM', 'SOLID_ARROW_TOP_DOTTED', 'SOLID_ARROW_BOTTOM_DOTTED', 'STICK_ARROW_TOP_DOTTED', 'STICK_ARROW_BOTTOM_DOTTED', 'SOLID_ARROW_TOP_REVERSE', 'SOLID_ARROW_BOTTOM_REVERSE', 'STICK_ARROW_TOP_REVERSE', 'STICK_ARROW_BOTTOM_REVERSE', 'SOLID_ARROW_TOP_REVERSE_DOTTED', 'SOLID_ARROW_BOTTOM_REVERSE_DOTTED', 'STICK_ARROW_TOP_REVERSE_DOTTED', 'STICK_ARROW_BOTTOM_REVERSE_DOTTED', 'BIDIRECTIONAL_SOLID_ARROW', 'DOTTED_ARROW', 'BIDIRECTIONAL_DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT']
|
Expecting 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'BIDIRECTIONAL_SOLID_ARROW', 'DOTTED_ARROW', 'BIDIRECTIONAL_DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT']
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -4,9 +4,6 @@ import { internalHelpers } from '../internals.js';
|
|||||||
import { log } from '../logger.js';
|
import { log } from '../logger.js';
|
||||||
import type { LayoutData } from './types.js';
|
import type { LayoutData } from './types.js';
|
||||||
|
|
||||||
// console.log('MUST be removed, this only for keeping dev server working');
|
|
||||||
// import tmp from './layout-algorithms/dagre/index.js';
|
|
||||||
|
|
||||||
export interface RenderOptions {
|
export interface RenderOptions {
|
||||||
algorithm?: string;
|
algorithm?: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import { evaluate } from '../../diagrams/common/common.js';
|
import { evaluate, getUrl } from '../../diagrams/common/common.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import { createText } from '../createText.js';
|
import { createText } from '../createText.js';
|
||||||
import utils from '../../utils.js';
|
import utils from '../../utils.js';
|
||||||
import {
|
import { getLineFunctionsWithOffset } from '../../utils/lineWithOffset.js';
|
||||||
getLineFunctionsWithOffset,
|
|
||||||
markerOffsets,
|
|
||||||
markerOffsets2,
|
|
||||||
} from '../../utils/lineWithOffset.js';
|
|
||||||
import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js';
|
import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -31,8 +27,8 @@ import createLabel from './createLabel.js';
|
|||||||
import { addEdgeMarkers } from './edgeMarker.ts';
|
import { addEdgeMarkers } from './edgeMarker.ts';
|
||||||
import { isLabelStyle, styles2String } from './shapes/handDrawnShapeStyles.js';
|
import { isLabelStyle, styles2String } from './shapes/handDrawnShapeStyles.js';
|
||||||
|
|
||||||
export const edgeLabels = new Map();
|
const edgeLabels = new Map();
|
||||||
export const terminalLabels = new Map();
|
const terminalLabels = new Map();
|
||||||
|
|
||||||
export const clear = () => {
|
export const clear = () => {
|
||||||
edgeLabels.clear();
|
edgeLabels.clear();
|
||||||
@@ -61,7 +57,7 @@ export const insertEdgeLabel = async (elem, edge) => {
|
|||||||
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
|
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
|
||||||
|
|
||||||
// Create inner g, label, this will be positioned now for centering the text
|
// Create inner g, label, this will be positioned now for centering the text
|
||||||
const label = edgeLabel.insert('g').attr('class', 'label').attr('data-id', edge.id);
|
const label = edgeLabel.insert('g').attr('class', 'label');
|
||||||
label.node().appendChild(labelElement);
|
label.node().appendChild(labelElement);
|
||||||
|
|
||||||
// Center the label
|
// Center the label
|
||||||
@@ -444,33 +440,7 @@ const fixCorners = function (lineData) {
|
|||||||
}
|
}
|
||||||
return newLineData;
|
return newLineData;
|
||||||
};
|
};
|
||||||
const generateDashArray = (len, oValueS, oValueE) => {
|
export const insertEdge = function (elem, edge, clusterDb, diagramType, startNode, endNode, id) {
|
||||||
const middleLength = len - oValueS - oValueE;
|
|
||||||
const dashLength = 2; // Length of each dash
|
|
||||||
const gapLength = 2; // Length of each gap
|
|
||||||
const dashGapPairLength = dashLength + gapLength;
|
|
||||||
|
|
||||||
// Calculate number of complete dash-gap pairs that can fit
|
|
||||||
const numberOfPairs = Math.floor(middleLength / dashGapPairLength);
|
|
||||||
|
|
||||||
// Generate the middle pattern array
|
|
||||||
const middlePattern = Array(numberOfPairs).fill(`${dashLength} ${gapLength}`).join(' ');
|
|
||||||
|
|
||||||
// Combine all parts
|
|
||||||
const dashArray = `0 ${oValueS} ${middlePattern} ${oValueE}`;
|
|
||||||
|
|
||||||
return dashArray;
|
|
||||||
};
|
|
||||||
export const insertEdge = function (
|
|
||||||
elem,
|
|
||||||
edge,
|
|
||||||
clusterDb,
|
|
||||||
diagramType,
|
|
||||||
startNode,
|
|
||||||
endNode,
|
|
||||||
id,
|
|
||||||
skipIntersect = false
|
|
||||||
) {
|
|
||||||
const { handDrawnSeed } = getConfig();
|
const { handDrawnSeed } = getConfig();
|
||||||
let points = edge.points;
|
let points = edge.points;
|
||||||
let pointsHasChanged = false;
|
let pointsHasChanged = false;
|
||||||
@@ -484,12 +454,11 @@ export const insertEdge = function (
|
|||||||
edgeClassStyles.push(edge.cssCompiledStyles[key]);
|
edgeClassStyles.push(edge.cssCompiledStyles[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug('UIO intersect check', edge.points, head.x, tail.x);
|
if (head.intersect && tail.intersect) {
|
||||||
if (head.intersect && tail.intersect && !skipIntersect) {
|
|
||||||
points = points.slice(1, edge.points.length - 1);
|
points = points.slice(1, edge.points.length - 1);
|
||||||
points.unshift(tail.intersect(points[0]));
|
points.unshift(tail.intersect(points[0]));
|
||||||
log.debug(
|
log.debug(
|
||||||
'Last point UIO',
|
'Last point APA12',
|
||||||
edge.start,
|
edge.start,
|
||||||
'-->',
|
'-->',
|
||||||
edge.end,
|
edge.end,
|
||||||
@@ -499,7 +468,6 @@ export const insertEdge = function (
|
|||||||
);
|
);
|
||||||
points.push(head.intersect(points[points.length - 1]));
|
points.push(head.intersect(points[points.length - 1]));
|
||||||
}
|
}
|
||||||
const pointsStr = btoa(JSON.stringify(points));
|
|
||||||
if (edge.toCluster) {
|
if (edge.toCluster) {
|
||||||
log.info('to cluster abc88', clusterDb.get(edge.toCluster));
|
log.info('to cluster abc88', clusterDb.get(edge.toCluster));
|
||||||
points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node);
|
points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node);
|
||||||
@@ -563,10 +531,6 @@ export const insertEdge = function (
|
|||||||
curve = curveBasis;
|
curve = curveBasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (edge.curve) {
|
|
||||||
// curve = edge.curve;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const { x, y } = getLineFunctionsWithOffset(edge);
|
const { x, y } = getLineFunctionsWithOffset(edge);
|
||||||
const lineFunction = line().x(x).y(y).curve(curve);
|
const lineFunction = line().x(x).y(y).curve(curve);
|
||||||
|
|
||||||
@@ -598,14 +562,10 @@ export const insertEdge = function (
|
|||||||
strokeClasses += ' edge-pattern-solid';
|
strokeClasses += ' edge-pattern-solid';
|
||||||
}
|
}
|
||||||
let svgPath;
|
let svgPath;
|
||||||
let linePath =
|
let linePath = lineFunction(lineData);
|
||||||
edge.curve === 'rounded'
|
const edgeStyles = Array.isArray(edge.style) ? edge.style : edge.style ? [edge.style] : [];
|
||||||
? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 5)
|
|
||||||
: lineFunction(lineData);
|
|
||||||
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
|
||||||
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
|
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
|
||||||
|
|
||||||
let animatedEdge = false;
|
|
||||||
if (edge.look === 'handDrawn') {
|
if (edge.look === 'handDrawn') {
|
||||||
const rc = rough.svg(elem);
|
const rc = rough.svg(elem);
|
||||||
Object.assign([], lineData);
|
Object.assign([], lineData);
|
||||||
@@ -636,10 +596,7 @@ export const insertEdge = function (
|
|||||||
animationClass = ' edge-animation-' + edge.animation;
|
animationClass = ' edge-animation-' + edge.animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathStyle =
|
const pathStyle = stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles;
|
||||||
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
|
|
||||||
';' +
|
|
||||||
(edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
|
||||||
svgPath = elem
|
svgPath = elem
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('d', linePath)
|
.attr('d', linePath)
|
||||||
@@ -649,39 +606,11 @@ export const insertEdge = function (
|
|||||||
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '')
|
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '')
|
||||||
)
|
)
|
||||||
.attr('style', pathStyle);
|
.attr('style', pathStyle);
|
||||||
|
|
||||||
//eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
|
|
||||||
strokeColor = pathStyle.match(/stroke:([^;]+)/)?.[1];
|
strokeColor = pathStyle.match(/stroke:([^;]+)/)?.[1];
|
||||||
|
|
||||||
// Possible fix to remove eslint-disable-next-line
|
|
||||||
//strokeColor = /stroke:([^;]+)/.exec(pathStyle)?.[1];
|
|
||||||
|
|
||||||
animatedEdge =
|
|
||||||
edge.animate === true || !!edge.animation || stylesFromClasses.includes('animation');
|
|
||||||
const pathNode = svgPath.node();
|
|
||||||
const len = typeof pathNode.getTotalLength === 'function' ? pathNode.getTotalLength() : 0;
|
|
||||||
const oValueS = markerOffsets2[edge.arrowTypeStart] || 0;
|
|
||||||
const oValueE = markerOffsets2[edge.arrowTypeEnd] || 0;
|
|
||||||
|
|
||||||
if (edge.look === 'neo' && !animatedEdge) {
|
|
||||||
const dashArray =
|
|
||||||
edge.pattern === 'dotted' || edge.pattern === 'dashed'
|
|
||||||
? generateDashArray(len, oValueS, oValueE)
|
|
||||||
: `0 ${oValueS} ${len - oValueS - oValueE} ${oValueE}`;
|
|
||||||
|
|
||||||
// No offset needed because we already start with a zero-length dash that effectively sets us up for a gap at the start.
|
|
||||||
const mOffset = `stroke-dasharray: ${dashArray}; stroke-dashoffset: 0;`;
|
|
||||||
svgPath.attr('style', mOffset + svgPath.attr('style'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MC Special
|
// DEBUG code, DO NOT REMOVE
|
||||||
svgPath.attr('data-edge', true);
|
// adds a red circle at each edge coordinate
|
||||||
svgPath.attr('data-et', 'edge');
|
|
||||||
svgPath.attr('data-id', edge.id);
|
|
||||||
svgPath.attr('data-points', pointsStr);
|
|
||||||
|
|
||||||
// DEBUG code, adds a red circle at each edge coordinate
|
|
||||||
// cornerPoints.forEach((point) => {
|
// cornerPoints.forEach((point) => {
|
||||||
// elem
|
// elem
|
||||||
// .append('circle')
|
// .append('circle')
|
||||||
@@ -691,27 +620,19 @@ export const insertEdge = function (
|
|||||||
// .attr('cx', point.x)
|
// .attr('cx', point.x)
|
||||||
// .attr('cy', point.y);
|
// .attr('cy', point.y);
|
||||||
// });
|
// });
|
||||||
if (edge.showPoints) {
|
// lineData.forEach((point) => {
|
||||||
lineData.forEach((point) => {
|
// elem
|
||||||
elem
|
// .append('circle')
|
||||||
.append('circle')
|
// .style('stroke', 'red')
|
||||||
.style('stroke', 'red')
|
// .style('fill', 'red')
|
||||||
.style('fill', 'red')
|
// .attr('r', 1)
|
||||||
.attr('r', 1)
|
// .attr('cx', point.x)
|
||||||
.attr('cx', point.x)
|
// .attr('cy', point.y);
|
||||||
.attr('cy', point.y);
|
// });
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = '';
|
let url = '';
|
||||||
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
|
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
|
||||||
url =
|
url = getUrl(true);
|
||||||
window.location.protocol +
|
|
||||||
'//' +
|
|
||||||
window.location.host +
|
|
||||||
window.location.pathname +
|
|
||||||
window.location.search;
|
|
||||||
url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
|
||||||
}
|
}
|
||||||
log.info('arrowTypeStart', edge.arrowTypeStart);
|
log.info('arrowTypeStart', edge.arrowTypeStart);
|
||||||
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
||||||
@@ -730,134 +651,3 @@ export const insertEdge = function (
|
|||||||
paths.originalPath = edge.points;
|
paths.originalPath = edge.points;
|
||||||
return paths;
|
return paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates SVG path data with rounded corners from an array of points.
|
|
||||||
* @param {Array} points - Array of points in the format [{x: Number, y: Number}, ...]
|
|
||||||
* @param {Number} radius - The radius of the rounded corners
|
|
||||||
* @returns {String} - SVG path data string
|
|
||||||
*/
|
|
||||||
function generateRoundedPath(points, radius) {
|
|
||||||
if (points.length < 2) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = '';
|
|
||||||
const size = points.length;
|
|
||||||
const epsilon = 1e-5;
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
const currPoint = points[i];
|
|
||||||
const prevPoint = points[i - 1];
|
|
||||||
const nextPoint = points[i + 1];
|
|
||||||
|
|
||||||
if (i === 0) {
|
|
||||||
// Move to the first point
|
|
||||||
path += `M${currPoint.x},${currPoint.y}`;
|
|
||||||
} else if (i === size - 1) {
|
|
||||||
// Last point, draw a straight line to the final point
|
|
||||||
path += `L${currPoint.x},${currPoint.y}`;
|
|
||||||
} else {
|
|
||||||
// Calculate vectors for incoming and outgoing segments
|
|
||||||
const dx1 = currPoint.x - prevPoint.x;
|
|
||||||
const dy1 = currPoint.y - prevPoint.y;
|
|
||||||
const dx2 = nextPoint.x - currPoint.x;
|
|
||||||
const dy2 = nextPoint.y - currPoint.y;
|
|
||||||
|
|
||||||
const len1 = Math.hypot(dx1, dy1);
|
|
||||||
const len2 = Math.hypot(dx2, dy2);
|
|
||||||
|
|
||||||
// Prevent division by zero
|
|
||||||
if (len1 < epsilon || len2 < epsilon) {
|
|
||||||
path += `L${currPoint.x},${currPoint.y}`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the vectors
|
|
||||||
const nx1 = dx1 / len1;
|
|
||||||
const ny1 = dy1 / len1;
|
|
||||||
const nx2 = dx2 / len2;
|
|
||||||
const ny2 = dy2 / len2;
|
|
||||||
|
|
||||||
// Calculate the angle between the vectors
|
|
||||||
const dot = nx1 * nx2 + ny1 * ny2;
|
|
||||||
// Clamp the dot product to avoid numerical issues with acos
|
|
||||||
const clampedDot = Math.max(-1, Math.min(1, dot));
|
|
||||||
const angle = Math.acos(clampedDot);
|
|
||||||
|
|
||||||
// Skip rounding if the angle is too small or too close to 180 degrees
|
|
||||||
if (angle < epsilon || Math.abs(Math.PI - angle) < epsilon) {
|
|
||||||
path += `L${currPoint.x},${currPoint.y}`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the distance to offset the control point
|
|
||||||
const cutLen = Math.min(radius / Math.sin(angle / 2), len1 / 2, len2 / 2);
|
|
||||||
|
|
||||||
// Calculate the start and end points of the curve
|
|
||||||
const startX = currPoint.x - nx1 * cutLen;
|
|
||||||
const startY = currPoint.y - ny1 * cutLen;
|
|
||||||
const endX = currPoint.x + nx2 * cutLen;
|
|
||||||
const endY = currPoint.y + ny2 * cutLen;
|
|
||||||
|
|
||||||
// Draw the line to the start of the curve
|
|
||||||
path += `L${startX},${startY}`;
|
|
||||||
|
|
||||||
// Draw the quadratic Bezier curve
|
|
||||||
path += `Q${currPoint.x},${currPoint.y} ${endX},${endY}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
// Helper function to calculate delta and angle between two points
|
|
||||||
function calculateDeltaAndAngle(point1, point2) {
|
|
||||||
if (!point1 || !point2) {
|
|
||||||
return { angle: 0, deltaX: 0, deltaY: 0 };
|
|
||||||
}
|
|
||||||
const deltaX = point2.x - point1.x;
|
|
||||||
const deltaY = point2.y - point1.y;
|
|
||||||
const angle = Math.atan2(deltaY, deltaX);
|
|
||||||
return { angle, deltaX, deltaY };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to adjust the first and last points of the points array
|
|
||||||
function applyMarkerOffsetsToPoints(points, edge) {
|
|
||||||
// Copy the points array to avoid mutating the original data
|
|
||||||
const newPoints = points.map((point) => ({ ...point }));
|
|
||||||
|
|
||||||
// Handle the first point (start of the edge)
|
|
||||||
if (points.length >= 2 && markerOffsets[edge.arrowTypeStart]) {
|
|
||||||
const offsetValue = markerOffsets[edge.arrowTypeStart];
|
|
||||||
|
|
||||||
const point1 = points[0];
|
|
||||||
const point2 = points[1];
|
|
||||||
|
|
||||||
const { angle } = calculateDeltaAndAngle(point1, point2);
|
|
||||||
|
|
||||||
const offsetX = offsetValue * Math.cos(angle);
|
|
||||||
const offsetY = offsetValue * Math.sin(angle);
|
|
||||||
|
|
||||||
newPoints[0].x = point1.x + offsetX;
|
|
||||||
newPoints[0].y = point1.y + offsetY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the last point (end of the edge)
|
|
||||||
const n = points.length;
|
|
||||||
if (n >= 2 && markerOffsets[edge.arrowTypeEnd]) {
|
|
||||||
const offsetValue = markerOffsets[edge.arrowTypeEnd];
|
|
||||||
|
|
||||||
const point1 = points[n - 1];
|
|
||||||
const point2 = points[n - 2];
|
|
||||||
|
|
||||||
const { angle } = calculateDeltaAndAngle(point2, point1);
|
|
||||||
|
|
||||||
const offsetX = offsetValue * Math.cos(angle);
|
|
||||||
const offsetY = offsetValue * Math.sin(angle);
|
|
||||||
|
|
||||||
newPoints[n - 1].x = point1.x - offsetX;
|
|
||||||
newPoints[n - 1].y = point1.y - offsetY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPoints;
|
|
||||||
}
|
|
||||||
|
@@ -4,22 +4,12 @@ import type { EdgeData, Point } from '../types.js';
|
|||||||
// under any transparent markers.
|
// under any transparent markers.
|
||||||
// The offsets are calculated from the markers' dimensions.
|
// The offsets are calculated from the markers' dimensions.
|
||||||
export const markerOffsets = {
|
export const markerOffsets = {
|
||||||
aggregation: 17.25,
|
aggregation: 18,
|
||||||
extension: 17.25,
|
extension: 18,
|
||||||
composition: 17.25,
|
composition: 18,
|
||||||
dependency: 6,
|
dependency: 6,
|
||||||
lollipop: 13.5,
|
lollipop: 13.5,
|
||||||
arrow_point: 4,
|
arrow_point: 4,
|
||||||
//arrow_cross: 24,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// We need to draw the lines a bit shorter to avoid drawing
|
|
||||||
// under any transparent markers.
|
|
||||||
// The offsets are calculated from the markers' dimensions.
|
|
||||||
export const markerOffsets2 = {
|
|
||||||
arrow_point: 9,
|
|
||||||
arrow_cross: 12.5,
|
|
||||||
arrow_circle: 12.5,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +104,6 @@ export const getLineFunctionsWithOffset = (
|
|||||||
adjustment *= DIRECTION === 'right' ? -1 : 1;
|
adjustment *= DIRECTION === 'right' ? -1 : 1;
|
||||||
offset += adjustment;
|
offset += adjustment;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pointTransformer(d).x + offset;
|
return pointTransformer(d).x + offset;
|
||||||
},
|
},
|
||||||
y: function (
|
y: function (
|
||||||
|
@@ -44,18 +44,15 @@ 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`?
|
||||||
|
@@ -154,7 +154,6 @@
|
|||||||
### 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 ...)
|
||||||
@@ -173,14 +172,12 @@
|
|||||||
### 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
|
||||||
|
|
||||||
|
3458
pnpm-lock.yaml
generated
3458
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user