Compare commits

..

10 Commits

Author SHA1 Message Date
omkarht
4c40dd406d Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 6623-add-image-icons-support-to-sequence-diagram 2025-09-16 15:03:02 +05:30
omkarht
8dcddaf314 fix: refactored image and icon test case files
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-25 15:34:30 +05:30
omkarht
bd19523f5d Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 6623-add-image-icons-support-to-sequence-diagram 2025-08-25 15:21:24 +05:30
omkarht
6b64b92b6e docs: add documentation
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-14 12:58:24 +05:30
omkarht
41e3d2449a test: add test cases for image and icon support
Some optional description over here if you need to add more info

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-14 12:49:43 +05:30
omkarht
0386ed6b32 fix: refactored code
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-14 12:40:37 +05:30
omkarht
97481b4d11 Merge branch '6623-add-image-icons-support-to-sequence-diagram' of https://github.com/mermaid-js/mermaid into 6623-add-image-icons-support-to-sequence-diagram 2025-08-13 14:12:18 +05:30
omkarht
b8b120939e chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-13 14:11:54 +05:30
autofix-ci[bot]
71e4d62153 [autofix.ci] apply automated fixes 2025-08-13 07:57:40 +00:00
omkarht
b867370f1b 6623: add image and icon support for sequence diagrams
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-13 13:21:06 +05:30
147 changed files with 7161 additions and 8370 deletions

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Prevent HTML tags from being escaped in sandbox label rendering

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Support edge animation in hand drawn look

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
feat: Add custom images and icons support for sequence diagram actors

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Resolved parsing error where direction TD was not recognized within subgraphs

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix(treemap): Fixed treemap classDef style application to properly apply user-defined styles

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Correct viewBox casing and make SVGs responsive

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Render newlines as spaces in class diagrams

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Handle arrows correctly when auto number is enabled

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Improve participant parsing and prevent recursive loops on invalid syntax

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.

View File

@@ -0,0 +1,7 @@
---
'mermaid': minor
'@mermaid-js/layout-tidy-tree': minor
'@mermaid-js/layout-elk': minor
---
feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
feat: add alias support for new participant syntax of sequence diagrams

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Add half-arrowheads (solid & stick) and central connection support

View File

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

View File

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

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: allow to put notes in namespaces on classDiagram

View File

@@ -1,5 +0,0 @@
---
'@mermaid': patch
---
fix: Mindmap breaking in ELK layout

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Support ComponentQueue_Ext to prevent parsing error

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: validate dates and tick interval to prevent UI freeze/crash in gantt diagramtype

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Mindmap rendering issue when the number of Level 2 nodes exceeds 11

View File

@@ -1,5 +1,3 @@
!viewbox
# It should be viewBox
# This file contains coding related terms
ALPHANUM
antiscript

View File

@@ -64,7 +64,6 @@ rscratch
shiki
Slidev
sparkline
speccharts
sphinxcontrib
ssim
stylis

View File

@@ -71,9 +71,6 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const outFileName = getFileName(name, options);
const { dependencies, version } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
const output: BuildOptions = buildOptions({
...rest,
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
@@ -85,13 +82,15 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
'injected.version': `'${version}'`,
includeLargeFeatures: `${includeLargeFeatures}`,
'import.meta.vitest': 'undefined',
},
});
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies

View File

@@ -26,8 +26,8 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ['javascript', 'actions']
# CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
language: ['javascript']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
@@ -36,7 +36,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
with:
config-file: ./.github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
@@ -48,7 +48,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -62,4 +62,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10

View File

@@ -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"
- name: Cypress run
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
id: cypress
with:
start: pnpm run dev

View File

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

View File

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

View File

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

View File

@@ -89,7 +89,7 @@ jobs:
continue-on-error: ${{ github.event_name == 'push' }}
run: pnpm run docs:verify
- uses: testomatio/check-tests@8d7e741fd2c9e46c8e8a3b27207731b0658e0fbe # stable
- uses: testomatio/check-tests@0ea638fcec1820cf2e7b9854fdbdd04128a55bd4 # stable
with:
framework: cypress
tests: './cypress/e2e/**/**.spec.js'

View File

@@ -36,10 +36,11 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
with:
version: pnpm changeset:version
publish: pnpm changeset:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
name: Validate pnpm-lock.yaml
on:
pull_request_target:
pull_request:
paths:
- 'pnpm-lock.yaml'
- '**/package.json'
@@ -15,8 +15,13 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Validate pnpm-lock.yaml entries
id: validate # give this step an ID so we can reference its outputs
@@ -50,41 +55,16 @@ jobs:
exit 1
fi
- name: Find existing lockfile validation comment
if: always()
uses: peter-evans/find-comment@v3
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: 'Lockfile Validation Failed'
- name: Comment on PR if validation failed
if: failure()
uses: peter-evans/create-or-update-comment@v5
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
edit-mode: replace
body: |
❌ **Lockfile Validation Failed**
The following issue(s) were detected:
${{ steps.validate.outputs.errors }}
Please address these and push an update.
_Posted automatically by GitHub Actions_
- name: Delete comment if validation passed
if: success() && steps.find-comment.outputs.comment-id != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.find-comment.outputs.comment-id }},
});

View File

@@ -78,8 +78,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
},
define: {
'import.meta.vitest': 'undefined',
'injected.includeLargeFeatures': 'true',
'injected.version': `'0.0.0'`,
},
resolve: {
extensions: [],
@@ -96,6 +94,10 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
}),
...visualizerOptions(packageName, core),
],
define: {
// Needs to be string
includeLargeFeatures: 'true',
},
};
if (watch && config.build) {

View File

@@ -5,7 +5,7 @@ USER 0:0
RUN corepack enable \
&& corepack enable pnpm
RUN apk add --no-cache git~=2.43 \
RUN apk add --no-cache git~=2.43.4 \
&& git config --add --system safe.directory /mermaid
ENV NODE_OPTIONS="--max_old_space_size=8192"

View File

@@ -6,7 +6,6 @@ interface CypressConfig {
listUrl?: boolean;
listId?: string;
name?: string;
screenshot?: boolean;
}
type CypressMermaidConfig = MermaidConfig & CypressConfig;
@@ -91,33 +90,20 @@ export const renderGraph = (
export const openURLAndVerifyRendering = (
url: string,
{ screenshot = true, ...options }: CypressMermaidConfig,
options: CypressMermaidConfig,
validation?: any
): void => {
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
// Handle sandbox mode where SVG is inside an iframe
if (options.securityLevel === 'sandbox') {
cy.get('iframe').should('be.visible');
if (validation) {
cy.get('iframe').should(validation);
}
} else {
cy.get('svg').should('be.visible');
// cspell:ignore viewbox
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) {
cy.get('svg').should(validation);
}
if (validation) {
cy.get('svg').should(validation);
}
if (screenshot) {
verifyScreenshot(name);
}
verifyScreenshot(name);
};
export const verifyScreenshot = (name: string): void => {

View File

@@ -114,28 +114,4 @@ describe('C4 diagram', () => {
{}
);
});
it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
imgSnapshotTest(
`
C4Context
title System Context diagram with ComponentQueue_Ext
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, msgQueue, "Sends messages to")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
`,
{}
);
});
});

View File

@@ -562,20 +562,6 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`

View File

@@ -709,20 +709,6 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`

View File

@@ -369,92 +369,4 @@ ORDER ||--|{ LINE-ITEM : contains
);
});
});
describe('Special characters and numbers syntax', () => {
it('should render ER diagram with numeric entity names', () => {
imgSnapshotTest(
`
erDiagram
1 ||--|| ORDER : places
ORDER ||--|{ 2 : contains
2 ||--o{ 3.5 : references
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with "u" character in entity names and cardinality', () => {
imgSnapshotTest(
`
erDiagram
CUSTOMER ||--|| u : has
u ||--|| ORDER : places
PROJECT u--o{ TEAM_MEMBER : "parent"
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with decimal numbers in relationships', () => {
imgSnapshotTest(
`
erDiagram
2.5 ||--|| 1.5 : has
CUSTOMER ||--o{ 3.14 : references
1.0 ||--|{ ORDER : contains
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with numeric entity names and attributes', () => {
imgSnapshotTest(
`
erDiagram
1 {
string name
int value
}
1 ||--|| ORDER : places
ORDER {
float price
string description
}
`,
{ logLevel: 1 }
);
});
it('should render complex ER diagram with mixed special entity names', () => {
imgSnapshotTest(
`
erDiagram
CUSTOMER ||--o{ 1 : places
1 ||--|{ u : contains
1.5
u ||--|| 2.5 : processes
2.5 {
string id
float value
}
u {
varchar(50) name
int count
}
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with standalone numeric entities', () => {
imgSnapshotTest(
`erDiagram
PRODUCT ||--o{ ORDER-ITEM : has
1.5
u
1
`,
{ logLevel: 1 }
);
});
});
});

View File

@@ -1029,19 +1029,4 @@ graph TD
}
);
});
it('FDH49: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ look: 'handDrawn', screenshot: false }
);
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
});

View File

@@ -79,18 +79,6 @@ describe('Flowchart v2', () => {
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('6a: should render complex HTML in labels with sandbox security', () => {
imgSnapshotTest(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
);
});
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD

View File

@@ -774,21 +774,6 @@ describe('Graph', () => {
expect(svg).to.not.have.attr('style');
});
});
it('40: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ screenshot: false }
);
// Verify animation classes are applied to both edges
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
it('58: handle styling with style expressions', () => {
imgSnapshotTest(
`
@@ -988,19 +973,4 @@ graph TD
}
);
});
it('70: should render a subgraph with direction TD', () => {
imgSnapshotTest(
`
flowchart LR
subgraph A
direction TD
a --> b
end
`,
{
fontFamily: 'courier',
}
);
});
});

View File

@@ -803,64 +803,4 @@ describe('Gantt diagram', () => {
{}
);
});
it('should handle numeric timestamps with dateFormat x', () => {
imgSnapshotTest(
`
gantt
title Process time profile (ms)
dateFormat x
axisFormat %L
tickInterval 250millisecond
section Pipeline
Parse JSON p1: 000, 120
`,
{}
);
});
it('should handle numeric timestamps with dateFormat X', () => {
imgSnapshotTest(
`
gantt
title Process time profile (ms)
dateFormat X
axisFormat %L
tickInterval 250millisecond
section Pipeline
Parse JSON p1: 000, 120
`,
{}
);
});
it('should handle seconds-only format with tickInterval (issue #5496)', () => {
imgSnapshotTest(
`
gantt
tickInterval 1second
dateFormat ss
axisFormat %s
section Network Request
RTT : rtt, 0, 20
`,
{}
);
});
it('should handle dates with year typo like 202 instead of 2024 (issue #5496)', () => {
imgSnapshotTest(
`
gantt
title Schedule
dateFormat YYYY-MM-DD
tickInterval 1week
axisFormat %m-%d
section Vacation
London : 2024-12-01, 7d
London : 202-12-01, 7d
`,
{}
);
});
});

View File

@@ -247,31 +247,5 @@ root
);
});
});
describe('Level 2 nodes exceeding 11', () => {
it('should render all Level 2 nodes correctly when there are more than 11', () => {
imgSnapshotTest(
`mindmap
root
Node1
Node2
Node3
Node4
Node5
Node6
Node7
Node8
Node9
Node10
Node11
Node12
Node13
Node14
Node15`,
{},
undefined,
shouldHaveRoot
);
});
});
/* The end */
});

View File

@@ -0,0 +1,118 @@
import { imgSnapshotTest } from '../../helpers/util';
const looks = ['classic'] as const;
looks.forEach((look) => {
describe(`SequenceDiagram icon participants in ${look} look`, () => {
it(`single participant with icon`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
Note over Bob: Icon participant`;
imgSnapshotTest(diagram, { look });
});
it(`two participants, one icon and one normal`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
participant Alice
Bob->>Alice: Hello`;
imgSnapshotTest(diagram, { look });
});
it(`two icon participants`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
participant Alice@{ type: "icon", icon: "fa:user" }
Bob->>Alice: Hello
Alice-->>Bob: Hi`;
imgSnapshotTest(diagram, { look });
});
it(`with markdown htmlLabels:true content`, () => {
// html/markdown in messages/notes (participants themselves don't support label/form/w/h)
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
participant Alice
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>
Note over Bob,Alice: Mixed <em>HTML</em> and **markdown**`;
imgSnapshotTest(diagram, { look });
});
it(`with markdown htmlLabels:false content`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
participant Alice
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>`;
imgSnapshotTest(diagram, {
look,
htmlLabels: false,
flowchart: { htmlLabels: false },
});
});
it(`with styles applied to participant`, () => {
// style by participant id
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
participant Alice
Bob->>Alice: Styled participant
`;
imgSnapshotTest(diagram, { look });
});
it(`with classDef and class application`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "fa:bell" }
participant Alice
Bob->>Alice: Classed participant
`;
imgSnapshotTest(diagram, { look });
});
});
});
// Colored emoji icon tests (analogous to the flowchart colored icon tests), no direction line.
describe('SequenceDiagram colored icon participant', () => {
it('colored emoji icon without styles', () => {
const icon = 'fluent-emoji:tropical-fish';
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "${icon}" }
Note over Bob: colored emoji icon
`;
imgSnapshotTest(diagram);
});
it('colored emoji icon with styles', () => {
const icon = 'fluent-emoji:tropical-fish';
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "${icon}" }
`;
imgSnapshotTest(diagram);
});
});
// Mixed scenario: multiple interactions, still no direction line.
describe('SequenceDiagram icon participant with multiple interactions', () => {
const icon = 'fa:bell-slash';
it('icon participant interacts with two normal participants', () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "icon", icon: "${icon}" }
participant Alice
participant Carol
Bob->>Alice: Ping
Alice-->>Bob: Pong
Bob->>Carol: Notify
Note right of Bob: Icon side note`;
imgSnapshotTest(diagram);
});
});

View File

@@ -0,0 +1,94 @@
import { imgSnapshotTest } from '../../helpers/util';
const looks = ['classic'] as const;
looks.forEach((look) => {
describe(`SequenceDiagram image participants in ${look} look`, () => {
it(`single participant with image`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
Note over Bob: Image participant`;
imgSnapshotTest(diagram, { look });
});
it(`two participants, one image and one normal`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Alice
Bob->>Alice: Hello`;
imgSnapshotTest(diagram, { look });
});
it(`two image participants`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Alice@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
Bob->>Alice: Hello
Alice-->>Bob: Hi`;
imgSnapshotTest(diagram, { look });
});
it(`with markdown htmlLabels:true content`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Alice
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>
Note over Bob,Alice: Mixed <em>HTML</em> and **markdown**`;
imgSnapshotTest(diagram, { look });
});
it(`with markdown htmlLabels:false content`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Alice
Bob->>Alice: This is **bold** </br>and <strong>strong</strong>`;
imgSnapshotTest(diagram, {
look,
htmlLabels: false,
flowchart: { htmlLabels: false },
});
});
it(`with styles applied to participant`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Alice
Bob->>Alice: Styled participant
`;
imgSnapshotTest(diagram, { look });
});
it(`with classDef and class application`, () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Alice
Bob->>Alice: Classed participant
`;
imgSnapshotTest(diagram, { look });
});
});
});
// Mixed scenario: multiple interactions, still no direction line.
describe('SequenceDiagram image participant with multiple interactions', () => {
const imageUrl = 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg';
it('image participant interacts with two normal participants', () => {
const diagram = `
sequenceDiagram
participant Bob@{ type: "image", "image": "${imageUrl}" }
participant Alice
participant Carol
Bob->>Alice: Ping
Alice-->>Bob: Pong
Bob->>Carol: Notify
Note right of Bob: Image side note`;
imgSnapshotTest(diagram);
});
});

View File

@@ -655,315 +655,5 @@ describe('Sequence Diagram Special Cases', () => {
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 } }
);
});
});
describe('Participant Stereotypes with Aliases', () => {
it('should render participants with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary" } as Public API
participant Auth@{ "type" : "control" } as Auth Controller
participant DB@{ "type" : "database" } as User Database
participant Cache@{ "type" : "entity" } as Cache Layer
API ->> Auth: Authenticate request
Auth ->> DB: Query user
DB -->> Auth: User data
Auth ->> Cache: Store session
Cache -->> Auth: Confirmed
Auth -->> API: Token`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render actors with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
actor U@{ "type" : "actor" } as End User
actor A@{ "type" : "boundary" } as API Gateway
actor S@{ "type" : "control" } as Service Layer
actor D@{ "type" : "database" } as Data Store
U ->> A: Send request
A ->> S: Process
S ->> D: Persist
D -->> S: Success
S -->> A: Response
A -->> U: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render mixed participants and actors with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
actor Client@{ "type" : "actor" } AS Mobile Client
participant Gateway@{ "type" : "boundary" } as API Gateway
participant OrderSvc@{ "type" : "control" } as Order Service
participant Queue@{ "type" : "queue" } as Message Queue
participant DB@{ "type" : "database" } as Order Database
participant Logs@{ "type" : "collections" } as Audit Logs
Client ->> Gateway: Place order
Gateway ->> OrderSvc: Validate order
OrderSvc ->> Queue: Queue for processing as well
OrderSvc ->> DB: Save order
OrderSvc ->> Logs: Log transaction
Queue -->> OrderSvc: Processing started AS Well
DB -->> OrderSvc: Order saved
Logs -->> OrderSvc: Logged
OrderSvc -->> Gateway: Order confirmed
Gateway -->> Client: Confirmation`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render stereotypes with aliases in boxes', () => {
imgSnapshotTest(
`sequenceDiagram
box rgb(200,220,255) Frontend Layer
actor User@{ "type" : "actor" } as End User
participant UI@{ "type" : "boundary" } as User Interface
end
box rgb(255,220,200) Backend Layer
participant API@{ "type" : "boundary" } as REST API
participant Svc@{ "type" : "control" } as Business Logic
end
box rgb(220,255,200) Data Layer
participant DB@{ "type" : "database" } as Primary DB
participant Cache@{ "type" : "entity" } as Cache Store
end
User ->> UI: Click button
UI ->> API: HTTP request
API ->> Svc: Process
Svc ->> Cache: Check cache
Cache -->> Svc: Cache miss
Svc ->> DB: Query data
DB -->> Svc: Data
Svc ->> Cache: Update cache
Svc -->> API: Response
API -->> UI: Data
UI -->> User: Display`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render stereotypes with aliases and complex interactions', () => {
imgSnapshotTest(
`sequenceDiagram
participant Web@{ "type" : "boundary" } as Web Portal
participant Auth@{ "type" : "control" } as Auth Service
participant UserDB@{ "type" : "database" } as User DB
participant Queue@{ "type" : "queue" } as Event Queue
participant Audit@{ "type" : "collections" } as Audit Trail
Web ->> Auth: Login request
activate Auth
Auth ->> UserDB: Verify credentials
activate UserDB
UserDB -->> Auth: User found
deactivate UserDB
alt Valid credentials
Auth ->> Queue: Publish login event
Auth ->> Audit: Log success
par Parallel processing
Queue -->> Auth: Event queued
and
Audit -->> Auth: Logged
end
Auth -->> Web: Success token
else Invalid credentials
Auth ->> Audit: Log failure
Audit -->> Auth: Logged
Auth --x Web: Access denied
end
deactivate Auth
Note over Web,Audit: All interactions logged`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
describe('Participant Inline Alias in Config', () => {
it('should render participants with inline alias in config object', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary", "alias": "Public API" }
participant Auth@{ "type" : "control", "alias": "Auth Service" }
participant DB@{ "type" : "database", "alias": "User DB" }
API ->> Auth: Login request
Auth ->> DB: Query user
DB -->> Auth: User data
Auth -->> API: Token`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render actors with inline alias in config object', () => {
imgSnapshotTest(
`sequenceDiagram
actor U@{ "type" : "actor", "alias": "End User" }
actor G@{ "type" : "boundary", "alias": "Gateway" }
actor S@{ "type" : "control", "alias": "Service" }
U ->> G: Request
G ->> S: Process
S -->> G: Response
G -->> U: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should handle mixed inline and external alias syntax', () => {
imgSnapshotTest(
`sequenceDiagram
participant A@{ "type" : "boundary", "alias": "Service A" }
participant B@{ "type" : "control" } as Service B
participant C@{ "type" : "database" }
A ->> B: Request
B ->> C: Query
C -->> B: Data
B -->> A: Response`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should prioritize external alias over inline alias', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type" : "database", "alias": "Internal DB" } AS External DB
API ->> DB: Query
DB -->> API: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render inline alias with only alias field (no type)', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "alias": "Public API" }
participant Auth@{ "alias": "Auth Service" }
API ->> Auth: Request
Auth -->> API: Response`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
});
});

View File

@@ -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`
);
});
});
});

View File

@@ -327,97 +327,8 @@ classDef sales fill:#c3a66b,stroke:#333;
{}
);
});
it('12: should apply classDef fill color to leaf nodes', () => {
imgSnapshotTest(
`treemap-beta
"Root"
"Item A": 30:::redClass
"Item B": 20
"Item C": 25:::blueClass
classDef redClass fill:#ff0000;
classDef blueClass fill:#0000ff;
`,
{}
);
});
it('13: should apply classDef stroke styles to sections', () => {
imgSnapshotTest(
`treemap-beta
%% This is a comment
"Category A":::thickBorder
"Item A1": 10
"Item A2": 20
%% Another comment
"Category B":::dashedBorder
"Item B1": 15
"Item B2": 25
classDef thickBorder stroke:red,stroke-width:8px;
classDef dashedBorder stroke:black,stroke-dasharray:5,stroke-width:8px;
`,
{}
);
});
it('14: should apply classDef color to text labels', () => {
imgSnapshotTest(
`treemap-beta
"Products"
"Electronics":::whiteText
"Phones": 40
"Laptops": 30
"Furniture":::darkText
"Chairs": 25
"Tables": 20
classDef whiteText fill:#2c3e50,color:#ffffff;
classDef darkText fill:#ecf0f1,color:#000000;
`,
{}
);
});
it('15: should apply multiple classDef properties simultaneously', () => {
imgSnapshotTest(
`treemap-beta
"Budget"
"Critical":::critical
"Server Costs": 50000
"Salaries": 80000
"Normal":::normal
"Office Supplies": 5000
"Marketing": 15000
classDef critical fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:3px;
classDef normal fill:#3498db,color:#fff,stroke:#2980b9,stroke-width:1px;
`,
{}
);
});
it('16: should handle classDef on nested sections and leaves', () => {
imgSnapshotTest(
`treemap-beta
"Company"
"Engineering":::engSection
"Frontend": 30:::highlight
"Backend": 40
"DevOps": 20:::highlight
"Sales"
"Direct": 35
"Channel": 25:::highlight
classDef engSection fill:#9b59b6,stroke:#8e44ad,stroke-width:2px;
classDef highlight fill:#f39c12,color:#000,stroke:#e67e22,stroke-width:2px;
`,
{}
);
});
/*
it.skip('17: should render a treemap with title', () => {
it.skip('12: should render a treemap with title', () => {
imgSnapshotTest(
`
treemap-beta

View File

@@ -110,48 +110,6 @@
config:
layout: elk
---
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness&lt;br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
id)I am a cloud(
id))I am a bang((
Tools
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart
aid0
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
mindmap
aid0
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: ogdc
---
flowchart-elk TB
c1-->a2
subgraph one

View File

@@ -603,10 +603,6 @@
</div>
<div class="test">
<pre class="mermaid">
---
config:
theme: dark
---
classDiagram
test ()--() test2
</pre>

View File

@@ -2,227 +2,227 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 5944
"duration": 5841
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2180
"duration": 2138
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3282
"duration": 3370
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 2137
"duration": 2052
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 11926
"duration": 12243
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 2021
"duration": 2065
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 31377
"duration": 31288
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3442
"duration": 3421
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 103
"duration": 97
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 18390
"duration": 18500
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 6468
"duration": 5793
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 41282
"duration": 40966
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 39226
"duration": 39176
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 25028
"duration": 23468
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 38458
"duration": 38291
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 17305
"duration": 16949
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9762
"duration": 9480
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2923
"duration": 2753
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 89135
"duration": 88028
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 18976
"duration": 15615
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3643
"duration": 3706
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 43103
"duration": 43905
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 31637
"duration": 31217
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7630
"duration": 7531
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 25642
"duration": 25423
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 50365
"duration": 49664
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 32790
"duration": 32525
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 23065
"duration": 20915
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 52238
"duration": 53556
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 289380
"duration": 283038
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 59265
"duration": 59434
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3269
"duration": 3101
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7470
"duration": 7099
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7980
"duration": 7567
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3896
"duration": 3817
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2640
"duration": 2624
},
{
"spec": "cypress/integration/rendering/mindmap-tidy-tree.spec.js",
"duration": 4327
"duration": 4246
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 12588
"duration": 11967
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 153490
"duration": 151914
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 117833
"duration": 116698
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4975
"duration": 4967
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 6682
"duration": 6700
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 8972
"duration": 8963
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5631
"duration": 5540
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2776
"duration": 2782
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 54373
"duration": 54797
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 7203
"duration": 6914
},
{
"spec": "cypress/integration/rendering/sequencediagram-v2.spec.js",
"duration": 31707
"duration": 20481
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 48327
"duration": 38490
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 30728
"duration": 30766
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16881
"duration": 16705
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30715
"duration": 30928
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8586
"duration": 8424
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 15184
"duration": 12533
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 21282
"duration": 21197
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3576
"duration": 3455
}
]
}

View File

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

View File

@@ -184,7 +184,6 @@
}
Admin --> Report : generates
</pre>
<hr />
<pre class="mermaid">
classDiagram
namespace Company.Project.Module {
@@ -241,20 +240,6 @@
Bike --> Square : "Logo Shape"
</pre>
<hr />
<pre class="mermaid">
classDiagram
note "This is a outer note"
note for Class1 "This is a outer note for Class1"
namespace ns {
note "This is a inner note"
note for Class1 "This is a inner note for Class1"
class Class1
class Class2
}
</pre>
<hr />
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({

View File

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

View File

@@ -29,8 +29,7 @@ In GitHub, you first [**fork a mermaid repository**](https://github.com/mermaid-
Then you **clone** a copy to your local development machine (e.g. where you code) to make a copy with all the files to work with.
> **💡 Tip**
> [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
> **💡 Tip** > [Here is a GitHub document that gives an overview of the process](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
```bash
git clone git@github.com/your-fork/mermaid

View File

@@ -33,8 +33,7 @@ mindmap
## Join the Development
> **💡 Tip**
> **Check out our** [**detailed contribution guide**](./contributing.md).
> **💡 Tip** > **Check out our** [**detailed contribution guide**](./contributing.md).
Where to start:
@@ -48,8 +47,7 @@ Where to start:
## A Question Or a Suggestion?
> **💡 Tip**
> **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
> **💡 Tip** > **Have a look at** [**how to open an issue**](./questions-and-suggestions.md).
If you have faced a vulnerability [report it to us](./security.md).

View File

@@ -22,6 +22,7 @@ While directives allow you to change most of the default configuration settings,
Mermaid basically supports two types of configuration options to be overridden by directives.
1. _General/Top Level configurations_ : These are the configurations that are available and applied to all the diagram. **Some of the most important top-level** configurations are:
- theme
- fontFamily
- logLevel

View File

@@ -10,7 +10,7 @@
# Interface: ParseOptions
Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
Defined in: [packages/mermaid/src/types.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L76)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mer
> `optional` **suppressErrors**: `boolean`
Defined in: [packages/mermaid/src/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L94)
Defined in: [packages/mermaid/src/types.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L81)
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
The `parseError` function will not be called.

View File

@@ -10,7 +10,7 @@
# Interface: ParseResult
Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L97)
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mer
> **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/types.ts:105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L105)
Defined in: [packages/mermaid/src/types.ts:92](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L92)
The config passed as YAML frontmatter or directives
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
> **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L101)
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
The diagram type, e.g. 'flowchart', 'sequence', etc.

View File

@@ -10,7 +10,7 @@
# Interface: RenderResult
Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L115)
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/me
> `optional` **bindFunctions**: (`element`) => `void`
Defined in: [packages/mermaid/src/types.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L133)
Defined in: [packages/mermaid/src/types.ts:120](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L120)
Bind function to be called after the svg has been inserted into the DOM.
This is necessary for adding event listeners to the elements in the svg.
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
> **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L123)
Defined in: [packages/mermaid/src/types.ts:110](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L110)
The diagram type, e.g. 'flowchart', 'sequence', etc.
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
> **svg**: `string`
Defined in: [packages/mermaid/src/types.ts:119](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L119)
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
The svg code for the rendered graph.

View File

@@ -57,9 +57,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅
- [GNU Octave](https://octave.org/) ✅
- [octave_mermaid_js](https://github.com/CNOCTAVE/octave_mermaid_js) ✅
- [HackMD](https://hackmd.io/c/tutorials/%2F%40docs%2Fflowchart-en#Create-more-complex-flowcharts) ✅
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [MonsterWriter](https://www.monsterwriter.com/) ✅
- [Joplin](https://joplinapp.org) ✅
@@ -275,7 +272,6 @@ Communication tools and platforms
- [reveal.js-mermaid-plugin](https://github.com/ludwick/reveal.js-mermaid-plugin)
- [Reveal CK](https://github.com/jedcn/reveal-ck)
- [reveal-ck-mermaid-plugin](https://github.com/tmtm/reveal-ck-mermaid-plugin)
- [speccharts: Turn your test suites into specification diagrams](https://github.com/arnaudrenaud/speccharts)
- [Vitepress Plugin](https://github.com/sametcn99/vitepress-mermaid-renderer)
<!--- cspell:ignore Blazorade HueHive --->

View File

@@ -29,6 +29,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
- **Plugins** - A plugin system for extending the functionality of Mermaid.
Official Mermaid Chart plugins:
- [Mermaid Chart GPT](https://chatgpt.com/g/g-684cc36f30208191b21383b88650a45d-mermaid-chart-diagrams-and-charts)
- [Confluence](https://marketplace.atlassian.com/apps/1234056/mermaid-chart-for-confluence?hosting=cloud&tab=overview)
- [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud)
@@ -47,7 +48,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
## Plans
- **Free** - A free plan that includes six diagrams.
- **Free** - A free plan that includes three diagrams.
- **Pro** - A paid plan that includes unlimited diagrams, access to the collaboration feature, and more.

View File

@@ -35,11 +35,13 @@ The Mermaid Chart team is excited to introduce a new Visual Editor for Flowchart
Learn more:
- Visual Editor For Flowcharts
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts)
- [Demo video](https://www.youtube.com/watch?v=5aja0gijoO0)
- Visual Editor For Sequence diagrams
- [Blog post](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams)
- [Demo video](https://youtu.be/imc2u5_N6Dc)

View File

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

View File

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

View File

@@ -402,7 +402,7 @@ block
blockArrowId4<["Label"]>(down)
blockArrowId5<["Label"]>(x)
blockArrowId6<["Label"]>(y)
blockArrowId7<["Label"]>(x, down)
blockArrowId6<["Label"]>(x, down)
```
```mermaid
@@ -413,7 +413,7 @@ block
blockArrowId4<["Label"]>(down)
blockArrowId5<["Label"]>(x)
blockArrowId6<["Label"]>(y)
blockArrowId7<["Label"]>(x, down)
blockArrowId6<["Label"]>(x, down)
```
#### Example - Space Blocks

View File

@@ -139,6 +139,7 @@ The following unfinished features are not supported in the short term.
- [ ] Legend
- [x] System Context
- [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
- [x] Person_Ext
- [x] System(alias, label, ?descr, ?sprite, ?tags, $link)
@@ -152,6 +153,7 @@ The following unfinished features are not supported in the short term.
- [x] System_Boundary
- [x] Container diagram
- [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
- [x] ContainerDb
- [x] ContainerQueue
@@ -161,6 +163,7 @@ The following unfinished features are not supported in the short term.
- [x] Container_Boundary(alias, label, ?tags, $link)
- [x] Component diagram
- [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
- [x] ComponentDb
- [x] ComponentQueue
@@ -169,15 +172,18 @@ The following unfinished features are not supported in the short term.
- [x] ComponentQueue_Ext
- [x] Dynamic diagram
- [x] RelIndex(index, from, to, label, ?tags, $link)
- [x] Deployment diagram
- [x] Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link)
- [x] Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link): short name of Deployment_Node()
- [x] Node_L(alias, label, ?type, ?descr, ?sprite, ?tags, $link): left aligned Node()
- [x] Node_R(alias, label, ?type, ?descr, ?sprite, ?tags, $link): right aligned Node()
- [x] Relationship Types
- [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
- [x] BiRel (bidirectional relationship)
- [x] Rel_U, Rel_Up

View File

@@ -21,7 +21,7 @@ title: Animal example
classDiagram
note "From Duck till Zebra"
Animal <|-- Duck
note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
note for Duck "can fly\ncan swim\ncan dive\ncan help in debugging"
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
@@ -50,7 +50,7 @@ title: Animal example
classDiagram
note "From Duck till Zebra"
Animal <|-- Duck
note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
note for Duck "can fly\ncan swim\ncan dive\ncan help in debugging"
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age

View File

@@ -360,8 +360,7 @@ gantt
weekday monday
```
> **Warning**
> `millisecond` and `second` support was added in v10.3.0
> **Warning** > `millisecond` and `second` support was added in v10.3.0
## Output in compact mode

View File

@@ -62,7 +62,7 @@ radar-beta
radar-beta
title Restaurant Comparison
axis food["Food Quality"], service["Service"], price["Price"]
axis ambiance["Ambiance"]
axis ambiance["Ambiance"],
curve a["Restaurant A"]{4, 3, 2, 4}
curve b["Restaurant B"]{3, 4, 3, 3}
@@ -78,7 +78,7 @@ radar-beta
radar-beta
title Restaurant Comparison
axis food["Food Quality"], service["Service"], price["Price"]
axis ambiance["Ambiance"]
axis ambiance["Ambiance"],
curve a["Restaurant A"]{4, 3, 2, 4}
curve b["Restaurant B"]{3, 4, 3, 3}

View File

@@ -194,13 +194,49 @@ sequenceDiagram
Bob->>Alice: Queue response
```
### Icon
If you want to use a custom icon for a participant, use the JSON configuration syntax as shown below. The `icon` value can be a FontAwesome icon name, emoji, or other supported icon identifier.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "icon", "icon": "fa:bell" }
participant Bob
Alice->>Bob: Icon participant
Bob->>Alice: Response to icon
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "icon", "icon": "fa:bell" }
participant Bob
Alice->>Bob: Icon participant
Bob->>Alice: Response to icon
```
### Image
If you want to use a custom image for a participant, use the JSON configuration syntax as shown below. The `image` value should be a valid image URL.
```mermaid-example
sequenceDiagram
participant Alice@{ "type" : "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Bob
Alice->>Bob: Image participant
Bob->>Alice: Response to image
```
```mermaid
sequenceDiagram
participant Alice@{ "type" : "image", "image": "https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg" }
participant Bob
Alice->>Bob: Image participant
Bob->>Alice: Response to image
```
### Aliases
The actor can have a convenient identifier and a descriptive label. Aliases can be defined in two ways: using external syntax with the `as` keyword, or inline within the configuration object.
#### External Alias Syntax
You can define an alias using the `as` keyword after the participant declaration:
The actor can have a convenient identifier and a descriptive label.
```mermaid-example
sequenceDiagram
@@ -218,78 +254,6 @@ sequenceDiagram
J->>A: Great!
```
The external alias syntax also works with participant stereotype configurations, allowing you to combine type specification with aliases:
```mermaid-example
sequenceDiagram
participant API@{ "type": "boundary" } as Public API
actor DB@{ "type": "database" } as User Database
participant Svc@{ "type": "control" } as Auth Service
API->>Svc: Authenticate
Svc->>DB: Query user
DB-->>Svc: User data
Svc-->>API: Token
```
```mermaid
sequenceDiagram
participant API@{ "type": "boundary" } as Public API
actor DB@{ "type": "database" } as User Database
participant Svc@{ "type": "control" } as Auth Service
API->>Svc: Authenticate
Svc->>DB: Query user
DB-->>Svc: User data
Svc-->>API: Token
```
#### Inline Alias Syntax
Alternatively, you can define an alias directly inside the configuration object using the `"alias"` field. This works with both `participant` and `actor` keywords:
```mermaid-example
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Public API" }
participant Auth@{ "type": "control", "alias": "Auth Service" }
participant DB@{ "type": "database", "alias": "User Database" }
API->>Auth: Login request
Auth->>DB: Query user
DB-->>Auth: User data
Auth-->>API: Access token
```
```mermaid
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Public API" }
participant Auth@{ "type": "control", "alias": "Auth Service" }
participant DB@{ "type": "database", "alias": "User Database" }
API->>Auth: Login request
Auth->>DB: Query user
DB-->>Auth: User data
Auth-->>API: Access token
```
#### Alias Precedence
When both inline alias (in the configuration object) and external alias (using `as` keyword) are provided, the **external alias takes precedence**:
```mermaid-example
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type": "database", "alias": "Internal DB" } as External DB
API->>DB: Query
DB-->>API: Result
```
```mermaid
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type": "database", "alias": "Internal DB" } as External DB
API->>DB: Query
DB-->>API: Result
```
In the example above, "External Name" and "External DB" will be displayed, not "Internal Name" and "Internal DB".
### Actor Creation and Destruction (v10.3.0+)
It is possible to create and destroy actors by messages. To do so, add a create or destroy directive before the message.
@@ -405,11 +369,7 @@ Messages can be of two displayed either solid or with a dotted line.
[Actor][Arrow][Actor]:Message text
```
Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows.
#### Supported Arrow Types
**Standard Arrow Types**
There are ten types of arrows currently supported:
| Type | Description |
| -------- | ---------------------------------------------------- |
@@ -424,58 +384,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) |
| `--)` | 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
It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations:

View File

@@ -63,73 +63,73 @@
]
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.56.5",
"@argos-ci/cypress": "^6.2.2",
"@changesets/changelog-github": "^0.5.2",
"@changesets/cli": "^2.29.8",
"@cspell/eslint-plugin": "^9.3.2",
"@cypress/code-coverage": "^3.14.7",
"@applitools/eyes-cypress": "^3.44.9",
"@argos-ci/cypress": "^5.0.2",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.27.12",
"@cspell/eslint-plugin": "^8.19.4",
"@cypress/code-coverage": "^3.12.49",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.4",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@rollup/plugin-typescript": "^12.1.2",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
"@types/js-yaml": "^4.0.9",
"@types/jsdom": "^21.1.7",
"@types/lodash": "^4.17.21",
"@types/lodash": "^4.17.15",
"@types/mdast": "^4.0.4",
"@types/node": "^22.19.1",
"@types/node": "^22.13.5",
"@types/rollup-plugin-visualizer": "^5.0.3",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/spy": "^3.2.4",
"@vitest/ui": "^3.2.4",
"@vitest/coverage-v8": "^3.0.6",
"@vitest/spy": "^3.0.6",
"@vitest/ui": "^3.0.6",
"ajv": "^8.17.1",
"chokidar": "3.6.0",
"concurrently": "^9.2.1",
"concurrently": "^9.1.2",
"cors": "^2.8.5",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cspell": "^9.3.2",
"cypress": "^14.5.4",
"cspell": "^9.1.3",
"cypress": "^14.5.1",
"cypress-image-snapshot": "^4.0.1",
"cypress-split": "^1.24.25",
"esbuild": "^0.25.12",
"cypress-split": "^1.24.14",
"esbuild": "^0.25.0",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^5.2.0",
"eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-html": "^8.1.3",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsdoc": "^61.1.12",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-jsdoc": "^50.8.0",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^62.0.0",
"express": "^5.2.1",
"globals": "^16.4.0",
"globby": "^14.1.0",
"eslint-plugin-unicorn": "^59.0.1",
"express": "^5.1.0",
"globals": "^16.0.0",
"globby": "^14.0.2",
"husky": "^9.1.7",
"jest": "^30.1.3",
"jest": "^30.0.4",
"jison": "^0.4.18",
"js-yaml": "^4.1.1",
"js-yaml": "^4.1.0",
"jsdom": "^26.1.0",
"langium-cli": "3.3.0",
"lint-staged": "^16.1.6",
"lint-staged": "^16.1.2",
"markdown-table": "^3.0.4",
"nyc": "^17.1.0",
"path-browserify": "^1.0.1",
"prettier": "^3.6.2",
"prettier-plugin-jsdoc": "^1.3.3",
"prettier": "^3.5.2",
"prettier-plugin-jsdoc": "^1.3.2",
"rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.5",
"start-server-and-test": "^2.1.3",
"rollup-plugin-visualizer": "^6.0.3",
"start-server-and-test": "^2.0.10",
"tslib": "^2.8.1",
"tsx": "^4.20.6",
"tsx": "^4.7.3",
"typescript": "~5.7.3",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.8",
"vite": "^7.0.3",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.2.4"
"vitest": "^3.0.6"
},
"nyc": {
"report-dir": "coverage/cypress"

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/layout-elk",
"version": "0.2.0",
"version": "0.1.9",
"description": "ELK layout engine for mermaid",
"module": "dist/mermaid-layout-elk.core.mjs",
"types": "dist/layouts.d.ts",
@@ -18,9 +18,7 @@
"elk",
"mermaid"
],
"scripts": {
"clean": "rimraf dist"
},
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"

View File

@@ -67,22 +67,7 @@ export const render = async (
// Add the element to the DOM
if (!node.isGroup) {
// const child = node as NodeWithVertex;
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,
};
const child = node as NodeWithVertex;
graph.children.push(child);
nodeDb[node.id] = node;
@@ -165,7 +150,7 @@ export const render = async (
domId: { node: () => any; attr: (arg0: string, arg1: string) => void };
}) {
if (node) {
nodeDb[node.id] ??= {};
nodeDb[node.id] = node;
nodeDb[node.id].offset = {
posX: node.x + relX,
posY: node.y + relY,
@@ -875,13 +860,11 @@ export const render = async (
log.info('APA01 layout result:', JSON.stringify(g, null, 2));
} catch (error) {
log.error('APA01 ELK layout error:', error);
log.error('APA01 elkGraph that caused error:', JSON.stringify(elkGraph, null, 2));
throw error;
}
// debugger;
await drawNodes(0, 0, g.children, svg, subGraphsEl, 0);
g.edges?.map(
(edge: {
sources: (string | number)[];

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/layout-tidy-tree",
"version": "0.2.0",
"version": "0.1.0",
"description": "Tidy-tree layout engine for mermaid",
"module": "dist/mermaid-layout-tidy-tree.core.mjs",
"types": "dist/layouts.d.ts",
@@ -19,9 +19,7 @@
"mermaid",
"layout"
],
"scripts": {
"clean": "rimraf dist"
},
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"

View File

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

View File

@@ -1,44 +1,5 @@
# mermaid
## 11.12.1
### Patch Changes
- [#7107](https://github.com/mermaid-js/mermaid/pull/7107) [`cbf8946`](https://github.com/mermaid-js/mermaid/commit/cbf89462acecac7a06f19843e8d48cb137df0753) Thanks [@shubhamparikh2704](https://github.com/shubhamparikh2704)! - fix: Updated the dependency dagre-d3-es to 7.0.13 to fix GHSA-cc8p-78qf-8p7q
## 11.12.0
### Minor Changes
- [#6921](https://github.com/mermaid-js/mermaid/pull/6921) [`764b315`](https://github.com/mermaid-js/mermaid/commit/764b315dc16d0359add7c6b8e3ef7592e9bdc09c) Thanks [@quilicicf](https://github.com/quilicicf)! - feat: Add IDs in architecture diagrams
### Patch Changes
- [#6950](https://github.com/mermaid-js/mermaid/pull/6950) [`a957908`](https://github.com/mermaid-js/mermaid/commit/a9579083bfba367a4f4678547ec37ed7b61b9f5b) Thanks [@shubhamparikh2704](https://github.com/shubhamparikh2704)! - chore: Fix mindmap rendering in docs and apply tidytree layout
- [#6826](https://github.com/mermaid-js/mermaid/pull/6826) [`1d36810`](https://github.com/mermaid-js/mermaid/commit/1d3681053b9168354e48e5763023b6305cd1ca72) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Ensure edge label color is applied when using classDef with edge IDs
- [#6945](https://github.com/mermaid-js/mermaid/pull/6945) [`d318f1a`](https://github.com/mermaid-js/mermaid/commit/d318f1a13cd7429334a29c70e449074ec1cb9f68) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Resolve gantt chart crash due to invalid array length
- [#6918](https://github.com/mermaid-js/mermaid/pull/6918) [`cfe9238`](https://github.com/mermaid-js/mermaid/commit/cfe9238882cbe95416db1feea3112456a71b6aaf) Thanks [@shubhamparikh2704](https://github.com/shubhamparikh2704)! - chore: revert marked dependency from ^15.0.7 to ^16.0.0
- Reverted marked package version to ^16.0.0 for better compatibility
- This is a dependency update that maintains API compatibility
- All tests pass with the updated version
## 11.11.0
### Minor Changes
- [#6704](https://github.com/mermaid-js/mermaid/pull/6704) [`012530e`](https://github.com/mermaid-js/mermaid/commit/012530e98e9b8b80962ab270b6bb3b6d9f6ada05) Thanks [@omkarht](https://github.com/omkarht)! - feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.
- [#6802](https://github.com/mermaid-js/mermaid/pull/6802) [`c8e5027`](https://github.com/mermaid-js/mermaid/commit/c8e50276e877c4de7593a09ec458c99353e65af8) Thanks [@darshanr0107](https://github.com/darshanr0107)! - feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes
### Patch Changes
- [#6905](https://github.com/mermaid-js/mermaid/pull/6905) [`33bc4a0`](https://github.com/mermaid-js/mermaid/commit/33bc4a0b4e2ca6d937bb0a8c4e2081b1362b2800) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Render newlines as spaces in class diagrams
- [#6886](https://github.com/mermaid-js/mermaid/pull/6886) [`e0b45c2`](https://github.com/mermaid-js/mermaid/commit/e0b45c2d2b41c2a9038bf87646fa3ccd7560eb20) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Handle arrows correctly when auto number is enabled
## 11.10.0
### Minor Changes
@@ -193,6 +154,7 @@
### Minor Changes
- [#6408](https://github.com/mermaid-js/mermaid/pull/6408) [`ad65313`](https://github.com/mermaid-js/mermaid/commit/ad653138e16765d095613a6e5de86dc5e52ac8f0) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - fix: restore curve type configuration functionality for flowcharts. This fixes the issue where curve type settings were not being applied when configured through any of the following methods:
- Config
- Init directive (%%{ init: { 'flowchart': { 'curve': '...' } } }%%)
- LinkStyle command (linkStyle default interpolate ...)
@@ -211,12 +173,14 @@
### Minor Changes
- [#6187](https://github.com/mermaid-js/mermaid/pull/6187) [`7809b5a`](https://github.com/mermaid-js/mermaid/commit/7809b5a93fae127f45727071f5ff14325222c518) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Flowchart new syntax for node metadata bugs
- Incorrect label mapping for nodes when using `&`
- Syntax error when `}` with trailing spaces before new line
- [#6136](https://github.com/mermaid-js/mermaid/pull/6136) [`ec0d9c3`](https://github.com/mermaid-js/mermaid/commit/ec0d9c389aa6018043187654044c1e0b5aa4f600) Thanks [@knsv](https://github.com/knsv)! - Adding support for animation of flowchart edges
- [#6373](https://github.com/mermaid-js/mermaid/pull/6373) [`05bdf0e`](https://github.com/mermaid-js/mermaid/commit/05bdf0e20e2629fe77513218fbd4e28e65f75882) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Upgrade Requirement and ER diagram to use the common renderer flow
- Added support for directions
- Added support for hand drawn look

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.12.1",
"version": "11.10.0",
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -67,33 +67,33 @@
]
},
"dependencies": {
"@braintree/sanitize-url": "^7.1.1",
"@iconify/utils": "^3.0.2",
"@braintree/sanitize-url": "^7.0.4",
"@iconify/utils": "^3.0.1",
"@mermaid-js/parser": "workspace:^",
"@types/d3": "^7.4.3",
"cytoscape": "^3.33.1",
"cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.2.0",
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.13",
"dayjs": "^1.11.19",
"dompurify": "^3.3.0",
"katex": "^0.16.25",
"dagre-d3-es": "7.0.11",
"dayjs": "^1.11.13",
"dompurify": "^3.2.5",
"katex": "^0.16.22",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
"marked": "^16.3.0",
"marked": "^16.0.0",
"roughjs": "^4.6.6",
"stylis": "^4.3.6",
"ts-dedent": "^2.2.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.8",
"@adobe/jsonschema2md": "^8.0.2",
"@iconify/types": "^2.0.0",
"@types/cytoscape": "^3.21.9",
"@types/cytoscape-fcose": "^2.2.5",
"@types/d3-sankey": "^0.12.5",
"@types/cytoscape-fcose": "^2.2.4",
"@types/d3-sankey": "^0.12.4",
"@types/d3-scale": "^4.0.9",
"@types/d3-scale-chromatic": "^3.1.0",
"@types/d3-selection": "^3.0.11",
@@ -101,34 +101,34 @@
"@types/jsdom": "^21.1.7",
"@types/katex": "^0.16.7",
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.10",
"@types/micromatch": "^4.0.9",
"@types/stylis": "^4.2.7",
"@types/uuid": "^10.0.0",
"ajv": "^8.17.1",
"canvas": "^3.2.0",
"canvas": "^3.1.0",
"chokidar": "3.6.0",
"concurrently": "^9.2.1",
"concurrently": "^9.1.2",
"csstree-validator": "^4.0.1",
"globby": "^14.1.0",
"globby": "^14.0.2",
"jison": "^0.4.18",
"js-base64": "^3.7.8",
"js-base64": "^3.7.7",
"jsdom": "^26.1.0",
"json-schema-to-typescript": "^15.0.4",
"micromatch": "^4.0.8",
"path-browserify": "^1.0.1",
"prettier": "^3.6.2",
"prettier": "^3.5.2",
"remark": "^15.0.1",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"rimraf": "^6.0.1",
"start-server-and-test": "^2.1.3",
"type-fest": "^4.41.0",
"typedoc": "^0.28.15",
"typedoc-plugin-markdown": "^4.8.1",
"start-server-and-test": "^2.0.10",
"type-fest": "^4.35.0",
"typedoc": "^0.28.9",
"typedoc-plugin-markdown": "^4.8.0",
"typescript": "~5.7.3",
"unist-util-flatmap": "^1.0.0",
"unist-util-visit": "^5.0.0",
"vitepress": "^1.6.4",
"vitepress": "^1.0.2",
"vitepress-plugin-search": "1.0.4-alpha.22"
},
"files": [

View File

@@ -72,7 +72,7 @@ export const addDiagrams = () => {
}
);
if (injected.includeLargeFeatures) {
if (includeLargeFeatures) {
registerLazyLoadedDiagrams(flowchartElk, mindmap, architecture);
}

View File

@@ -1,58 +0,0 @@
import c4Db from '../c4Db.js';
import c4 from './c4Diagram.jison';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe.each([
['Component', 'component'],
['ComponentDb', 'component_db'],
['ComponentQueue', 'component_queue'],
['Component_Ext', 'external_component'],
['ComponentDb_Ext', 'external_component_db'],
['ComponentQueue_Ext', 'external_component_queue'],
])('parsing a C4 %s', function (macroName, elementName) {
beforeEach(function () {
c4.parser.yy = c4Db;
c4.parser.yy.clear();
});
it('should parse a C4 diagram with one Component correctly', function () {
c4.parser.parse(`C4Component
title Component diagram for Internet Banking Component
${macroName}(ComponentAA, "Internet Banking Component", "Technology", "Allows customers to view information about their bank accounts, and make payments.")`);
const yy = c4.parser.yy;
const shapes = yy.getC4ShapeArray();
expect(shapes.length).toBe(1);
const onlyShape = shapes[0];
expect(onlyShape).toMatchObject({
alias: 'ComponentAA',
descr: {
text: 'Allows customers to view information about their bank accounts, and make payments.',
},
label: {
text: 'Internet Banking Component',
},
techn: {
text: 'Technology',
},
typeC4Shape: {
text: elementName,
},
});
});
it('should handle a trailing whitespaces after Component', function () {
const whitespace = ' ';
const rendered = c4.parser.parse(`C4Component${whitespace}
title Component diagram for Internet Banking Component${whitespace}
${macroName}(ComponentAA, "Internet Banking Component", "Technology", "Allows customers to view information about their bank accounts, and make payments.")${whitespace}`);
expect(rendered).toBe(true);
});
});

View File

@@ -158,10 +158,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
"UpdateRelStyle" { this.begin("update_rel_style"); return 'UPDATE_REL_STYLE';}
"UpdateLayoutConfig" { this.begin("update_layout_config"); return 'UPDATE_LAYOUT_CONFIG';}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config><<EOF>> return "EOF_IN_STRUCT";
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(][ ]*[,] { this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(] { this.begin("attribute"); }
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config,attribute>[)] { this.popState();this.popState();}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config><<EOF>> return "EOF_IN_STRUCT";
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(][ ]*[,] { this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(] { this.begin("attribute"); }
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config,attribute>[)] { this.popState();this.popState();}
<attribute>",," { return 'ATTRIBUTE_EMPTY';}
<attribute>"," { }

View File

@@ -17,7 +17,6 @@ import type {
ClassRelation,
ClassNode,
ClassNote,
ClassNoteMap,
ClassMap,
NamespaceMap,
NamespaceNode,
@@ -34,16 +33,15 @@ const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());
export class ClassDB implements DiagramDB {
private relations: ClassRelation[] = [];
private classes: ClassMap = new Map<string, ClassNode>();
private classes = new Map<string, ClassNode>();
private readonly styleClasses = new Map<string, StyleClass>();
private notes: ClassNoteMap = new Map<string, ClassNote>();
private notes: ClassNote[] = [];
private interfaces: Interface[] = [];
// private static classCounter = 0;
private namespaces = new Map<string, NamespaceNode>();
private namespaceCounter = 0;
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
private functions: Function[] = [];
private functions: any[] = [];
constructor() {
this.functions.push(this.setupToolTips.bind(this));
@@ -126,7 +124,7 @@ export class ClassDB implements DiagramDB {
annotations: [],
styles: [],
domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter,
});
} as ClassNode);
classCounter++;
}
@@ -157,12 +155,12 @@ export class ClassDB implements DiagramDB {
public clear() {
this.relations = [];
this.classes = new Map<string, ClassNode>();
this.notes = new Map<string, ClassNote>();
this.classes = new Map();
this.notes = [];
this.interfaces = [];
this.functions = [];
this.functions.push(this.setupToolTips.bind(this));
this.namespaces = new Map<string, NamespaceNode>();
this.namespaces = new Map();
this.namespaceCounter = 0;
this.direction = 'TB';
commonClear();
@@ -180,12 +178,7 @@ export class ClassDB implements DiagramDB {
return this.relations;
}
public getNote(id: string | number): ClassNote {
const key = typeof id === 'number' ? `note${id}` : id;
return this.notes.get(key)!;
}
public getNotes(): ClassNoteMap {
public getNotes() {
return this.notes;
}
@@ -286,19 +279,16 @@ export class ClassDB implements DiagramDB {
}
}
public addNote(text: string, className: string): string {
const index = this.notes.size;
public addNote(text: string, className: string) {
const note = {
id: `note${index}`,
id: `note${this.notes.length}`,
class: className,
text: text,
index: index,
};
this.notes.set(note.id, note);
return note.id;
this.notes.push(note);
}
public cleanupLabel(label: string): string {
public cleanupLabel(label: string) {
if (label.startsWith(':')) {
label = label.substring(1);
}
@@ -364,7 +354,7 @@ export class ClassDB implements DiagramDB {
});
}
public getTooltip(id: string, namespace?: string): string | undefined {
public getTooltip(id: string, namespace?: string) {
if (namespace && this.namespaces.has(namespace)) {
return this.namespaces.get(namespace)!.classes.get(id)!.tooltip;
}
@@ -544,11 +534,10 @@ export class ClassDB implements DiagramDB {
this.namespaces.set(id, {
id: id,
classes: new Map<string, ClassNode>(),
notes: new Map<string, ClassNote>(),
children: new Map<string, NamespaceNode>(),
classes: new Map(),
children: {},
domId: MERMAID_DOM_ID_PREFIX + id + '-' + this.namespaceCounter,
});
} as NamespaceNode);
this.namespaceCounter++;
}
@@ -566,23 +555,16 @@ export class ClassDB implements DiagramDB {
*
* @param id - ID of the namespace to add
* @param classNames - IDs of the class to add
* @param noteNames - IDs of the notes to add
* @public
*/
public addClassesToNamespace(id: string, classNames: string[], noteNames: string[]) {
public addClassesToNamespace(id: string, classNames: string[]) {
if (!this.namespaces.has(id)) {
return;
}
for (const name of classNames) {
const { className } = this.splitClassNameAndType(name);
const classNode = this.getClass(className);
classNode.parent = id;
this.namespaces.get(id)!.classes.set(className, classNode);
}
for (const noteName of noteNames) {
const noteNode = this.getNote(noteName);
noteNode.parent = id;
this.namespaces.get(id)!.notes.set(noteName, noteNode);
this.classes.get(className)!.parent = id;
this.namespaces.get(id)!.classes.set(className, this.classes.get(className)!);
}
}
@@ -635,32 +617,36 @@ export class ClassDB implements DiagramDB {
const edges: Edge[] = [];
const config = getConfig();
for (const namespace of this.namespaces.values()) {
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: [],
look: config.look,
};
nodes.push(node);
for (const namespaceKey of this.namespaces.keys()) {
const namespace = this.namespaces.get(namespaceKey);
if (namespace) {
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'],
look: config.look,
};
nodes.push(node);
}
}
for (const classNode of this.classes.values()) {
const node: Node = {
...classNode,
type: undefined,
isGroup: false,
parentId: classNode.parent,
look: config.look,
};
nodes.push(node);
for (const classKey of this.classes.keys()) {
const classNode = this.classes.get(classKey);
if (classNode) {
const node = classNode as unknown as Node;
node.parentId = classNode.parent;
node.look = config.look;
nodes.push(node);
}
}
for (const note of this.notes.values()) {
let cnt = 0;
for (const note of this.notes) {
cnt++;
const noteNode: Node = {
id: note.id,
label: note.text,
@@ -674,15 +660,14 @@ export class ClassDB implements DiagramDB {
`stroke: ${config.themeVariables.noteBorderColor}`,
],
look: config.look,
parentId: note.parent,
};
nodes.push(noteNode);
const noteClassId = this.classes.get(note.class)?.id;
const noteClassId = this.classes.get(note.class)?.id ?? '';
if (noteClassId) {
const edge: Edge = {
id: `edgeNote${note.index}`,
id: `edgeNote${cnt}`,
start: note.id,
end: noteClassId,
type: 'normal',
@@ -712,7 +697,7 @@ export class ClassDB implements DiagramDB {
nodes.push(interfaceNode);
}
let cnt = 0;
cnt = 0;
for (const classRelation of this.relations) {
cnt++;
const edge: Edge = {

View File

@@ -417,7 +417,7 @@ class C13["With Città foreign language"]
note "This is a keyword: ${keyword}. It truly is."
`;
parser.parse(str);
expect(classDb.getNote(0).text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
expect(classDb.getNotes()[0].text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
});
it.each(keywords)(
@@ -427,7 +427,7 @@ class C13["With Città foreign language"]
note "${keyword}"`;
parser.parse(str);
expect(classDb.getNote(0).text).toEqual(`${keyword}`);
expect(classDb.getNotes()[0].text).toEqual(`${keyword}`);
}
);
@@ -441,7 +441,7 @@ class C13["With Città foreign language"]
`;
parser.parse(str);
expect(classDb.getNote(0).text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
expect(classDb.getNotes()[0].text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
});
it.each(keywords)(
@@ -456,7 +456,7 @@ class C13["With Città foreign language"]
`;
parser.parse(str);
expect(classDb.getNote(0).text).toEqual(`${keyword}`);
expect(classDb.getNotes()[0].text).toEqual(`${keyword}`);
}
);

View File

@@ -8,7 +8,7 @@ import utils, { getEdgeId } from '../../utils.js';
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import common from '../common/common.js';
import type { ClassRelation, ClassMap, ClassNoteMap, NamespaceMap } from './classTypes.js';
import type { ClassRelation, ClassNote, ClassMap, NamespaceMap } from './classTypes.js';
import type { EdgeData } from '../../types.js';
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());
@@ -65,9 +65,6 @@ export const addNamespaces = function (
g.setNode(vertex.id, node);
addClasses(vertex.classes, g, _id, diagObj, vertex.id);
const classes: ClassMap = diagObj.db.getClasses();
const relations: ClassRelation[] = diagObj.db.getRelations();
addNotes(vertex.notes, g, relations.length + 1, classes, vertex.id);
log.info('setNode', node);
});
@@ -147,74 +144,69 @@ export const addClasses = function (
* @param classes - Classes
*/
export const addNotes = function (
notes: ClassNoteMap,
notes: ClassNote[],
g: graphlib.Graph,
startEdgeId: number,
classes: ClassMap,
parent?: string
classes: ClassMap
) {
log.info(notes);
[...notes.values()]
.filter((note) => note.parent === parent)
.forEach(function (vertex) {
const cssNoteStr = '';
notes.forEach(function (note, i) {
const vertex = note;
const styles = { labelStyle: '', style: '' };
const cssNoteStr = '';
const vertexText = vertex.text;
const styles = { labelStyle: '', style: '' };
const radius = 0;
const shape = 'note';
const node = {
labelStyle: styles.labelStyle,
shape: shape,
labelText: sanitizeText(vertexText),
noteData: vertex,
rx: radius,
ry: radius,
class: cssNoteStr,
style: styles.style,
id: vertex.id,
domId: vertex.id,
tooltip: '',
type: 'note',
// TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release
padding: getConfig().flowchart?.padding ?? getConfig().class?.padding,
};
g.setNode(vertex.id, node);
log.info('setNode', node);
const vertexText = vertex.text;
if (parent) {
g.setParent(vertex.id, parent);
}
const radius = 0;
const shape = 'note';
const node = {
labelStyle: styles.labelStyle,
shape: shape,
labelText: sanitizeText(vertexText),
noteData: vertex,
rx: radius,
ry: radius,
class: cssNoteStr,
style: styles.style,
id: vertex.id,
domId: vertex.id,
tooltip: '',
type: 'note',
// TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release
padding: getConfig().flowchart?.padding ?? getConfig().class?.padding,
};
g.setNode(vertex.id, node);
log.info('setNode', node);
if (!vertex.class || !classes.has(vertex.class)) {
return;
}
const edgeId = startEdgeId + vertex.index;
if (!vertex.class || !classes.has(vertex.class)) {
return;
}
const edgeId = startEdgeId + i;
const edgeData: EdgeData = {
id: `edgeNote${edgeId}`,
//Set relationship style and line type
classes: 'relation',
pattern: 'dotted',
// Set link type for rendering
arrowhead: 'none',
//Set edge extra labels
startLabelRight: '',
endLabelLeft: '',
//Set relation arrow types
arrowTypeStart: 'none',
arrowTypeEnd: 'none',
style: 'fill:none',
labelStyle: '',
curve: interpolateToCurve(conf.curve, curveLinear),
};
const edgeData: EdgeData = {
id: `edgeNote${edgeId}`,
//Set relationship style and line type
classes: 'relation',
pattern: 'dotted',
// Set link type for rendering
arrowhead: 'none',
//Set edge extra labels
startLabelRight: '',
endLabelLeft: '',
//Set relation arrow types
arrowTypeStart: 'none',
arrowTypeEnd: 'none',
style: 'fill:none',
labelStyle: '',
curve: interpolateToCurve(conf.curve, curveLinear),
};
// Add the edge to the graph
g.setEdge(vertex.id, vertex.class, edgeData, edgeId);
});
// Add the edge to the graph
g.setEdge(vertex.id, vertex.class, edgeData, edgeId);
});
};
/**
@@ -337,7 +329,7 @@ export const draw = async function (text: string, id: string, _version: string,
const namespaces: NamespaceMap = diagObj.db.getNamespaces();
const classes: ClassMap = diagObj.db.getClasses();
const relations: ClassRelation[] = diagObj.db.getRelations();
const notes: ClassNoteMap = diagObj.db.getNotes();
const notes: ClassNote[] = diagObj.db.getNotes();
log.info(relations);
addNamespaces(namespaces, g, id, diagObj);
addClasses(classes, g, id, diagObj);

View File

@@ -206,7 +206,7 @@ export const draw = function (text, id, _version, diagObj) {
);
});
const notes = diagObj.db.getNotes().values();
const notes = diagObj.db.getNotes();
notes.forEach(function (note) {
log.debug(`Adding note: ${JSON.stringify(note)}`);
const node = svgDraw.drawNote(diagram, note, conf, diagObj);

View File

@@ -5,7 +5,7 @@ export interface ClassNode {
id: string;
type: string;
label: string;
shape: 'classBox';
shape: string;
text: string;
cssClasses: string;
methods: ClassMember[];
@@ -149,8 +149,6 @@ export interface ClassNote {
id: string;
class: string;
text: string;
index: number;
parent?: string;
}
export interface ClassRelation {
@@ -179,7 +177,6 @@ export interface NamespaceNode {
id: string;
domId: string;
classes: ClassMap;
notes: ClassNoteMap;
children: NamespaceMap;
}
@@ -190,5 +187,4 @@ export interface StyleClass {
}
export type ClassMap = Map<string, ClassNode>;
export type ClassNoteMap = Map<string, ClassNote>;
export type NamespaceMap = Map<string, NamespaceNode>;

View File

@@ -275,8 +275,8 @@ statement
;
namespaceStatement
: namespaceIdentifier STRUCT_START classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3[0], $3[1]); }
| namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4[0], $4[1]); }
: namespaceIdentifier STRUCT_START classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); }
| namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); }
;
namespaceIdentifier
@@ -284,12 +284,9 @@ namespaceIdentifier
;
classStatements
: classStatement {$$=[[$1], []]}
| classStatement NEWLINE {$$=[[$1], []]}
| classStatement NEWLINE classStatements {$3[0].unshift($1); $$=$3}
| noteStatement {$$=[[], [$1]]}
| noteStatement NEWLINE {$$=[[], [$1]]}
| noteStatement NEWLINE classStatements {$3[1].unshift($1); $$=$3}
: classStatement {$$=[$1]}
| classStatement NEWLINE {$$=[$1]}
| classStatement NEWLINE classStatements {$3.unshift($1); $$=$3}
;
classStatement
@@ -336,8 +333,8 @@ relationStatement
;
noteStatement
: NOTE_FOR className noteText { $$ = yy.addNote($3, $2); }
| NOTE noteText { $$ = yy.addNote($2); }
: NOTE_FOR className noteText { yy.addNote($3, $2); }
| NOTE noteText { yy.addNote($2); }
;
classDefStatement

View File

@@ -13,30 +13,6 @@ const getStyles = (options) =>
}
.cluster-label text {
fill: ${options.titleColor};
}
.cluster-label span {
color: ${options.titleColor};
}
.cluster-label span p {
background-color: transparent;
}
.cluster rect {
fill: ${options.clusterBkg};
stroke: ${options.clusterBorder};
stroke-width: 1px;
}
.cluster text {
fill: ${options.titleColor};
}
.cluster span {
color: ${options.titleColor};
}
.nodeLabel, .edgeLabel {
color: ${options.classText};
}

View File

@@ -70,31 +70,6 @@ describe('Sanitize text', () => {
});
expect(result).not.toContain('javascript:alert(1)');
});
it('should allow HTML tags in sandbox mode', () => {
const htmlStr = '<p>This is a <strong>bold</strong> text</p>';
const result = sanitizeText(htmlStr, {
securityLevel: 'sandbox',
flowchart: { htmlLabels: true },
});
expect(result).toContain('<p>');
expect(result).toContain('<strong>');
expect(result).toContain('</strong>');
expect(result).toContain('</p>');
});
it('should remove script tags in sandbox mode', () => {
const maliciousStr = '<p>Hello <script>alert(1)</script> world</p>';
const result = sanitizeText(maliciousStr, {
securityLevel: 'sandbox',
flowchart: { htmlLabels: true },
});
expect(result).not.toContain('<script>');
expect(result).not.toContain('alert(1)');
expect(result).toContain('<p>');
expect(result).toContain('Hello');
expect(result).toContain('world');
});
});
describe('generic parser', () => {

View File

@@ -66,7 +66,7 @@ export const removeScript = (txt: string): string => {
const sanitizeMore = (text: string, config: MermaidConfig) => {
if (config.flowchart?.htmlLabels !== false) {
const level = config.securityLevel;
if (level === 'antiscript' || level === 'strict' || level === 'sandbox') {
if (level === 'antiscript' || level === 'strict') {
text = removeScript(text);
} else if (level !== 'loose') {
text = breakToPlaceholder(text);
@@ -333,7 +333,7 @@ const renderKatexUnsanitized = async (text: string, config: MermaidConfig): Prom
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
}
if (injected.includeLargeFeatures) {
if (includeLargeFeatures) {
const { default: katex } = await import('katex');
const outputMode =
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)

View File

@@ -66,15 +66,12 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
\}\| return 'ONE_OR_MORE';
"one" return 'ONLY_ONE';
"only one" return 'ONLY_ONE';
[0-9]+\.[0-9]+ return 'DECIMAL_NUM';
"1"(?=\s+[A-Za-z_"']) return 'ONLY_ONE';
"1" return 'ENTITY_ONE';
[0-9]+ return 'NUM';
"1" return 'ONLY_ONE';
\|\| return 'ONLY_ONE';
o\| return 'ZERO_OR_ONE';
o\{ return 'ZERO_OR_MORE';
\|\{ return 'ONE_OR_MORE';
u(?=[\.\-\|]) return 'MD_PARENT';
\s*u return 'MD_PARENT';
\.\. return 'NON_IDENTIFYING';
\-\- return 'IDENTIFYING';
"to" return 'IDENTIFYING';
@@ -83,15 +80,13 @@ u(?=[\.\-\|]) return 'MD_PARENT';
\-\. return 'NON_IDENTIFYING';
<style>([^\x00-\x7F]|\w|\-|\*)+ return 'STYLE_TEXT';
<style>';' return 'SEMI';
([^\x00-\x7F]|\w|\-|\*|\.)+ return 'UNICODE_TEXT';
([^\x00-\x7F]|\w|\-|\*)+ return 'UNICODE_TEXT';
[0-9] return 'NUM';
. return yytext[0];
<<EOF>> return 'EOF';
/lex
%left 'ONLY_ONE'
%left 'ZERO_OR_ONE' 'ZERO_OR_MORE' 'ONE_OR_MORE' 'MD_PARENT'
%start start
%% /* language grammar */
@@ -233,9 +228,6 @@ styleComponent: STYLE_TEXT | NUM | COLON | BRKT;
entityName
: 'ENTITY_NAME' { $$ = $1.replace(/"/g, ''); }
| 'UNICODE_TEXT' { $$ = $1; }
| 'NUM' { $$ = $1; }
| 'DECIMAL_NUM' { $$ = $1; }
| 'ENTITY_ONE' { $$ = $1; }
;
attributes

View File

@@ -1001,90 +1001,4 @@ describe('when parsing ER diagram it...', function () {
}
);
});
describe('syntax fixes for special characters and numbers', function () {
describe('standalone entity names', function () {
it('should allow number "1" as standalone entity', function () {
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n1`);
});
it('should allow character "u" as standalone entity', function () {
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\nu`);
});
it('should allow decimal numbers as standalone entities', function () {
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n2.5`);
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n1.5`);
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n0.1`);
erDiagram.parser.parse(`erDiagram\nCUSTOMER }|..|{ DELIVERY-ADDRESS : has\n99.99`);
});
});
describe('entity names with attributes', function () {
it('should allow "u" as entity name with attributes', function () {
erDiagram.parser.parse(`erDiagram\nu {\nstring name\nint id\n}`);
});
it('should allow number "1" as entity name with attributes', function () {
erDiagram.parser.parse(`erDiagram\n1 {\nstring name\nint id\n}`);
});
it('should allow decimal numbers as entity names with attributes', function () {
erDiagram.parser.parse(`erDiagram\n2.5 {\nstring name\nint id\n}`);
erDiagram.parser.parse(`erDiagram\n1.5 {\nstring value\n}`);
});
});
describe('entity names in relationships', function () {
it('should allow "u" in relationships', function () {
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| u : has`);
erDiagram.parser.parse(`erDiagram\nu ||--|| ORDER : places`);
erDiagram.parser.parse(`erDiagram\nu ||--|| v : connects`);
});
it('should allow numbers in relationships', function () {
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| 1 : has`);
erDiagram.parser.parse(`erDiagram\n1 ||--|| ORDER : places`);
erDiagram.parser.parse(`erDiagram\n1 ||--|| 2 : connects`);
});
it('should allow decimal numbers in relationships', function () {
erDiagram.parser.parse(`erDiagram\nCUSTOMER ||--|| 2.5 : has`);
erDiagram.parser.parse(`erDiagram\n1.5 ||--|| ORDER : places`);
erDiagram.parser.parse(`erDiagram\n2.5 ||--|| 5.5 : connects`);
});
});
describe('mixed scenarios', function () {
it('should handle complex diagram with special entity names', function () {
erDiagram.parser.parse(
`erDiagram
CUSTOMER ||--o{ 1 : places
1 ||--|{ u : contains
u {
string name
int quantity
}
"2.5" ||--|| ORDER : processes
ORDER {
int id
date created
}
`
);
});
it('should handle attributes with numbers in names (but not starting)', function () {
erDiagram.parser.parse(
`erDiagram
ENTITY {
string name1
int value2
float point3_5
}
`
);
});
});
});
});

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