Compare commits

..

2 Commits

Author SHA1 Message Date
Sidharth Vinod
e2df3525de Merge branch 'develop' into update-browserslist 2024-08-24 15:40:47 +05:30
cmmoran
035954c9ed chore: update browsers list 2024-08-23 17:58:12 +02:00
231 changed files with 2006 additions and 13545 deletions

View File

@@ -25,7 +25,6 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
'sankey',
'block',
'packet',
'architecture',
] as const;
/**

View File

@@ -26,7 +26,6 @@ concat
controlx
controly
CSSCLASS
curv
CYLINDEREND
CYLINDERSTART
DAGA
@@ -56,7 +55,6 @@ GENERICTYPE
getBoundarys
grammr
graphtype
halign
iife
interp
introdcued
@@ -68,7 +66,6 @@ Kaufmann
keyify
LABELPOS
LABELTYPE
layoutstop
lcov
LEFTOF
Lexa
@@ -141,7 +138,6 @@ tsdoc
typeof
typestr
unshift
urlsafe
verifymethod
VERIFYMTHD
WARN_DOCSDIR_DOESNT_MATCH

View File

@@ -28,9 +28,6 @@ dictionaryDefinitions:
- name: suggestions
words:
- none
- disp
- subproc
- tria
suggestWords:
- seperator:separator
- vertice:vertex

View File

@@ -24,13 +24,11 @@ Doctave
DokuWiki
dompurify
elkjs
fcose
fontawesome
Foswiki
Gitea
graphlib
Grav
icones
iconify
Inkdrop
jiti

View File

@@ -1,11 +1,9 @@
Adamiecki
arrowend
Bendpoints
bmatrix
braintree
catmull
compositTitleSize
curv
doublecircle
elems
gantt
@@ -25,7 +23,6 @@ multigraph
nodesep
NOTEGROUP
Pinterest
procs
rankdir
ranksep
rect

View File

@@ -4,4 +4,3 @@ handDrawn
KOEPF
neato
newbranch
validify

5
.github/lychee.toml vendored
View File

@@ -44,10 +44,7 @@ exclude = [
"https://chromewebstore.google.com",
# Drupal 403
"https://(www.)?drupal.org",
# Swimm returns 404, eventhough the link is valid
"https://docs.swimm.io"
"https://(www.)?drupal.org"
]
# Exclude all private IPs from checking.

View File

@@ -13,13 +13,13 @@ jobs:
autofix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
@@ -42,4 +42,4 @@ jobs:
working-directory: ./packages/mermaid
run: pnpm run docs:build
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c # main
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c

View File

@@ -18,12 +18,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@v4
- name: Check for difference in README.md and docs/README.md
run: |

View File

@@ -11,9 +11,6 @@ on:
- synchronize
- ready_for_review
permissions: # added using https://github.com/step-security/secure-repo
contents: read
jobs:
analyze:
name: Analyze
@@ -32,11 +29,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5
uses: github/codeql-action/init@v3
with:
config-file: ./.github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
@@ -48,7 +45,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@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5
uses: github/codeql-action/autobuild@v3
# 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 +59,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5
uses: github/codeql-action/analyze@v3

View File

@@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@v4
- name: 'Dependency Review'
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
uses: actions/dependency-review-action@v4

View File

@@ -32,13 +32,13 @@ jobs:
run: |
echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
@@ -54,7 +54,7 @@ jobs:
APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com'
- name: Cypress run
uses: cypress-io/github-action@d79d2d530a66e641eb4a5f227e13bc985c60b964 # v4.2.2
uses: cypress-io/github-action@v4
id: cypress
with:
start: pnpm run dev

View File

@@ -36,15 +36,15 @@ jobs:
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
options: --user 1001
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
- name: Cache snapshots
id: cache-snapshot
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
uses: actions/cache@v4
with:
save-always: true
path: ./cypress/snapshots
@@ -53,13 +53,13 @@ jobs:
# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
- name: Switch to base branch
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@v4
with:
ref: ${{ env.targetHash }}
- name: Install dependencies
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2
uses: cypress-io/github-action@v6
with:
# just perform install
runTests: false
@@ -82,26 +82,26 @@ jobs:
matrix:
containers: [1, 2, 3, 4]
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
# These cached snapshots are downloaded, providing the reference snapshots.
- name: Cache snapshots
id: cache-snapshot
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
uses: actions/cache/restore@v4
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
- name: Install dependencies
uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2
uses: cypress-io/github-action@v6
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@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2
uses: cypress-io/github-action@v6
id: cypress
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
# Otherwise (e.g. if running from fork), we run on a single container only
@@ -141,7 +141,7 @@ jobs:
ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
uses: codecov/codecov-action@v4
# Run step only pushes to develop and pull_requests
if: ${{ steps.cypress.conclusion == 'success' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop')}}
with:

View File

@@ -4,17 +4,11 @@ on:
issues:
types: [opened]
permissions: # added using https://github.com/step-security/secure-repo
contents: read
jobs:
triage:
permissions:
issues: write # for andymckay/labeler to label issues
pull-requests: write # for andymckay/labeler to label PRs
runs-on: ubuntu-latest
steps:
- uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4
- uses: andymckay/labeler@1.0.4
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
add-labels: 'Status: Triage'

View File

@@ -19,9 +19,6 @@ on:
# * is a special character in YAML so you have to quote this string
- cron: '30 8 * * *'
permissions: # added using https://github.com/step-security/secure-repo
contents: read
jobs:
link-checker:
runs-on: ubuntu-latest
@@ -29,17 +26,17 @@ jobs:
# lychee only uses the GITHUB_TOKEN to avoid rate-limiting
contents: read
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
- name: Restore lychee cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
uses: actions/cache@v4
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Link Checker
uses: lycheeverse/lychee-action@c053181aa0c3d17606addfe97a9075a32723548a # v1.9.3
uses: lycheeverse/lychee-action@v1.9.3
with:
args: >-
--config .github/lychee.toml

View File

@@ -12,24 +12,16 @@ permissions:
contents: write
jobs:
docker-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0
with:
verbose: true
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
@@ -89,7 +81,7 @@ jobs:
continue-on-error: ${{ github.event_name == 'push' }}
run: pnpm run docs:verify
- uses: testomatio/check-tests@0ea638fcec1820cf2e7b9854fdbdd04128a55bd4 # stable
- uses: testomatio/check-tests@stable
with:
framework: cypress
tests: './cypress/e2e/**/**.spec.js'

View File

@@ -22,7 +22,7 @@ jobs:
pull-requests: write # write permission is required to label PRs
steps:
- name: Label PR
uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348 # v6.0.0
uses: release-drafter/release-drafter@v6
with:
config-name: pr-labeler.yml
disable-autolabeler: false

View File

@@ -23,12 +23,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
@@ -37,13 +37,13 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Setup Pages
uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4.0.0
uses: actions/configure-pages@v4
- name: Run Build
run: pnpm --filter mermaid run docs:build:vitepress
- name: Upload artifact
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
uses: actions/upload-pages-artifact@v3
with:
path: packages/mermaid/src/vitepress/.vitepress/dist
@@ -56,4 +56,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
uses: actions/deploy-pages@v4

View File

@@ -9,14 +9,14 @@ jobs:
publish-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
@@ -28,7 +28,7 @@ jobs:
CYPRESS_CACHE_FOLDER: .cache/Cypress
- name: Install Json
run: npm i json@11.0.0 --global
run: npm i json --global
- name: Publish
working-directory: ./packages/mermaid

View File

@@ -1,43 +0,0 @@
name: Preview release
on:
pull_request:
branches: [develop]
types: [opened, synchronize, labeled, ready_for_review]
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}
cancel-in-progress: true
permissions:
contents: read
actions: write
jobs:
preview:
if: ${{ github.repository_owner == 'mermaid-js' }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
issues: write
pull-requests: write
name: Publish preview release
timeout-minutes: 5
steps:
- name: Checkout Repo
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
cache: pnpm
node-version-file: '.node-version'
- name: Install Packages
run: pnpm install --frozen-lockfile
- name: Publish packages
run: pnpx pkg-pr-new publish --pnpm './packages/*'

View File

@@ -7,26 +7,18 @@ on:
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions: # added using https://github.com/step-security/secure-repo
contents: read
jobs:
release:
if: github.repository == 'mermaid-js/mermaid'
permissions:
contents: write # to create release (changesets/action)
id-token: write # OpenID Connect token needed for provenance
pull-requests: write # to create pull request (changesets/action)
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
uses: actions/checkout@v3
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
@@ -36,7 +28,7 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@aba318e9165b45b7948c60273e0b72fce0a64eb9 # v1.4.7
uses: changesets/action@v1
with:
version: pnpm changeset:version
publish: pnpm changeset:publish

View File

@@ -1,37 +0,0 @@
name: Scorecard supply-chain security
on:
branch_protection_rule:
push:
branches:
- develop
schedule:
- cron: 29 15 * * 0
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
permissions:
id-token: write
security-events: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload artifact
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@f0f3afee809481da311ca3a6ff1ff51d81dbeb24 # v3.26.4
with:
sarif_file: results.sarif

View File

@@ -9,13 +9,13 @@ jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
@@ -38,12 +38,8 @@ jobs:
run: |
pnpm exec vitest run ./packages/mermaid/src/diagrams/gantt/ganttDb.spec.ts --coverage
- name: Verify out-of-tree build with TypeScript
run: |
pnpm test:check:tsc
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
uses: codecov/codecov-action@v4
# Run step only pushes to develop and pull_requests
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }}
with:

View File

@@ -8,6 +8,6 @@ jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: Dunning-Kruger/unlock-issues@b06b7f7e5c3f2eaa1c6d5d89f40930e4d6d9699e # v1
- uses: Dunning-Kruger/unlock-issues@v1
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'

View File

@@ -8,18 +8,18 @@ jobs:
update-browser-list:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- run: npx update-browserslist-db@latest
- name: Commit changes
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
uses: EndBug/add-and-commit@v9
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: 'chore: update browsers list'
push: false
- name: Create Pull Request
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
uses: peter-evans/create-pull-request@v6
with:
branch: update-browserslist
title: Update Browserslist

View File

@@ -1,2 +0,0 @@
ignored:
- DL3002 # TODO: Last USER should not be root

View File

@@ -1,2 +1,4 @@
#!/usr/bin/env sh
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit

View File

@@ -1,13 +1,2 @@
FROM node:20.12.2-alpine3.19@sha256:7a91aa397f2e2dfbfcdad2e2d72599f374e0b0172be1d86eeb73f1d33f36a4b2
USER 0:0
RUN corepack enable \
&& corepack enable pnpm
RUN apk add --no-cache git~=2.43.4 \
&& git config --add --system safe.directory /mermaid
ENV NODE_OPTIONS="--max_old_space_size=8192"
EXPOSE 9000 3333
FROM node:20.12.2-alpine3.19 AS base
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh -

View File

@@ -35,8 +35,7 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
[![NPM Downloads](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid)
[![Join our Discord!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=discord&label=discord)](https://discord.gg/AgrbSrBer3)
[![Twitter Follow](https://img.shields.io/badge/Social-mermaidjs__-blue?style=social&logo=X)](https://twitter.com/mermaidjs_)
[![Covered by Argos Visual Testing](https://argos-ci.com/badge.svg)](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/mermaid-js/mermaid/badge)](https://securityscorecards.dev/viewer/?uri=github.com/mermaid-js/mermaid)
[![Covered by Argos Visual Testing](https://argos-ci.com/badge.svg)](https://argos-ci.com)
<img src="./img/header.png" alt="" />
@@ -83,10 +82,6 @@ You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](https://mermaid.js.org/intro/getting-started.html), [Usage](https://mermaid.js.org/config/usage.html) and [Tutorials](https://mermaid.js.org/ecosystem/tutorials.html).
Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze.
[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
<a href="https://applitools.com/">

View File

@@ -73,7 +73,7 @@ export const imgSnapshotTest = (
export const urlSnapshotTest = (
url: string,
options: CypressMermaidConfig = {},
options: CypressMermaidConfig,
_api = false,
validation?: any
): void => {

View File

@@ -1,180 +0,0 @@
import { imgSnapshotTest, urlSnapshotTest } from '../../helpers/util.ts';
describe.skip('architecture diagram', () => {
it('should render a simple architecture diagram with groups', () => {
imgSnapshotTest(
`architecture-beta
group api(cloud)[API]
service db(database)[Database] in api
service disk1(disk)[Storage] in api
service disk2(disk)[Storage] in api
service server(server)[Server] in api
service gateway(internet)[Gateway]
db L--R server
disk1 T--B server
disk2 T--B db
server T--B gateway
`
);
});
it('should render an architecture diagram with groups within groups', () => {
imgSnapshotTest(
`architecture-beta
group api[API]
group public[Public API] in api
group private[Private API] in api
service serv1(server)[Server] in public
service serv2(server)[Server] in private
service db(database)[Database] in private
service gateway(internet)[Gateway] in api
serv1 B--T serv2
serv2 L--R db
serv1 L--R gateway
`
);
});
it('should render an architecture diagram with the fallback icon', () => {
imgSnapshotTest(
`architecture-beta
service unknown(iconnamedoesntexist)[Unknown Icon]
`
);
});
it('should render an architecture diagram with split directioning', () => {
imgSnapshotTest(
`architecture-beta
service db(database)[Database]
service s3(disk)[Storage]
service serv1(server)[Server 1]
service serv2(server)[Server 2]
service disk(disk)[Disk]
db L--R s3
serv1 L--T s3
serv2 L--B s3
serv1 T--B disk
`
);
});
it('should render an architecture diagram with directional arrows', () => {
imgSnapshotTest(
`architecture-beta
service servC(server)[Server 1]
service servL(server)[Server 2]
service servR(server)[Server 3]
service servT(server)[Server 4]
service servB(server)[Server 5]
servC (L--R) servL
servC (R--L) servR
servC (T--B) servT
servC (B--T) servB
servL (T--L) servT
servL (B--L) servB
servR (T--R) servT
servR (B--R) servB
`
);
});
it('should render an architecture diagram with group edges', () => {
imgSnapshotTest(
`architecture-beta
group left_group(cloud)[Left]
group right_group(cloud)[Right]
group top_group(cloud)[Top]
group bottom_group(cloud)[Bottom]
group center_group(cloud)[Center]
service left_disk(disk)[Disk] in left_group
service right_disk(disk)[Disk] in right_group
service top_disk(disk)[Disk] in top_group
service bottom_disk(disk)[Disk] in bottom_group
service center_disk(disk)[Disk] in center_group
left_disk{group} (R--L) center_disk{group}
right_disk{group} (L--R) center_disk{group}
top_disk{group} (B--T) center_disk{group}
bottom_disk{group} (T--B) center_disk{group}
`
);
});
it('should render an architecture diagram with edge labels', () => {
imgSnapshotTest(
`architecture-beta
service servC(server)[Server 1]
service servL(server)[Server 2]
service servR(server)[Server 3]
service servT(server)[Server 4]
service servB(server)[Server 5]
servC L-[Label]-R servL
servC R-[Label]-L servR
servC T-[Label]-B servT
servC B-[Label]-T servB
servL T-[Label]-L servT
servL B-[Label]-L servB
servR T-[Label]-R servT
servR B-[Label]-R servB
`
);
});
it('should render an architecture diagram with simple junction edges', () => {
imgSnapshotTest(
`architecture-beta
service left_disk(disk)[Disk]
service top_disk(disk)[Disk]
service bottom_disk(disk)[Disk]
service top_gateway(internet)[Gateway]
service bottom_gateway(internet)[Gateway]
junction juncC
junction juncR
left_disk R--L juncC
top_disk B--T juncC
bottom_disk T--B juncC
juncC R--L juncR
top_gateway B--T juncR
bottom_gateway T--B juncR
`
);
});
it('should render an architecture diagram with complex junction edges', () => {
imgSnapshotTest(
`architecture-beta
group left
group right
service left_disk(disk)[Disk] in left
service top_disk(disk)[Disk] in left
service bottom_disk(disk)[Disk] in left
service top_gateway(internet)[Gateway] in right
service bottom_gateway(internet)[Gateway] in right
junction juncC in left
junction juncR in right
left_disk R--L juncC
top_disk B--T juncC
bottom_disk T--B juncC
top_gateway (B--T juncR
bottom_gateway (T--B juncR
juncC{group} R--L) juncR{group}
`
);
});
});
describe('architecture - external', () => {
it('should allow adding external icons', () => {
urlSnapshotTest('http://localhost:9000/architecture-external.html');
});
});

View File

@@ -581,63 +581,4 @@ class C13["With Città foreign language"]
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
});
it('renders a class diagram with a generic class in a namespace', () => {
const diagramDefinition = `
classDiagram-v2
namespace Company.Project.Module {
class GenericClass~T~ {
+addItem(item: T)
+getItem() T
}
}
`;
imgSnapshotTest(diagramDefinition);
});
it('renders a class diagram with nested namespaces and relationships', () => {
const diagramDefinition = `
classDiagram-v2
namespace Company.Project.Module.SubModule {
class Report {
+generatePDF(data: List)
+generateCSV(data: List)
}
}
namespace Company.Project.Module {
class Admin {
+generateReport()
}
}
Admin --> Report : generates
`;
imgSnapshotTest(diagramDefinition);
});
it('renders a class diagram with multiple classes and relationships in a namespace', () => {
const diagramDefinition = `
classDiagram-v2
namespace Company.Project.Module {
class User {
+login(username: String, password: String)
+logout()
}
class Admin {
+addUser(user: User)
+removeUser(user: User)
+generateReport()
}
class Report {
+generatePDF(reportData: List)
+generateCSV(reportData: List)
}
}
Admin --> User : manages
Admin --> Report : generates
`;
imgSnapshotTest(diagramDefinition);
});
});

View File

@@ -321,37 +321,4 @@ ORDER ||--|{ LINE-ITEM : contains
{ logLevel: 1 }
);
});
it('should render relationship labels with line breaks', () => {
imgSnapshotTest(
`
erDiagram
p[Person] {
string firstName
string lastName
}
a["Customer Account"] {
string email
}
b["Customer Account Secondary"] {
string email
}
c["Customer Account Tertiary"] {
string email
}
d["Customer Account Nth"] {
string email
}
p ||--o| a : "has<br />one"
p ||--o| b : "has<br />one<br />two"
p ||--o| c : "has<br />one<br/>two<br />three"
p ||--o| d : "has<br />one<br />two<br/>three<br />...<br/>Nth"
`,
{ logLevel: 1 }
);
});
});

View File

@@ -1,142 +0,0 @@
import { imgSnapshotTest } from '../../helpers/util.ts';
const aliasSet1 = ['process', 'rect', 'proc', 'rectangle'] as const;
const aliasSet2 = ['event', 'rounded'] as const;
const aliasSet3 = ['stadium', 'pill', 'terminal'] as const;
const aliasSet4 = ['fr-rect', 'subproc', 'subprocess', 'framed-rectangle', 'subroutine'] as const;
const aliasSet5 = ['db', 'database', 'cylinder', 'cyl'] as const;
const aliasSet6 = ['diam', 'decision', 'diamond'] as const;
const aliasSet7 = ['hex', 'hexagon', 'prepare'] as const;
const aliasSet8 = ['lean-r', 'lean-right', 'in-out'] as const;
const aliasSet9 = ['lean-l', 'lean-left', 'out-in'] as const;
const aliasSet10 = ['trap-b', 'trapezoid-bottom', 'priority'] as const;
const aliasSet11 = ['trap-t', 'trapezoid-top', 'manual'] as const;
const aliasSet12 = ['dbl-circ', 'double-circle'] as const;
const aliasSet13 = ['notched-rectangle', 'card', 'notch-rect'] as const;
const aliasSet14 = [
'lin-rect',
'lined-rectangle',
'lin-proc',
'lined-process',
'shaded-process',
] as const;
const aliasSet15 = ['sm-circ', 'small-circle', 'start'] as const;
const aliasSet16 = ['fr-circ', 'framed-circle', 'stop'] as const;
const aliasSet17 = ['fork', 'join'] as const;
// brace-r', 'braces'
const aliasSet18 = ['comment', 'brace-l'] as const;
const aliasSet19 = ['bolt', 'com-link', 'lightning-bolt'] as const;
const aliasSet20 = ['doc', 'document'] as const;
const aliasSet21 = ['delay', 'half-rounded-rectangle'] as const;
const aliasSet22 = ['h-cyl', 'das', 'horizontal-cylinder'] as const;
const aliasSet23 = ['lin-cyl', 'disk', 'lined-cylinder'] as const;
const aliasSet24 = ['curv-trap', 'display', 'curved-trapezoid'] as const;
const aliasSet25 = ['div-rect', 'div-proc', 'divided-rectangle', 'divided-process'] as const;
const aliasSet26 = ['extract', 'tri', 'triangle'] as const;
const aliasSet27 = ['win-pane', 'internal-storage', 'window-pane'] as const;
const aliasSet28 = ['f-circ', 'junction', 'filled-circle'] as const;
const aliasSet29 = ['lin-doc', 'lined-document'] as const;
const aliasSet30 = ['notch-pent', 'loop-limit', 'notched-pentagon'] as const;
const aliasSet31 = ['flip-tri', 'manual-file', 'flipped-triangle'] as const;
const aliasSet32 = ['sl-rect', 'manual-input', 'sloped-rectangle'] as const;
const aliasSet33 = ['docs', 'documents', 'st-doc', 'stacked-document'] as const;
const aliasSet34 = ['procs', 'processes', 'st-rect', 'stacked-rectangle'] as const;
const aliasSet35 = ['flag', 'paper-tape'] as const;
const aliasSet36 = ['bow-rect', 'stored-data', 'bow-tie-rectangle'] as const;
const aliasSet37 = ['cross-circ', 'summary', 'crossed-circle'] as const;
const aliasSet38 = ['tag-doc', 'tagged-document'] as const;
const aliasSet39 = ['tag-rect', 'tag-proc', 'tagged-rectangle', 'tagged-process'] as const;
const aliasSet40 = ['collate', 'hourglass'] as const;
// Aggregate all alias sets into a single array
const aliasSets = [
aliasSet1,
aliasSet2,
aliasSet3,
aliasSet4,
aliasSet5,
aliasSet6,
aliasSet7,
aliasSet8,
aliasSet9,
aliasSet10,
aliasSet11,
aliasSet12,
aliasSet13,
aliasSet14,
aliasSet15,
aliasSet16,
aliasSet17,
aliasSet18,
aliasSet19,
aliasSet20,
aliasSet21,
aliasSet22,
aliasSet23,
aliasSet24,
aliasSet25,
aliasSet26,
aliasSet27,
aliasSet28,
aliasSet29,
aliasSet30,
aliasSet31,
aliasSet32,
aliasSet33,
aliasSet34,
aliasSet35,
aliasSet36,
aliasSet37,
aliasSet38,
aliasSet39,
] as const;
aliasSets.forEach((aliasSet) => {
describe(`Test ${aliasSet.join(',')} `, () => {
it(`All ${aliasSet.join(',')} should render same shape`, () => {
let flowchartCode = `flowchart \n`;
aliasSet.forEach((alias, index) => {
flowchartCode += ` n${index}@{ shape: ${alias}, label: "${alias}" }\n`;
});
imgSnapshotTest(flowchartCode);
});
});
});

View File

@@ -786,7 +786,7 @@ A ~~~ B
`---
title: Subgraph nodeSpacing and rankSpacing example
config:
flowchart:
flowchart:
nodeSpacing: 250
rankSpacing: 250
---
@@ -1052,28 +1052,5 @@ end
}
);
});
it('Should render self-loops', () => {
imgSnapshotTest(
`flowchart
A --> A
subgraph B
B1 --> B1
end
subgraph C
subgraph C1
C2 --> C2
subgraph D
D1 --> D1
end
D --> D
end
C1 --> C1
end
`,
{
flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } },
}
);
});
});
});

View File

@@ -1,126 +0,0 @@
import { imgSnapshotTest } from '../../helpers/util';
const looks = ['classic', 'handDrawn'] as const;
const directions = [
'TB',
//'BT',
'LR',
// 'RL'
] as const;
const forms = [undefined, 'square', 'circle', 'rounded'] as const;
const labelPos = [undefined, 't', 'b'] as const;
looks.forEach((look) => {
directions.forEach((direction) => {
forms.forEach((form) => {
labelPos.forEach((pos) => {
describe(`Test iconShape in ${form ? `${form} form,` : ''} ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => {
it(`without label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is a label for icon shape'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with very long label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is a very very very very very long long long label for icon shape'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:true`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold** </br>and <strong>strong</strong> for icon shape'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:false`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold** </br>and <strong>strong</strong> for icon shape'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, {
look,
htmlLabels: false,
flowchart: { htmlLabels: false },
});
});
it(`with styles`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
flowchartCode += ` style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with classDef`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`;
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`;
if (form) {
flowchartCode += `, form: '${form}'`;
}
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
flowchartCode += ` nAA:::customClazz\n`;
imgSnapshotTest(flowchartCode, { look });
});
});
});
});
});
});
describe('Test iconShape with different h', () => {
it('with different h', () => {
let flowchartCode = `flowchart TB\n`;
const icon = 'fa:bell';
const iconHeight = 64;
flowchartCode += ` nA --> nAA@{ icon: '${icon}', label: 'icon with different h', h: ${iconHeight} }\n`;
imgSnapshotTest(flowchartCode);
});
});

View File

@@ -1,103 +0,0 @@
import { imgSnapshotTest } from '../../helpers/util';
const looks = ['classic', 'handDrawn'] as const;
const directions = [
'TB',
//'BT',
'LR',
// 'RL'
] as const;
const labelPos = [undefined, 't', 'b'] as const;
looks.forEach((look) => {
directions.forEach((direction) => {
labelPos.forEach((pos) => {
describe(`Test imageShape in ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => {
it(`without label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', w: '100', h: '100' }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is a label for image shape'`;
flowchartCode += `, w: '100', h: '200'`;
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with very long label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is a very very very very very long long long label for image shape'`;
flowchartCode += `, w: '100', h: '250'`;
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:true`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is **bold** </br>and <strong>strong</strong> for image shape'`;
flowchartCode += `, w: '550', h: '200'`;
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, { look, htmlLabels: true });
});
it(`with markdown htmlLabels:false`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is **bold** </br>and <strong>strong</strong> for image shape'`;
flowchartCode += `, w: '250', h: '200'`;
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
imgSnapshotTest(flowchartCode, {
look,
htmlLabels: false,
flowchart: { htmlLabels: false },
});
});
it(`with styles`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'new image shape'`;
flowchartCode += `, w: '550', h: '200'`;
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
flowchartCode += ` style A fill:#f9f,stroke:#333,stroke-width:4px \n`;
imgSnapshotTest(flowchartCode, { look });
});
it(`with classDef`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#000000,stroke-dasharray: 5 5\n`;
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'new image shape'`;
flowchartCode += `, w: '500', h: '550'`;
if (pos) {
flowchartCode += `, pos: '${pos}'`;
}
flowchartCode += ` }\n`;
flowchartCode += ` A:::customClazz\n`;
imgSnapshotTest(flowchartCode, { look });
});
});
});
});
});

View File

@@ -1,146 +0,0 @@
import { imgSnapshotTest } from '../../helpers/util.ts';
const looks = ['classic', 'handDrawn'] as const;
const directions = [
'TB',
//'BT',
'LR',
//'RL'
] as const;
const newShapesSet1 = [
'triangle',
'sloped-rectangle',
'horizontal-cylinder',
'flipped-triangle',
'hourglass',
] as const;
const newShapesSet2 = [
'tagged-rectangle',
'documents',
'lightning-bolt',
'filled-circle',
'window-pane',
] as const;
const newShapesSet3 = [
'curved-trapezoid',
'bow-rect',
'tagged-document',
'divided-rectangle',
'crossed-circle',
] as const;
const newShapesSet4 = [
'document',
'notched-pentagon',
'lined-cylinder',
'stacked-document',
'half-rounded-rectangle',
] as const;
const newShapesSet5 = [
'lined-document',
'tagged-document',
'brace-l',
'comment',
'braces',
'brace-r',
] as const;
const newShapesSet6 = ['brace-r', 'braces'] as const;
// Aggregate all shape sets into a single array
const newShapesSets = [
newShapesSet1,
newShapesSet2,
newShapesSet3,
newShapesSet4,
newShapesSet5,
newShapesSet6,
];
looks.forEach((look) => {
directions.forEach((direction) => {
newShapesSets.forEach((newShapesSet) => {
describe(`Test ${newShapesSet.join(', ')} in ${look} look and dir ${direction}`, () => {
it(`without label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape} }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`connect all shapes with each other`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
});
for (let i = 0; i < newShapesSet.length; i++) {
for (let j = i + 1; j < newShapesSet.length; j++) {
flowchartCode += ` n${i}${i} --> n${j}${j}\n`;
}
}
if (!(direction === 'TB' && look === 'handDrawn' && newShapesSet === newShapesSet1)) {
//skip this test, works in real. Need to look
imgSnapshotTest(flowchartCode, { look });
}
});
it(`with very long label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a very very very very very long long long label for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:true`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:false`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, {
look,
htmlLabels: false,
flowchart: { htmlLabels: false },
});
});
it(`with styles`, () => {
let flowchartCode = `flowchart ${direction}\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with classDef`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`;
newShapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
flowchartCode += ` n${index}${index}:::customClazz\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
});
});
});
});

View File

@@ -1,107 +0,0 @@
import { imgSnapshotTest } from '../../helpers/util';
const looks = ['classic', 'handDrawn'] as const;
const directions = [
'TB',
//'BT',
'LR',
//'RL'
] as const;
const shapesSet1 = ['text', 'card', 'lin-rect', 'diamond', 'hexagon'] as const;
// removing labelRect, need have alias for it
const shapesSet2 = ['rounded', 'rect', 'start', 'stop'] as const;
const shapesSet3 = ['fork', 'choice', 'note', 'stadium', 'odd'] as const;
const shapesSet4 = ['subroutine', 'cylinder', 'circle', 'doublecircle', 'odd'] as const;
const shapesSet5 = ['anchor', 'lean-r', 'lean-l', 'trap-t', 'trap-b'] as const;
// Aggregate all shape sets into a single array
const shapesSets = [shapesSet1, shapesSet2, shapesSet3, shapesSet4, shapesSet5] as const;
looks.forEach((look) => {
directions.forEach((direction) => {
shapesSets.forEach((shapesSet) => {
describe(`Test ${shapesSet.join(', ')} in ${look} look and dir ${direction}`, () => {
it(`without label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape} }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`connect all shapes with each other`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index}${index}@{ shape: ${newShape}, label: 'This is a label for ${newShape} shape' }\n`;
});
for (let i = 0; i < shapesSet.length; i++) {
for (let j = i + 1; j < shapesSet.length; j++) {
flowchartCode += ` n${i}${i} --> n${j}${j}\n`;
}
}
imgSnapshotTest(flowchartCode, { look });
});
it(`with very long label`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a very very very very very long long long label for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:true`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with markdown htmlLabels:false`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold** </br>and <strong>strong</strong> for ${newShape} shape' }\n`;
});
imgSnapshotTest(flowchartCode, {
look,
htmlLabels: false,
flowchart: { htmlLabels: false },
});
});
it(`with styles`, () => {
let flowchartCode = `flowchart ${direction}\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`;
});
imgSnapshotTest(flowchartCode, { look });
});
it(`with classDef`, () => {
let flowchartCode = `flowchart ${direction}\n`;
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`;
shapesSet.forEach((newShape, index) => {
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new ${newShape} shape' }\n`;
flowchartCode += ` n${index}${index}:::customClazz\n`;
});
imgSnapshotTest(flowchartCode, { look });
});
});
});
});
});

View File

@@ -1,52 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Architecture Mermaid Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<h2>External Icons Demo</h2>
<pre class="mermaid">
architecture-beta
service s3(logos:aws-s3)[Cloud Store]
service ec2(logos:aws-ec2)[Server]
service api(logos:aws-api-gateway)[Api Gateway]
service fa(fa:image)[Font Awesome Icon]
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
startOnLoad: false,
logLevel: 0,
});
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
{
name: 'fa',
loader: () =>
fetch('https://unpkg.com/@iconify-json/fa6-regular/icons.json').then((res) =>
res.json()
),
},
]);
await mermaid.run();
if (window.Cypress) {
window.rendered = true;
}
</script>
</body>
</html>

View File

@@ -73,9 +73,7 @@
font-family: monospace;
font-size: 72px;
}
pre {
width: 100%;
}
/* tspan {
font-size: 6px !important;
} */
@@ -84,275 +82,22 @@
<body>
<div class="flex">
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
---
title: hello2
config:
look: handDrawn
layout: elk
elk:
<!-- nodePlacementStrategy: INTERACTIVE -->
<!-- mergeEdges: true -->
---
stateDiagram-v2
direction LR
accTitle: An idealized Open Source supply-chain graph
%%
state "🟦 Importer" as author_importer
state "🟥 Supplier, Owner" as author_owner
state "🟨🟥 Maintainer, Author\n🟨 Custodian" as author
state "🟩 Distributor" as repository_distributor
state "🟦 Importer" as language_importer
state "🟦🟨 Packager" as language_packager
state "🟦🟨 OSS Steward" as language_steward
state "🟨 Curator" as language_curator
state "🟩 Distributor" as language_distributor
state "🟦 Contributor" as contributor
state "🟦 Importer" as package_importer
state "🟨 Patcher" as package_patcher
state "🟨🟦 Builder\n🟨🟦 Packager\n🟨🟦 Containerizer" as package_packager
state "🟨 Curator" as package_curator
state "🟩 Distributor" as package_distributor
state "🟦 Importer" as integrator_importer
state "🟥 Supplier, Manufacturer, Owner" as integrator_owner
state "🟦🟨🟥 Integrator, Developer" as integrator_developer
state "🟩🟨 SBOM Redactor\n🟩 Publisher" as integrator_publisher
state "🟦🟨 Builder" as integrator_builder
state "🟨 Deployer" as deployer
state "🟦 Vuln. Checker" as integrator_checker
state "🟩🟨 SBOM Redactor" as redactor
state "🟦 Consumer\n🟦 User" as consumer
state "🟦 Auditor" as auditor_internal
state "🟦 Auditor" as auditor_external
%%
classDef createsSBOM stroke:red,stroke-width:3px;
classDef updatesSBOM stroke:yellow,stroke-width:3px;
classDef assemblesSBOM stroke:yellow,stroke-width:3px;
classDef distributesSBOM stroke:green,stroke-width:3px;
classDef verifiesSBOM stroke:#07f,stroke-width:3px;
%%
class author_importer verifiesSBOM
class author_owner createsSBOM
class manufacturer_owner createsSBOM
class author assemblesSBOM
class package_importer verifiesSBOM
class package_patcher updatesSBOM
class package_packager assemblesSBOM
class package_curator distributesSBOM
class package_distributor distributesSBOM
class language_importer verifiesSBOM
class language_packager assemblesSBOM
class language_steward updatesSBOM
class language_curator distributesSBOM
class language_distributor distributesSBOM
class repository_distributor distributesSBOM
class integrator_importer verifiesSBOM
class integrator_owner createsSBOM
class integrator_developer assemblesSBOM
class integrator_publisher distributesSBOM
class integrator_builder assemblesSBOM
class integrator_checker verifiesSBOM
class deployer assemblesSBOM
class redactor distributesSBOM
class auditor_internal verifiesSBOM
class auditor_external verifiesSBOM
state "Maintainer Environment" as environment_maintainer {
[*] --> author_importer
[*] --> author
author_importer --> author
author_owner --> author
author --> language_packager
}
[*] --> environment_maintainer
state "Language Ecosystem" as ecosystem_lang {
[*] --> language_importer
[*] --> language_steward
[*] --> language_curator
[*] --> language_distributor
language_importer --> language_distributor
language_importer --> language_curator
language_steward --> language_curator
language_curator --> language_distributor
}
language_packager --> ecosystem_lang
ecosystem_lang --> ecosystem_lang
state "Public Collaboration Ecosystem" as ecosystem_repo {
[*] --> repository_distributor
}
author --> ecosystem_repo
ecosystem_repo --> author
repository_distributor --> contributor
contributor --> repository_distributor
state "Package Ecosystem" as ecosystem_package {
[*] --> package_importer
[*] --> package_packager
[*] --> package_patcher
package_importer --> package_patcher
package_importer --> package_packager
package_patcher --> package_packager
package_packager --> package_curator
package_packager --> package_distributor
package_curator --> package_distributor
}
repository_distributor --> ecosystem_package
language_distributor --> ecosystem_package
ecosystem_package --> ecosystem_package
state "Integrator Environment" as environment_integrator {
[*] --> integrator_developer
[*] --> integrator_importer
integrator_importer --> integrator_developer
integrator_owner --> integrator_developer
integrator_builder --> integrator_publisher
integrator_developer --> integrator_checker
integrator_checker --> integrator_developer
auditor_internal --> integrator_developer
integrator_developer --> integrator_builder
integrator_developer --> auditor_internal
}
repository_distributor --> environment_integrator
language_distributor --> environment_integrator
package_distributor --> environment_integrator
state "Production Environment" as environment_prod {
[*] --> deployer
deployer --> redactor
}
integrator_publisher --> [*]
integrator_developer --> environment_prod
integrator_builder --> environment_prod
integrator_publisher --> environment_prod
deployer --> auditor_external
deployer --> consumer
redactor --> consumer
</pre>
<pre id="diagram" class="mermaid2">
---
config:
look: neo
---
flowchart RL
subgraph " "
A5@{ shape: manual-file, label: "a label"}
B5@{ shape: manual-input, label: "a label" }
C5@{ shape: mul-doc, label: "a label" }
D5@{ shape: mul-proc, label: "a label" }
E5@{ shape: paper-tape, label: "a label" }
B3@{ shape: das, label: "a label" }
C3@{ shape: disk, label: "a label" }
D4@{ shape: lin-doc, label: "a label" }
E4@{ shape: loop-limit, label: "a label" }
end
subgraph " "
B6@{ shape: summary, label: "a label" }
C6@{ shape: tag-we-rect, label: "a label" }
D6@{ shape: tag-rect, label: "a label" }
A2@{ shape: fork}
B2@{ shape: hourglass }
C2@{ shape: comment, label: "I am a comment" }
D2@{ shape: bolt }
D3@{ shape: disp, label: "a label" }
C4@{ shape: junction, label: "a label" }
A4@{ shape: extract, label: "a label"}
B52[a fr]@{ shape: fr }
end
subgraph " "
A1@{ shape: text, label: This is a textblock}
B1@{ shape: card, label: "a label" }
C1@{ shape: lined-proc, label: "a label" }
D1@{ shape: start, label: "a label" }
E1@{ shape: stop, label: "a label" }
E2@{ shape: doc, label: "a label" }
A6@{ shape: stored-data, label: "a label"}
A3@{ shape: delay, label: "a label" }
E3@{ shape: div-proc, label: "a label" }
B4[a label]@{ shape: win-pane }
end
</pre>
<pre id="diagram" class="mermaid2">
---
title: hello2
config:
look: handDrawn
elk:
<!-- nodePlacementStrategy: SIMPLE -->
---
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TD
A([Start]) -->|go to booking page| B("select
ISBS booking no")
A --> QQ{cancel booking}
A --> RR{no show}
A --> SS{change booking}
B -->C(wmpay_request_payment.request_type= 'partial',
wmpay_request_payment.status= 'paid',
pos_booking.booking_status= partial and 'full_deposit')
style C text-align:left
C -->D{manage booking}
D -->|cancel|E[ระบบแสดงช่องให้กรอกเหตุผล]
E -->F{กดปุ่ม 'cancel' หรือไม่}
F -->|Yes|G[ระบบบันทึกค่าใหม่
และไม่สามารถแก้ไขข้อมูลได้]
F -->|No|H[กดปุ่ม 'close']
H -->|ระบบไม่เปลี่ยนแปลงข้อมูล|Z
G -->|ระบบส่งข้อมูล|I[(POS_database)]
I -->|pos_booking.booking_status='cancel'|Z([End])
D -->|no show|J[ระบบแสดงช่องให้กรอกเหตุผล]
J -->K{กดปุ่ม 'noshow' หรือไม่}
K -->|Yes|L[ระบบสร้างใบเสร็จอัตโนมัติ
Product_id: 439,
ItemName: no show]
style L text-align:left
K -->|No|O[กดปุ่ม 'close']
O -->|ระบบไม่เปลี่ยนแปลงข้อมูล|Z
L -->M[ระบบบันทึกค่าใหม่]
M -->|ระบบส่งข้อมูล|N[(POS_database)]
N -->|pos_booking.booking_status=noshow|Z
</pre>
<pre id="diagram" class="mermaid2">
---
title: hello2
config:
look: handDrawn
layout: dagre
elk:
nodePlacementStrategy: BRANDES_KOEPF
---
flowchart
A --> A
subgraph A
B --> B
subgraph B
C
end
end
flowchart LR
A[Start] --Some text--> B(Continue)
B --> C{Evaluate}
C -- One --> D[Option 1]
C -- Two --> E[Option 2]
C -- Three --> F[fa:fa-car Option 3]
</pre
@@ -421,12 +166,6 @@ flowchart LR
</pre>
<pre id="diagram4" class="mermaid">
flowchart TB
A@{
label: "aksljhf kasjdh"
}
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import layouts from './mermaid-layout-elk.esm.mjs';
@@ -456,7 +195,7 @@ flowchart TB
messageFontFamily: 'courier',
},
fontSize: 12,
logLevel: 3,
logLevel: 0,
securityLevel: 'loose',
});
function callback() {

View File

@@ -1,174 +0,0 @@
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background: #333; */
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
/* background-color: #efefef;
background-image: radial-gradient(#fff 51%, transparent 91%),
radial-gradient(#fff 51%, transparent 91%);
background-size: 20px 20px;
background-position:
0 0,
10px 10px;
background-repeat: repeat; */
}
</style>
</head>
<body style="display: flex; gap: 2rem; flex-direction: row">
<pre id="diagram4" class="mermaid">
flowchart LR
A@{ icon: "fa:window-minimize", form: circle }
E@{ icon: "fa:window-minimize", form: circle }
B@{ icon: "fa:bell", form: circle }
B2@{ icon: "fa:bell", form: circle }
C@{ icon: "fa:address-book", form: square }
D@{ icon: "fa:star-half", form: square }
A --> E
B --> B2
</pre>
<pre id="diagram4" class="mermaid2">
flowchart TB
A --test2--> B2@{ icon: "fa:bell", form: "rounded", label: "B2 aiduaid uyawduad uaduabd uyduadb", pos: "b" }
B2 --test--> C
D --> B2 --> E
style B2 fill:#f9f,stroke:#333,stroke-width:4px
</pre
>
<pre id="diagram43" class="mermaid2">
flowchart BT
A --test2--> B2@{ icon: "fa:bell", form: "square", label: "B2", pos: "t", h: 40, w: 30 }
B2 --test--> C
D --> B2 --> E
</pre
>
<pre id="diagram4" class="mermaid2">
flowchart BT
A --test2--> B2@{ icon: "fa:bell", label: "B2 awiugdawu uydgayuiwd wuydguy", pos: "b", h: 40, w: 30 }
B2 --test--> C
</pre
>
<pre id="diagram43" class="mermaid2">
flowchart BT
A --test2--> B2@{ icon: "fa:bell", label: "B2 dawuygd ayuwgd uy", pos: "t", h: 40, w: 30 }
B2 --test--> C
</pre
>
<pre id="diagram6" class="mermaid2">
flowchart TB
A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "t", w: 200, h: 100 } --> C
</pre
>
<pre id="diagram6" class="mermaid2">
flowchart TB
A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "b", w: 200, h: 100 } --> C
D --> B2 --> E
</pre
>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import layouts from './mermaid-layout-elk.esm.mjs';
mermaid.registerLayoutLoaders(layouts);
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
{
name: 'fa',
loader: () =>
fetch('https://unpkg.com/@iconify-json/fa6-solid/icons.json').then((res) => res.json()),
},
]);
mermaid.parseError = function (err, hash) {
console.error('Mermaid error: ', err);
};
window.callback = function () {
alert('A callback was triggered');
};
mermaid.initialize({
// theme: 'base',
// handdrawnSeed: 12,
look: 'classic',
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
// 'elk.nodePlacement.strategy': 'SIMPLE',
// 'elk.nodePlacement.strategy': 'LAYERED',
// 'elk.mergeEdges': true,
// layout: 'dagre',
// layout: 'elk',
// layout: 'fixed',
// htmlLabels: false,
flowchart: { titleTopMargin: 10, padding: 0, htmlLabels: true },
// fontFamily: 'Caveat',
// fontFamily: 'Kalam',
fontFamily: 'courier',
sequence: {
actorFontFamily: 'courier',
noteFontFamily: 'courier',
messageFontFamily: 'courier',
},
themeVariables: {
fontSize: 50,
fontFamily: 'courier',
},
logLevel: 0,
securityLevel: 'loose',
});
function callback() {
alert('It worked');
}
mermaid.parseError = function (err, hash) {
console.error('In parse error:');
console.error(err);
};
</script>
</body>
</html>

View File

@@ -50,23 +50,6 @@ const contentLoaded = async function () {
mermaid.registerLayoutLoaders(layouts);
mermaid.initialize(graphObj.mermaid);
const staticBellIconPack = {
prefix: 'fa6-regular',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',
width: 448,
},
},
width: 512,
height: 512,
};
mermaid.registerIconPacks([
{
name: 'fa',
loader: () => staticBellIconPack,
},
]);
await mermaid.run();
}
};

View File

@@ -1,256 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Architecture Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<h1>Architecture diagram demo</h1>
<h2>Simple diagram with groups</h2>
<pre class="mermaid">
architecture-beta
group api(cloud)[API]
service db(database)[Database] in api
service disk1(disk)[Storage] in api
service disk2(disk)[Storage] in api
service server(server)[Server] in api
service gateway(internet)[Gateway]
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db
server:T -- B:gateway
</pre>
<hr />
<h2>Groups within groups</h2>
<pre class="mermaid">
architecture-beta
group api[API]
group public[Public API] in api
group private[Private API] in api
service serv1(server)[Server] in public
service serv2(server)[Server] in private
service db(database)[Database] in private
service gateway(internet)[Gateway] in api
serv1:B -- T:serv2
serv2:L -- R:db
serv1:L -- R:gateway
</pre>
<hr />
<h2>Default icon (?) from unknown icon name</h2>
<pre class="mermaid">
architecture-beta
service unknown(iconnamedoesntexist)[Unknown Icon]
</pre>
<hr />
<h2>Split Direction</h2>
<pre class="mermaid">
architecture-beta
service db(database)[Database]
service s3(disk)[Storage]
service serv1(server)[Server 1]
service serv2(server)[Server 2]
service disk(disk)[Disk]
db:L -- R:s3
serv1:L -- T:s3
serv2:L -- B:s3
serv1:T -- B:disk
</pre>
<hr />
<h2>Arrow Tests</h2>
<pre class="mermaid">
architecture-beta
service servC(server)[Server 1]
service servL(server)[Server 2]
service servR(server)[Server 3]
service servT(server)[Server 4]
service servB(server)[Server 5]
servC:L <--> R:servL
servC:R <--> L:servR
servC:T <--> B:servT
servC:B <--> T:servB
servL:T <--> L:servT
servL:B <--> L:servB
servR:T <--> R:servT
servR:B <--> R:servB
</pre>
<pre class="mermaid">
architecture-beta
service servC(server)[Server 1]
service servL(server)[Server 2]
service servR(server)[Server 3]
service servT(server)[Server 4]
service servB(server)[Server 5]
servC:L <--> R:servL
servC:R <--> L:servR
servC:T <--> B:servT
servC:B <--> T:servB
servT:L <--> T:servL
servB:L <--> B:servL
servT:R <--> T:servR
servB:R <--> B:servR
</pre>
<hr />
<h2>Group Edges</h2>
<pre class="mermaid">
architecture-beta
group left_group(cloud)[Left]
group right_group(cloud)[Right]
group top_group(cloud)[Top]
group bottom_group(cloud)[Bottom]
group center_group(cloud)[Center]
service left_disk(disk)[Disk] in left_group
service right_disk(disk)[Disk] in right_group
service top_disk(disk)[Disk] in top_group
service bottom_disk(disk)[Disk] in bottom_group
service center_disk(disk)[Disk] in center_group
left_disk{group}:R <--> L:center_disk{group}
right_disk{group}:L <--> R:center_disk{group}
top_disk{group}:B <--> T:center_disk{group}
bottom_disk{group}:T <--> B:center_disk{group}
</pre
>
<hr />
<h2>Edge Label Test</h2>
<pre class="mermaid">
architecture-beta
service servC(server)[Server 1]
service servL(server)[Server 2]
service servR(server)[Server 3]
service servT(server)[Server 4]
service servB(server)[Server 5]
servC:L -[Label]- R:servL
servC:R -[Label]- L:servR
servC:T -[Label]- B:servT
servC:B -[Label]- T:servB
servL:T -[Label]- L:servT
servL:B -[Label]- L:servB
servR:T -[Label]- R:servT
servR:B -[Label]- R:servB
</pre>
<pre class="mermaid">
architecture-beta
service servC(server)[Server 1]
service servL(server)[Server 2]
service servR(server)[Server 3]
service servT(server)[Server 4]
service servB(server)[Server 5]
servC:L -[Label that is Long]- R:servL
servC:R -[Label that is Long]- L:servR
servC:T -[Label that is Long]- B:servT
servC:B -[Label that is Long]- T:servB
servL:T -[Label that is Long]- L:servT
servL:B -[Label that is Long]- L:servB
servR:T -[Label that is Long]- R:servT
servR:B -[Label that is Long]- R:servB
</pre>
<hr />
<h2>Junction Demo</h2>
<pre class="mermaid">
architecture-beta
service left_disk(disk)[Disk]
service top_disk(disk)[Disk]
service bottom_disk(disk)[Disk]
service top_gateway(internet)[Gateway]
service bottom_gateway(internet)[Gateway]
junction juncC
junction juncR
left_disk:R -- L:juncC
top_disk:B -- T:juncC
bottom_disk:T -- B:juncC
juncC:R -- L:juncR
top_gateway:B -- T:juncR
bottom_gateway:T -- B:juncR
</pre>
<hr />
<h2>Junction Demo Groups</h2>
<pre class="mermaid">
architecture-beta
group left
group right
service left_disk(disk)[Disk] in left
service top_disk(disk)[Disk] in left
service bottom_disk(disk)[Disk] in left
service top_gateway(internet)[Gateway] in right
service bottom_gateway(internet)[Gateway] in right
junction juncC in left
junction juncR in right
left_disk:R -- L:juncC
top_disk:B -- T:juncC
bottom_disk:T -- B:juncC
top_gateway:B <-- T:juncR
bottom_gateway:T <-- B:juncR
juncC{group}:R --> L:juncR{group}
</pre>
<hr />
<h2>External Icons Demo</h2>
<pre class="mermaid">
architecture-beta
service s3(logos:aws-s3)[Cloud Store]
service ec2(logos:aws-ec2)[Server]
service api(logos:aws-api-gateway)[Api Gateway]
service fa(fa:image)[Font Awesome Icon]
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
{
name: 'fa',
loader: () =>
fetch('https://unpkg.com/@iconify-json/fa6-regular/icons.json').then((res) =>
res.json()
),
},
]);
</script>
</body>
</html>

View File

@@ -159,87 +159,30 @@
class People List~List~Person~~
</pre>
<hr />
<pre class="mermaid">
classDiagram
namespace Company.Project.Module {
class GenericClass~T~ {
+addItem(item: T)
+getItem() T
A1 --> B1
namespace A {
class A1 {
+foo : string
}
class A2 {
+bar : int
}
}
namespace B {
class B1 {
+foo : bool
}
class B2 {
+bar : float
}
}
A2 --> B2
</pre>
<hr />
<pre class="mermaid">
classDiagram
namespace Company.Project.Module.SubModule {
class Report {
+generatePDF(data: List)
+generateCSV(data: List)
}
}
namespace Company.Project.Module {
class Admin {
+generateReport()
}
}
Admin --> Report : generates
</pre>
<pre class="mermaid">
classDiagram
namespace Company.Project.Module {
class User {
+login(username: String, password: String)
+logout()
}
class Admin {
+addUser(user: User)
+removeUser(user: User)
+generateReport()
}
class Report {
+generatePDF(reportData: List)
+generateCSV(reportData: List)
}
}
Admin --> User : manages
Admin --> Report : generates
</pre>
<hr />
<pre class="mermaid">
classDiagram
namespace Shapes {
class Shape {
+calculateArea() double
}
class Circle {
+double radius
}
class Square {
+double side
}
}
Shape <|-- Circle
Shape <|-- Square
namespace Vehicles {
class Vehicle {
+String brand
}
class Car {
+int horsepower
}
class Bike {
+boolean hasGears
}
}
Vehicle <|-- Car
Vehicle <|-- Bike
Car --> Circle : "Logo Shape"
Bike --> Square : "Logo Shape"
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({

View File

@@ -125,35 +125,6 @@
</pre>
<hr />
<pre class="mermaid">
erDiagram
p[Person] {
string firstName
string lastName
}
a["Customer Account"] {
string email
}
b["Customer Account Secondary"] {
string email
}
c["Customer Account Tertiary"] {
string email
}
d["Customer Account Nth"] {
string email
}
p ||--o| a : "has<br />one"
p ||--o| b : "has<br />one<br />two"
p ||--o| c : "has<br />one<br />two<br />three"
p ||--o| d : "has<br />one<br />two<br />three<br />...<br />Nth"
</pre>
<hr />
<pre class="mermaid">
erDiagram
_customer_order {

View File

@@ -88,9 +88,6 @@
<li>
<h2><a href="./block.html">Layered Blocks</a></h2>
</li>
<li>
<h2><a href="./architecture.html">Architecture</a></h2>
</li>
</ul>
</body>
</html>

View File

@@ -7,6 +7,9 @@ services:
tty: true
working_dir: /mermaid
mem_limit: '8G'
entrypoint: ./docker-entrypoint.sh
environment:
- NODE_OPTIONS=--max_old_space_size=8192
volumes:
- ./:/mermaid
- root_cache:/root/.cache

3
docker-entrypoint.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
source /root/.shrc
exec "$@"

View File

@@ -1,228 +0,0 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/adding-new-shape.md](../packages/mermaid/src/docs/adding-new-shape.md).
# Custom SVG Shapes Library
This library provides a collection of custom SVG shapes, utilities, and helpers for generating diagram components. The shapes are designed to be used within an SVG container and include a variety of common and complex shapes.
## Overview
## Shape Helpers and Utilities
Before starting with shape creation, it's essential to familiarize yourself with the utilities provided in the `utils.ts` file from `packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js`. These utilities are designed to assist with various aspects of SVG shape manipulation and ensure consistent and accurate rendering.
## Available Utilities
### 1. `labelHelper`
- **Purpose**: This function creates and inserts labels inside SVG shapes.
- **Features**:
- Handles both HTML labels and plain text.
- Calculates the bounding box dimensions of the label.
- Ensures proper positioning of labels within shapes.
### 2. `updateNodeBounds`
- **Purpose**: Updates the bounding box dimensions (width and height) of a node.
- **Usage**:
- Adjusts the size of the node to fit the content or shape.
- Useful for ensuring that shapes resize appropriately based on their content.
### 3. `insertPolygonShape`
- **Purpose**: Inserts a polygon shape into an SVG container.
- **Features**:
- Handles the creation and insertion of complex polygonal shapes.
- Configures the shape's appearance and positioning within the SVG container.
### 4. `getNodeClasses`
- **Purpose**: Returns the appropriate CSS classes for a node based on its configuration.
- **Usage**:
- Dynamically applies CSS classes to nodes for styling purposes.
- Ensures that nodes adhere to the desired design and theme.
### 5. `createPathFromPoints`
- **Purpose**: Generates an SVG path string from an array of points.
- **Usage**:
- Converts a list of points into a smooth path.
- Useful for creating custom shapes or paths within the SVG.
### 6. `generateFullSineWavePoints`
- **Purpose**: Generates points for a sine wave, useful for creating wavy-edged shapes.
- **Usage**:
- Facilitates the creation of shapes with wavy or sine-wave edges.
- Can be used to add decorative or dynamic edges to shapes.
## Getting Started
To utilize these utilities, simply import them from the `utils.ts` file into your shape creation script. These helpers will streamline the process of building and customizing SVG shapes, ensuring consistent results across your projects.
```typescript
import {
labelHelper,
updateNodeBounds,
insertPolygonShape,
getNodeClasses,
createPathFromPoints,
generateFullSineWavePoints,
} from './utils.ts';
```
## Example Usage
Heres a basic example of how you might use some of these utilities:
```typescript
import { labelHelper, insertPolygonShape } from './utils.ts';
const svgContainer = document.getElementById('svgContainer');
// Insert a polygon shape
insertPolygonShape(svgContainer /* shape-specific parameters */);
// Create and insert a label inside the shape
labelHelper(svgContainer /* label-specific parameters */);
```
## Adding New Shapes
### 1. Create the Shape Function
To add a new shape:
- **Create the shape function**: Create a new file of name of the shape and export a function in the `shapes` directory that generates your shape. The file and function should follow the pattern used in existing shapes and return an SVG element.
- **Example**:
```typescript
import { Node, RenderOptions } from '../../types.d.ts';
export const myNewShape = async (
parent: SVGAElement,
node: Node,
renderOptions: RenderOptions
) => {
// Create your shape here
const shape = parent.insert('g').attr('class', 'my-new-shape');
// Add other elements or styles as needed
return shape;
};
```
### 2. Register the Shape
- **Register the shape**: Add your shape to the `shapes` object in the main shapes module. This allows your shape to be recognized and used within the system.
- **Example**:
```typescript
import { myNewShape } from './shapes/myNewShape';
const shapes = {
...,
'my-new-shape': myNewShape,
// Shortened alias (if any).
'm-nsh': myNewShape
};
```
# Shape Intersection Algorithms
This contains algorithms and utilities for calculating intersection points for various shapes in SVG. Arrow intersection points are crucial for accurately determining where arrows connect with shapes. Ensuring precise intersection points enhances the clarity and accuracy of flowcharts and diagrams.
## Shape Intersection Functions
### 1. `Ellipse`
Calculates the intersection points for an ellipse.
**Usage**:
```javascript
import intersectEllipse from './intersect-ellipse.js';
const intersection = intersectEllipse(node, rx, ry, point);
```
- **Parameters**:
- `node`: The SVG node element.
- `rx`: The x-radius of the ellipse.
- `ry`: The y-radius of the ellipse.
- `point`: The point from which the intersection is calculated.
### 2. `intersectRect`
Calculates the intersection points for a rectangle.
**Usage**:
```javascript
import intersectRect from './intersect-rect.js';
const intersection = intersectRect(node, point);
```
- **Parameters**:
- `node`: The SVG node element.
- `point`: The point from which the intersection is calculated.
### 3. `intersectPolygon`
Calculates the intersection points for a polygon.
**Usage**:
```javascript
import intersectPolygon from './intersect-polygon.js';
const intersection = intersectPolygon(node, polyPoints, point);
```
- **Parameters**:
- `node`: The SVG node element.
- `polyPoints`: Array of points defining the polygon.
- `point`: The point from which the intersection is calculated.
## Cypress Tests
To ensure the robustness of the flowchart shapes, there are implementation of comprehensive Cypress test cases in `newShapes.spec.ts` file. These tests cover various aspects such as:
- **Shapes**: Testing new shapes like `bowTieRect`, `waveRectangle`, `trapezoidalPentagon`, etc.
- **Looks**: Verifying shapes under different visual styles (`classic` and `handDrawn`).
- **Directions**: Ensuring correct rendering in all flow directions of arrows :
- `TB` `(Top -> Bottom)`
- `BT` `(Bottom -> Top)`
- `LR` `(Left -> Right)`
- `RL` `(Right -> Left)`
- **Labels**: Testing shapes with different labels, including:
- No labels
- Short labels
- Very long labels
- Markdown with `htmlLabels:true` and `htmlLabels:false`
- **Styles**: Applying custom styles to shapes and verifying correct rendering.
- **Class Definitions**: Using `classDef` to apply custom classes and testing their impact.
### Running the Tests
To run the Cypress tests, follow these steps:
1. Ensure you have all dependencies installed by running:
```bash
pnpm install
```
2. Start the Cypress test runner:
```bash
cypress open --env updateSnapshots=true
```
3. Select the test suite from the Cypress interface to run all the flowchart shape tests.

View File

@@ -370,9 +370,9 @@ If the users have no way to know that things have changed, then you haven't real
Likewise, if users don't know that there is a new feature that you've implemented, it will forever remain unknown and unused.
The documentation has to be updated for users to know that things have been changed and added!
If you are adding a new feature, add `(v<MERMAID_RELEASE_VERSION>+)` in the title or description. It will be replaced automatically with the current version number when the release happens.
If you are adding a new feature, add `(v10.8.0+)` in the title or description. It will be replaced automatically with the current version number when the release happens.
eg: `# Feature Name (v<MERMAID_RELEASE_VERSION>+)`
eg: `# Feature Name (v10.8.0+)`
We know it can sometimes be hard to code _and_ write user documentation.

View File

@@ -127,7 +127,7 @@ Error.prepareStackTrace
#### Defined in
node_modules/.pnpm/@types+node\@20.16.2/node_modules/@types/node/globals.d.ts:28
node_modules/@types/node/globals.d.ts:28
---
@@ -141,7 +141,7 @@ Error.stackTraceLimit
#### Defined in
node_modules/.pnpm/@types+node\@20.16.2/node_modules/@types/node/globals.d.ts:30
node_modules/@types/node/globals.d.ts:30
## Methods
@@ -168,4 +168,4 @@ Error.captureStackTrace
#### Defined in
node_modules/.pnpm/@types+node\@20.16.2/node_modules/@types/node/globals.d.ts:21
node_modules/@types/node/globals.d.ts:21

View File

@@ -16,11 +16,11 @@
### config
**config**: [`MermaidConfig`](mermaid.MermaidConfig.md)
**config**: `MermaidConfig`
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L125)
[packages/mermaid/src/rendering-util/types.d.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.d.ts#L118)
---
@@ -30,7 +30,7 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L124)
[packages/mermaid/src/rendering-util/types.d.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.d.ts#L117)
---
@@ -40,4 +40,4 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L123)
[packages/mermaid/src/rendering-util/types.d.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.d.ts#L116)

View File

@@ -28,7 +28,7 @@ page.
#### Defined in
[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
[packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435)
---
@@ -59,7 +59,7 @@ A graph definition key
#### Defined in
[packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438)
[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
---
@@ -89,7 +89,7 @@ Use [initialize](mermaid.Mermaid.md#initialize) and [run](mermaid.Mermaid.md#run
#### Defined in
[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430)
---
@@ -116,7 +116,7 @@ This function should be called before the run function.
#### Defined in
[packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435)
[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434)
---
@@ -130,7 +130,7 @@ Use [parse](mermaid.Mermaid.md#parse) and [render](mermaid.Mermaid.md#render) in
#### Defined in
[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425)
[packages/mermaid/src/mermaid.ts:424](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L424)
---
@@ -180,7 +180,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
#### Defined in
[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426)
[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425)
---
@@ -190,7 +190,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
#### Defined in
[packages/mermaid/src/mermaid.ts:420](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L420)
[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419)
---
@@ -218,31 +218,7 @@ Used to register external diagram types.
#### Defined in
[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434)
---
### registerIconPacks
**registerIconPacks**: (`iconLoaders`: `IconLoader`\[]) => `void`
#### Type declaration
▸ (`iconLoaders`): `void`
##### Parameters
| Name | Type |
| :------------ | :-------------- |
| `iconLoaders` | `IconLoader`\[] |
##### Returns
`void`
#### Defined in
[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439)
[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433)
---
@@ -266,7 +242,7 @@ Used to register external diagram types.
#### Defined in
[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433)
[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
---
@@ -292,7 +268,7 @@ Used to register external diagram types.
#### Defined in
[packages/mermaid/src/mermaid.ts:427](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L427)
[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426)
---
@@ -340,7 +316,7 @@ Renders the mermaid diagrams
#### Defined in
[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
---
@@ -375,7 +351,7 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me
#### Defined in
[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
---
@@ -385,4 +361,4 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me
#### Defined in
[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419)
[packages/mermaid/src/mermaid.ts:418](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L418)

View File

@@ -16,17 +16,7 @@
#### Defined in
[packages/mermaid/src/config.type.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L122)
---
### architecture
`Optional` **architecture**: `ArchitectureDiagramConfig`
#### Defined in
[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
[packages/mermaid/src/config.type.ts:112](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L112)
---
@@ -39,7 +29,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
[packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
---
@@ -49,7 +39,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
---
@@ -59,7 +49,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
---
@@ -69,7 +59,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
[packages/mermaid/src/config.type.ts:177](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L177)
---
@@ -79,7 +69,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L113)
[packages/mermaid/src/config.type.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L103)
---
@@ -93,7 +83,7 @@ You can set this attribute to base the seed on a static string.
#### Defined in
[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
[packages/mermaid/src/config.type.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L171)
---
@@ -111,7 +101,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
[packages/mermaid/src/config.type.ts:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L164)
---
@@ -121,7 +111,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
---
@@ -131,11 +121,10 @@ should not change unless content is changed.
#### Type declaration
| Name | Type | Description |
| :----------------------- | :-------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cycleBreakingStrategy?` | `"GREEDY"` \| `"DEPTH_FIRST"` \| `"INTERACTIVE"` \| `"MODEL_ORDER"` \| `"GREEDY_MODEL_ORDER"` | This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops. |
| `mergeEdges?` | `boolean` | Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram. |
| `nodePlacementStrategy?` | `"SIMPLE"` \| `"NETWORK_SIMPLEX"` \| `"LINEAR_SEGMENTS"` \| `"BRANDES_KOEPF"` | Elk specific option affecting how nodes are placed. |
| Name | Type | Description |
| :----------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mergeEdges?` | `boolean` | Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram. |
| `nodePlacementStrategy?` | `"SIMPLE"` \| `"NETWORK_SIMPLEX"` \| `"LINEAR_SEGMENTS"` \| `"BRANDES_KOEPF"` | Elk specific option affecting how nodes are placed. |
#### Defined in
@@ -149,7 +138,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
[packages/mermaid/src/config.type.ts:179](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L179)
---
@@ -159,7 +148,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
[packages/mermaid/src/config.type.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L172)
---
@@ -173,7 +162,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
[packages/mermaid/src/config.type.ts:111](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L111)
---
@@ -183,7 +172,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
---
@@ -197,7 +186,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163)
[packages/mermaid/src/config.type.ts:153](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L153)
---
@@ -207,7 +196,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
---
@@ -217,7 +206,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
---
@@ -239,7 +228,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L114)
[packages/mermaid/src/config.type.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L104)
---
@@ -249,7 +238,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
[packages/mermaid/src/config.type.ts:175](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L175)
---
@@ -276,7 +265,7 @@ fall back to legacy rendering for KaTeX.
#### Defined in
[packages/mermaid/src/config.type.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L156)
[packages/mermaid/src/config.type.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L146)
---
@@ -288,7 +277,7 @@ This option decides the amount of logging to be used by mermaid.
#### Defined in
[packages/mermaid/src/config.type.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L127)
[packages/mermaid/src/config.type.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L117)
---
@@ -310,7 +299,7 @@ Defines which main look to use for the diagram.
#### Defined in
[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
---
@@ -344,7 +333,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
---
@@ -354,7 +343,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
---
@@ -364,7 +353,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
[packages/mermaid/src/config.type.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L180)
---
@@ -374,7 +363,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
---
@@ -384,7 +373,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
---
@@ -394,7 +383,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
---
@@ -408,7 +397,7 @@ This prevents malicious graph directives from overriding a site's default securi
#### Defined in
[packages/mermaid/src/config.type.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L148)
[packages/mermaid/src/config.type.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L138)
---
@@ -420,7 +409,7 @@ Level of trust for parsed diagram
#### Defined in
[packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
[packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
---
@@ -430,7 +419,7 @@ Level of trust for parsed diagram
#### Defined in
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
[packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173)
---
@@ -442,7 +431,7 @@ Dictates whether mermaid starts on Page load
#### Defined in
[packages/mermaid/src/config.type.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L135)
[packages/mermaid/src/config.type.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L125)
---
@@ -452,7 +441,7 @@ Dictates whether mermaid starts on Page load
#### Defined in
[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
[packages/mermaid/src/config.type.ts:178](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L178)
---
@@ -465,7 +454,7 @@ This is useful when you want to control how to handle syntax errors in your appl
#### Defined in
[packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
---
@@ -508,7 +497,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
[packages/mermaid/src/config.type.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L176)
---
@@ -518,7 +507,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
---
@@ -528,4 +517,4 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)

View File

@@ -19,4 +19,4 @@ The `parseError` function will not be called.
#### Defined in
[packages/mermaid/src/types.ts:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L56)
[packages/mermaid/src/types.ts:43](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L43)

View File

@@ -10,18 +10,6 @@
## Properties
### config
**config**: [`MermaidConfig`](mermaid.MermaidConfig.md)
The config passed as YAML frontmatter or directives
#### Defined in
[packages/mermaid/src/types.ts:67](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L67)
---
### diagramType
**diagramType**: `string`
@@ -30,4 +18,4 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[packages/mermaid/src/types.ts:63](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L63)
[packages/mermaid/src/types.ts:50](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L50)

View File

@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
#### Defined in
[packages/mermaid/src/types.ts:90](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L90)
[packages/mermaid/src/types.ts:73](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L73)
---
@@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
[packages/mermaid/src/types.ts:63](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L63)
---
@@ -63,4 +63,4 @@ The svg code for the rendered graph.
#### Defined in
[packages/mermaid/src/types.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L76)
[packages/mermaid/src/types.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L59)

View File

@@ -18,7 +18,7 @@ The nodes to render. If this is set, `querySelector` will be ignored.
#### Defined in
[packages/mermaid/src/mermaid.ts:49](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L49)
[packages/mermaid/src/mermaid.ts:48](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L48)
---
@@ -44,7 +44,7 @@ A callback to call after each diagram is rendered.
#### Defined in
[packages/mermaid/src/mermaid.ts:53](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L53)
[packages/mermaid/src/mermaid.ts:52](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L52)
---
@@ -56,7 +56,7 @@ The query selector to use when finding elements to render. Default: `".mermaid"`
#### Defined in
[packages/mermaid/src/mermaid.ts:45](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L45)
[packages/mermaid/src/mermaid.ts:44](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L44)
---
@@ -68,4 +68,4 @@ If `true`, errors will be logged to the console, but not thrown. Default: `false
#### Defined in
[packages/mermaid/src/mermaid.ts:57](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L57)
[packages/mermaid/src/mermaid.ts:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L56)

View File

@@ -14,7 +14,7 @@
#### Defined in
[packages/mermaid/src/defaultConfig.ts:267](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L267)
[packages/mermaid/src/defaultConfig.ts:266](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L266)
---

View File

@@ -87,4 +87,4 @@
#### Defined in
[packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442)
[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440)

View File

@@ -64,7 +64,7 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [Mermaid Flow Visual Editor](https://www.mermaidflow.app) ✅
- [Mermerd](https://github.com/KarnerTh/mermerd)
- [Slab](https://slab.com) ✅
- [Swimm](https://docs.swimm.io/features/diagrams-and-charts) ✅
- [Swimm](https://docs.swimm.io/features/diagrams-and-charts/#mermaid--swimm--up-to-date-diagrams-) ✅
- [NotesHub](https://noteshub.app) ✅
- [Notion](https://notion.so) ✅
- [Observable](https://observablehq.com/@observablehq/mermaid) ✅
@@ -74,7 +74,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [Markdown for mermaid plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
- [redmine-mermaid](https://github.com/styz/redmine_mermaid)
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
- [Microsoft Loop](https://loop.cloud.microsoft) ✅
### LLM integrations
@@ -145,7 +144,7 @@ Communication tools and platforms
- [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid)
- [PmWiki](https://www.pmwiki.org)
- [MermaidJs Cookbook recipe](https://www.pmwiki.org/wiki/Cookbook/MermaidJs)
- [Semantic Media Wiki](https://www.semantic-mediawiki.org)
- [Semantic Media Wiki](https://semantic-mediawiki.org)
- [Mermaid Plugin](https://github.com/SemanticMediaWiki/Mermaid)
- [TiddlyWiki](https://tiddlywiki.com/)
- [mermaid-tw5: wrapper for Mermaid Live](https://github.com/efurlanm/mermaid-tw5)

View File

@@ -12,7 +12,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
<br />
<a href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;whiteboard" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light" alt="Mermaid&#0032;Whiteboard - Drag&#0032;&#0038;&#0032;Drop&#0032;your&#0032;Nodes&#0032;with&#0032;Mermaid&#0039;s&#0032;new&#0032;Whiteboard&#0033; | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://www.producthunt.com/products/mermaid-chart?utm_source=badge-follow&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/follow.svg?product_id=552855&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
## About
@@ -22,20 +22,17 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
- **Editor** - A web based editor for creating and editing Mermaid diagrams.
- **Mermaid AI** - Use our embedded AI Chat to generate diagrams from natural language descriptions.
- **Visual Editor** - The Visual Editor enables users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options.
- **Whiteboard** - A virtual whiteboard for creating and editing Mermaid diagrams.
- **AI Chat** - Use our embedded AI Chat to generate diagrams from natural language descriptions.
- **Plugins** - A plugin system for extending the functionality of Mermaid.
Official Mermaid Chart plugins:
- [Mermaid Chart GPT](https://chat.openai.com/g/g-1IRFKwq4G-mermaid-chart)
- [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)
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart)
- [JetBrains IDE](https://plugins.jetbrains.com/plugin/23043-mermaid-chart)
- [Google Docs](https://gsuite.google.com/marketplace/app/mermaidchart/947683068472)
- [Microsoft PowerPoint and Word](https://appsource.microsoft.com/en-us/product/office/WA200006214?tab=Overview)
Visit our [Plugins](https://www.mermaidchart.com/plugins) page for more information.

View File

@@ -63,7 +63,7 @@ import matplotlib.pyplot as plt
def mm(graph):
graphbytes = graph.encode("utf8")
base64_bytes = base64.urlsafe_b64encode(graphbytes)
base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii")
display(Image(url="https://mermaid.ink/img/" + base64_string))

View File

@@ -55,10 +55,6 @@ For a more detailed introduction to Mermaid and some of its more basic uses, loo
**Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project 🙏**
Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze.
[![Covered by Argos Visual Testing](https://argos-ci.com/badge-large.svg)](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
<a href="https://applitools.com/">

View File

@@ -6,18 +6,6 @@
# Blog
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
24 September 2024 · 5 mins
Discover 30 new shapes in Mermaid flowcharts, offering enhanced clarity, customization, and versatility for more dynamic and expressive visualizations.
## [Introducing Architecture Diagrams in Mermaid](https://www.mermaidchart.com/blog/posts/mermaid-supports-architecture-diagrams/)
2 September 2024 · 2 mins
Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love.
## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11/)
23 August 2024 · 2 mins

View File

@@ -1,275 +0,0 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/architecture.md](../../packages/mermaid/src/docs/syntax/architecture.md).
# Architecture Diagrams Documentation (v11.1.0+)
> In the context of mermaid-js, the architecture diagram is used to show the relationship between services and resources commonly found within the Cloud or CI/CD deployments. In an architecture diagram, services (nodes) are connected by edges. Related services can be placed within groups to better illustrate how they are organized.
## Example
```mermaid-example
architecture-beta
group api(cloud)[API]
service db(database)[Database] in api
service disk1(disk)[Storage] in api
service disk2(disk)[Storage] in api
service server(server)[Server] in api
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db
```
```mermaid
architecture-beta
group api(cloud)[API]
service db(database)[Database] in api
service disk1(disk)[Storage] in api
service disk2(disk)[Storage] in api
service server(server)[Server] in api
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db
```
## Syntax
The building blocks of an architecture are `groups`, `services`, `edges`, and `junctions`.
For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`.
To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component.
### Groups
The syntax for declaring a group is:
```
group {group id}({icon name})[{title}] (in {parent id})?
```
Put together:
```
group public_api(cloud)[Public API]
```
creates a group identified as `public_api`, uses the icon `cloud`, and has the label `Public API`.
Additionally, groups can be placed within a group using the optional `in` keyword
```
group private_api(cloud)[Private API] in public_api
```
### Services
The syntax for declaring a service is:
```
service {service id}({icon name})[{title}] (in {parent id})?
```
Put together:
```
service database(db)[Database]
```
creates the service identified as `database`, using the icon `db`, with the label `Database`.
If the service belongs to a group, it can be placed inside it through the optional `in` keyword
```
service database(db)[Database] in private_api
```
### Edges
The syntax for declaring an edge is:
```
{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}?
```
#### Edge Direction
The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B`
For example:
```
db:R -- L:server
```
creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`.
```
db:T -- L:server
```
creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`.
#### Arrows
Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right.
For example:
```
subnet:R --> L:gateway
```
creates an edge with the arrow going into the `gateway` service
#### Edges out of Groups
To have an edge go from a group to another group or service within another group, the `{group}` modifier can be added after the `serviceId`.
For example:
```
service server[Server] in groupOne
service subnet[Subnet] in groupTwo
server{group}:B --> T:subnet{group}
```
creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`.
It's important to note that `groupId`s cannot be used for specifying edges and the `{group}` modifier can only be used for services within a group.
### Junctions
Junctions are a special type of node which acts as a potential 4-way split between edges.
The syntax for declaring a junction is:
```
junction {junction id} (in {parent id})?
```
```mermaid-example
architecture-beta
service left_disk(disk)[Disk]
service top_disk(disk)[Disk]
service bottom_disk(disk)[Disk]
service top_gateway(internet)[Gateway]
service bottom_gateway(internet)[Gateway]
junction junctionCenter
junction junctionRight
left_disk:R -- L:junctionCenter
top_disk:B -- T:junctionCenter
bottom_disk:T -- B:junctionCenter
junctionCenter:R -- L:junctionRight
top_gateway:B -- T:junctionRight
bottom_gateway:T -- B:junctionRight
```
```mermaid
architecture-beta
service left_disk(disk)[Disk]
service top_disk(disk)[Disk]
service bottom_disk(disk)[Disk]
service top_gateway(internet)[Gateway]
service bottom_gateway(internet)[Gateway]
junction junctionCenter
junction junctionRight
left_disk:R -- L:junctionCenter
top_disk:B -- T:junctionCenter
bottom_disk:T -- B:junctionCenter
junctionCenter:R -- L:junctionRight
top_gateway:B -- T:junctionRight
bottom_gateway:T -- B:junctionRight
```
## 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 their own custom icons, by following the steps below.
The icon packs available can be found at [icones.js.org](https://icones.js.org/).
We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram.
Using JSON file directly from CDN:
```js
import mermaid from 'CDN/mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
]);
```
Using packages and a bundler:
```bash
npm install @iconify-json/logos
```
With lazy loading
```js
import mermaid from 'mermaid';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () => import('@iconify-json/logos').then((module) => module.icons),
},
]);
```
Without lazy loading
```js
import mermaid from 'mermaid';
import { icons } from '@iconify-json/logos';
mermaid.registerIconPacks([
{
name: icons.prefix, // To use the prefix defined in the icon pack
icons,
},
]);
```
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.
```mermaid-example
architecture-beta
group api(logos:aws-lambda)[API]
service db(logos:aws-aurora)[Database] in api
service disk1(logos:aws-glacier)[Storage] in api
service disk2(logos:aws-s3)[Storage] in api
service server(logos:aws-ec2)[Server] in api
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db
```
```mermaid
architecture-beta
group api(logos:aws-lambda)[API]
service db(logos:aws-aurora)[Database] in api
service disk1(logos:aws-glacier)[Storage] in api
service disk2(logos:aws-s3)[Storage] in api
service server(logos:aws-ec2)[Server] in api
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db
```

View File

@@ -211,27 +211,6 @@ block-beta
This example demonstrates how Mermaid dynamically adjusts the width of the columns to accommodate the widest block, in this case, 'a' and the composite block 'e'. This dynamic adjustment is essential for creating visually balanced and easy-to-understand diagrams.
**Merging Blocks Horizontally:**
In scenarios where you need to stack blocks horizontally, you can use column width to accomplish the task. Blocks can be arranged vertically by putting them in a single column. Here is how you can create a block diagram in which 4 blocks are stacked on top of each other:
```mermaid-example
block-beta
block
columns 1
a["A label"] b c d
end
```
```mermaid
block-beta
block
columns 1
a["A label"] b c d
end
```
In this example, the width of the merged block dynamically adjusts to the width of the largest child block.
With these advanced configuration options, Mermaid's block diagrams can be tailored to represent a wide array of complex systems and structures. The flexibility offered by these features enables users to create diagrams that are both informative and visually appealing. In the following sections, we will explore further capabilities, including different block shapes and linking options.
## 4. Block Varieties and Shapes

View File

@@ -286,7 +286,6 @@ erDiagram
- If you want the relationship label to be more than one word, you must use double quotes around the phrase
- If you don't want a label at all on a relationship, you must use an empty double-quoted string
- (v11.1.0+) If you want a multi-line label on a relationship, use `<br />` between the two lines (`"first line<br />second line"`)
## Styling

View File

@@ -298,634 +298,6 @@ flowchart TD
id1(((This is the text in the circle)))
```
## Expanded Node Shapes in Mermaid Flowcharts (v11.3.0+)
Mermaid introduces 30 new shapes to enhance the flexibility and precision of flowchart creation. These new shapes provide more options to represent processes, decisions, events, data storage visually, and other elements within your flowcharts, improving clarity and semantic meaning.
New Syntax for Shape Definition
Mermaid now supports a general syntax for defining shape types to accommodate the growing number of shapes. This syntax allows you to assign specific shapes to nodes using a clear and flexible format:
```
A@{ shape: rect }
```
This syntax creates a node A as a rectangle. It renders in the same way as `A["A"]`, or `A`.
### Complete List of New Shapes
Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases:
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
| ------------------------------------- | ---------------------- | -------------- | ------------------------------ | -------------------------------------------------------------- |
| **Process** | Rectangle | `rect` | Standard process shape | `proc`, `process`, `rectangle` |
| **Event** | Rounded Rectangle | `rounded` | Represents an event | `event` |
| **Terminal Point** | Stadium | `stadium` | Terminal point | `terminal`, `pill` |
| **Subprocess** | Framed Rectangle | `fr-rect` | Subprocess | `subprocess`,`subproc`, `framed-rectangle`, `subroutine` |
| **Database** | Cylinder | `cyl` | Database storage | `db`, `database`, `cylinder` |
| **Start** | Circle | `circle` | Starting point | `circ` |
| **Odd** | Odd | `odd` | Odd shape | |
| **Decision** | Diamond | `diam` | Decision-making step | `decision`, `diamond` |
| **Prepare Conditional** | Hexagon | `hex` | Preparation or condition step | `hexagon`, `prepare` |
| **Data Input/Output** | Lean Right | `lean-r` | Represents input or output | `lean-right`, `in-out` |
| **Data Input/Output** | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
| **Priority Action** | Trapezoid Base Bottom | `trap-b` | Priority action | `priority`, `trapezoid-bottom` |
| **Manual Operation** | Trapezoid Base Top | `trap-t` | Represents a manual task | `manual`, `trapezoid-top` |
| **Stop** | Double Circle | `dbl-circ` | Represents a stop point | `double-circle` |
| **Text Block** | Text Block | `text` | Text block | - |
| **Card** | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
| **Lined/Shaded Process** | Lined Rectangle | `lin-rect` | Lined process shape | `lined-rectangle`,`lined-process`, `lin-proc`,`shaded-process` |
| **Start** | Small Circle | `sm-circ` | Small starting point | `start`, `small-circle` |
| **Stop** | Framed Circle | `fr-circ` | Stop point | `stop`, `framed-circle` |
| **Fork/Join** | Filled Rectangle | `fork` | Fork or join in process flow | `join` |
| **Collate** | Hourglass | `hourglass` | Represents a collate operation | `hourglass` |
| **Comment** | Curly Brace | `brace` | Adds a comment | `comment`, `brace-l` |
| **Comment Right** | Curly Brace | `brace-r` | Adds a comment | - |
| **Comment with braces on both sides** | Curly Braces | `braces` | Adds a comment | - |
| **Com Link** | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
| **Document** | Document | `doc` | Represents a document | `doc`, `document` |
| **Delay** | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
| **Direct Access Storage** | Horizontal Cylinder | `h-cyl` | Direct access storage | `das`, `horizontal-cylinder` |
| **Disk Storage** | Lined Cylinder | `lin-cyl` | Disk storage | `disk`, `lined-cylinder` |
| **Display** | Curved Trapezoid | `curv-trap` | Represents a display | `curved-trapezoid`, `display` |
| **Divided Process** | Divided Rectangle | `div-rect` | Divided process shape | `div-proc`, `divided-rectangle`, `divided-process` |
| **Extract** | Triangle | `tri` | Extraction process | `extract`, `triangle` |
| **Internal Storage** | Window Pane | `win-pane` | Internal storage | `internal-storage`, `window-pane` |
| **Junction** | Filled Circle | `f-circ` | Junction point | `junction`, `filled-circle` |
| **Lined Document** | Lined Document | `lin-doc` | Lined document | `lined-document` |
| **Loop Limit** | Trapezoidal Pentagon | `notch-pent` | Loop limit step | `loop-limit`, `notched-pentagon` |
| **Manual File** | Flipped Triangle | `flip-tri` | Manual file operation | `manual-file`, `flipped-triangle` |
| **Manual Input** | Sloped Rectangle | `sl-rect` | Manual input step | `manual-input`, `sloped-rectangle` |
| **Multi-Document** | Stacked Document | `docs` | Multiple documents | `documents`, `st-doc`, `stacked-document` |
| **Multi-Process** | Stacked Rectangle | `st-rect` | Multiple processes | `procs`, `processes`, `stacked-rectangle` |
| **Paper Tape** | Flag | `flag` | Paper tape | `paper-tape` |
| **Stored Data** | Bow Tie Rectangle | `bow-rect` | Stored data | `stored-data`, `bow-tie-rectangle` |
| **Summary** | Crossed Circle | `cross-circ` | Summary | `summary`, `crossed-circle` |
| **Tagged Document** | Tagged Document | `tag-doc` | Tagged document | `tag-doc`, `tagged-document` |
| **Tagged Process** | Tagged Rectangle | `tag-rect` | Tagged process | `tagged-rectangle`,`tag-proc`, `tagged-process` |
### Example Flowchart with New Shapes
Heres an example flowchart that utilizes some of the newly introduced shapes:
```mermaid-example
flowchart RL
A@{ shape: manual-file, label: "File Handling"}
B@{ shape: manual-input, label: "User Input"}
C@{ shape: docs, label: "Multiple Documents"}
D@{ shape: procs, label: "Process Automation"}
E@{ shape: paper-tape, label: "Paper Records"}
```
```mermaid
flowchart RL
A@{ shape: manual-file, label: "File Handling"}
B@{ shape: manual-input, label: "User Input"}
C@{ shape: docs, label: "Multiple Documents"}
D@{ shape: procs, label: "Process Automation"}
E@{ shape: paper-tape, label: "Paper Records"}
```
### Process
```mermaid-example
flowchart TD
A@{ shape: rect, label: "This is a process" }
```
```mermaid
flowchart TD
A@{ shape: rect, label: "This is a process" }
```
### Event
```mermaid-example
flowchart TD
A@{ shape: rounded, label: "This is an event" }
```
```mermaid
flowchart TD
A@{ shape: rounded, label: "This is an event" }
```
### Terminal Point (Stadium)
```mermaid-example
flowchart TD
A@{ shape: stadium, label: "Terminal point" }
```
```mermaid
flowchart TD
A@{ shape: stadium, label: "Terminal point" }
```
### Subprocess
```mermaid-example
flowchart TD
A@{ shape: subproc, label: "This is a subprocess" }
```
```mermaid
flowchart TD
A@{ shape: subproc, label: "This is a subprocess" }
```
### Database (Cylinder)
```mermaid-example
flowchart TD
A@{ shape: cyl, label: "Database" }
```
```mermaid
flowchart TD
A@{ shape: cyl, label: "Database" }
```
### Start (Circle)
```mermaid-example
flowchart TD
A@{ shape: circle, label: "Start" }
```
```mermaid
flowchart TD
A@{ shape: circle, label: "Start" }
```
### Odd
```mermaid-example
flowchart TD
A@{ shape: odd, label: "Odd shape" }
```
```mermaid
flowchart TD
A@{ shape: odd, label: "Odd shape" }
```
### Decision (Diamond)
```mermaid-example
flowchart TD
A@{ shape: diamond, label: "Decision" }
```
```mermaid
flowchart TD
A@{ shape: diamond, label: "Decision" }
```
### Prepare Conditional (Hexagon)
```mermaid-example
flowchart TD
A@{ shape: hex, label: "Prepare conditional" }
```
```mermaid
flowchart TD
A@{ shape: hex, label: "Prepare conditional" }
```
### Data Input/Output (Lean Right)
```mermaid-example
flowchart TD
A@{ shape: lean-r, label: "Input/Output" }
```
```mermaid
flowchart TD
A@{ shape: lean-r, label: "Input/Output" }
```
### Data Input/Output (Lean Left)
```mermaid-example
flowchart TD
A@{ shape: lean-l, label: "Output/Input" }
```
```mermaid
flowchart TD
A@{ shape: lean-l, label: "Output/Input" }
```
### Priority Action (Trapezoid Base Bottom)
```mermaid-example
flowchart TD
A@{ shape: trap-b, label: "Priority action" }
```
```mermaid
flowchart TD
A@{ shape: trap-b, label: "Priority action" }
```
### Manual Operation (Trapezoid Base Top)
```mermaid-example
flowchart TD
A@{ shape: trap-t, label: "Manual operation" }
```
```mermaid
flowchart TD
A@{ shape: trap-t, label: "Manual operation" }
```
### Stop (Double Circle)
```mermaid-example
flowchart TD
A@{ shape: dbl-circ, label: "Stop" }
```
```mermaid
flowchart TD
A@{ shape: dbl-circ, label: "Stop" }
```
### Text Block
```mermaid-example
flowchart TD
A@{ shape: text, label: "This is a text block" }
```
```mermaid
flowchart TD
A@{ shape: text, label: "This is a text block" }
```
### Card (Notched Rectangle)
```mermaid-example
flowchart TD
A@{ shape: notch-rect, label: "Card" }
```
```mermaid
flowchart TD
A@{ shape: notch-rect, label: "Card" }
```
### Lined/Shaded Process
```mermaid-example
flowchart TD
A@{ shape: lin-rect, label: "Lined process" }
```
```mermaid
flowchart TD
A@{ shape: lin-rect, label: "Lined process" }
```
### Start (Small Circle)
```mermaid-example
flowchart TD
A@{ shape: sm-circ, label: "Small start" }
```
```mermaid
flowchart TD
A@{ shape: sm-circ, label: "Small start" }
```
### Stop (Framed Circle)
```mermaid-example
flowchart TD
A@{ shape: framed-circle, label: "Stop" }
```
```mermaid
flowchart TD
A@{ shape: framed-circle, label: "Stop" }
```
### Fork/Join (Long Rectangle)
```mermaid-example
flowchart TD
A@{ shape: fork, label: "Fork or Join" }
```
```mermaid
flowchart TD
A@{ shape: fork, label: "Fork or Join" }
```
### Collate (Hourglass)
```mermaid-example
flowchart TD
A@{ shape: hourglass, label: "Collate" }
```
```mermaid
flowchart TD
A@{ shape: hourglass, label: "Collate" }
```
### Comment (Curly Brace)
```mermaid-example
flowchart TD
A@{ shape: comment, label: "Comment" }
```
```mermaid
flowchart TD
A@{ shape: comment, label: "Comment" }
```
### Comment Right (Curly Brace Right)
```mermaid-example
flowchart TD
A@{ shape: brace-r, label: "Comment" }
```
```mermaid
flowchart TD
A@{ shape: brace-r, label: "Comment" }
```
### Comment with braces on both sides
```mermaid-example
flowchart TD
A@{ shape: braces, label: "Comment" }
```
```mermaid
flowchart TD
A@{ shape: braces, label: "Comment" }
```
### Com Link (Lightning Bolt)
```mermaid-example
flowchart TD
A@{ shape: bolt, label: "Communication link" }
```
```mermaid
flowchart TD
A@{ shape: bolt, label: "Communication link" }
```
### Document
```mermaid-example
flowchart TD
A@{ shape: doc, label: "Document" }
```
```mermaid
flowchart TD
A@{ shape: doc, label: "Document" }
```
### Delay (Half-Rounded Rectangle)
```mermaid-example
flowchart TD
A@{ shape: delay, label: "Delay" }
```
```mermaid
flowchart TD
A@{ shape: delay, label: "Delay" }
```
### Direct Access Storage (Horizontal Cylinder)
```mermaid-example
flowchart TD
A@{ shape: das, label: "Direct access storage" }
```
```mermaid
flowchart TD
A@{ shape: das, label: "Direct access storage" }
```
### Disk Storage (Lined Cylinder)
```mermaid-example
flowchart TD
A@{ shape: lin-cyl, label: "Disk storage" }
```
```mermaid
flowchart TD
A@{ shape: lin-cyl, label: "Disk storage" }
```
### Display (Curved Trapezoid)
```mermaid-example
flowchart TD
A@{ shape: curv-trap, label: "Display" }
```
```mermaid
flowchart TD
A@{ shape: curv-trap, label: "Display" }
```
### Divided Process (Divided Rectangle)
```mermaid-example
flowchart TD
A@{ shape: div-rect, label: "Divided process" }
```
```mermaid
flowchart TD
A@{ shape: div-rect, label: "Divided process" }
```
### Extract (Small Triangle)
```mermaid-example
flowchart TD
A@{ shape: tri, label: "Extract" }
```
```mermaid
flowchart TD
A@{ shape: tri, label: "Extract" }
```
### Internal Storage (Window Pane)
```mermaid-example
flowchart TD
A@{ shape: win-pane, label: "Internal storage" }
```
```mermaid
flowchart TD
A@{ shape: win-pane, label: "Internal storage" }
```
### Junction (Filled Circle)
```mermaid-example
flowchart TD
A@{ shape: f-circ, label: "Junction" }
```
```mermaid
flowchart TD
A@{ shape: f-circ, label: "Junction" }
```
### Lined Document
```mermaid-example
flowchart TD
A@{ shape: lin-doc, label: "Lined document" }
```
```mermaid
flowchart TD
A@{ shape: lin-doc, label: "Lined document" }
```
### Loop Limit (Notched Pentagon)
```mermaid-example
flowchart TD
A@{ shape: notch-pent, label: "Loop limit" }
```
```mermaid
flowchart TD
A@{ shape: notch-pent, label: "Loop limit" }
```
### Manual File (Flipped Triangle)
```mermaid-example
flowchart TD
A@{ shape: flip-tri, label: "Manual file" }
```
```mermaid
flowchart TD
A@{ shape: flip-tri, label: "Manual file" }
```
### Manual Input (Sloped Rectangle)
```mermaid-example
flowchart TD
A@{ shape: sl-rect, label: "Manual input" }
```
```mermaid
flowchart TD
A@{ shape: sl-rect, label: "Manual input" }
```
### Multi-Document (Stacked Document)
```mermaid-example
flowchart TD
A@{ shape: docs, label: "Multiple documents" }
```
```mermaid
flowchart TD
A@{ shape: docs, label: "Multiple documents" }
```
### Multi-Process (Stacked Rectangle)
```mermaid-example
flowchart TD
A@{ shape: processes, label: "Multiple processes" }
```
```mermaid
flowchart TD
A@{ shape: processes, label: "Multiple processes" }
```
### Paper Tape (Flag)
```mermaid-example
flowchart TD
A@{ shape: flag, label: "Paper tape" }
```
```mermaid
flowchart TD
A@{ shape: flag, label: "Paper tape" }
```
### Stored Data (Bow Tie Rectangle)
```mermaid-example
flowchart TD
A@{ shape: bow-rect, label: "Stored data" }
```
```mermaid
flowchart TD
A@{ shape: bow-rect, label: "Stored data" }
```
### Summary (Crossed Circle)
```mermaid-example
flowchart TD
A@{ shape: cross-circ, label: "Summary" }
```
```mermaid
flowchart TD
A@{ shape: cross-circ, label: "Summary" }
```
### Tagged Document
```mermaid-example
flowchart TD
A@{ shape: tag-doc, label: "Tagged document" }
```
```mermaid
flowchart TD
A@{ shape: tag-doc, label: "Tagged document" }
```
### Tagged Process (Tagged Rectangle)
```mermaid-example
flowchart TD
A@{ shape: tag-rect, label: "Tagged process" }
```
```mermaid
flowchart TD
A@{ shape: tag-rect, label: "Tagged process" }
```
## Links between nodes
Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link.

View File

@@ -155,9 +155,6 @@ end
box rgb(33,66,99)
... actors ...
end
box rgba(33,66,99,0.5)
... actors ...
end
```
> **Note**
@@ -583,12 +580,6 @@ sequenceDiagram
It is possible to highlight flows by providing colored background rects. This is done by the notation
```
rect COLOR
... content ...
end
```
The colors are defined using rgb and rgba syntax.
```

View File

@@ -23,7 +23,6 @@ export default tseslint.config(
'**/generated/',
'**/coverage/',
'packages/mermaid/src/config.type.ts',
'packages/mermaid/src/docs/.vitepress/components.d.ts',
],
},
{

View File

@@ -4,7 +4,7 @@
"version": "10.2.4",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1",
"packageManager": "pnpm@9.7.1+sha512.faf344af2d6ca65c4c5c8c2224ea77a81a5e8859cbc4e06b1511ddce2f0151512431dd19e6aff31f2c6a8f5f2aced9bd2273e1fed7dd4de1868984059d2c4247",
"keywords": [
"diagram",
"markdown",
@@ -42,8 +42,7 @@
"test": "pnpm lint && vitest run",
"test:watch": "vitest --watch",
"test:coverage": "vitest --coverage",
"test:check:tsc": "tsx scripts/tsc-check.ts",
"prepare": "husky && pnpm build",
"prepare": "husky install && pnpm build",
"pre-commit": "lint-staged"
},
"repository": {
@@ -89,7 +88,7 @@
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cspell": "^8.6.0",
"cypress": "^13.14.1",
"cypress": "^13.11.0",
"cypress-image-snapshot": "^4.0.1",
"esbuild": "^0.21.5",
"eslint": "^9.4.0",
@@ -97,7 +96,7 @@
"eslint-plugin-cypress": "^3.3.0",
"eslint-plugin-html": "^8.1.1",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsdoc": "^50.0.0",
"eslint-plugin-jsdoc": "^48.2.9",
"eslint-plugin-json": "^4.0.0",
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.0.0",
@@ -117,6 +116,7 @@
"markdown-table": "^3.0.3",
"nyc": "^15.1.0",
"path-browserify": "^1.0.1",
"pnpm": "^8.15.5",
"prettier": "^3.2.5",
"prettier-plugin-jsdoc": "^1.3.0",
"rimraf": "^5.0.5",
@@ -131,10 +131,5 @@
},
"nyc": {
"report-dir": "coverage/cypress"
},
"pnpm": {
"patchedDependencies": {
"roughjs": "patches/roughjs.patch"
}
}
}

View File

@@ -1,29 +1,5 @@
# @mermaid-js/layout-elk
## 0.1.5
### Patch Changes
- [#5825](https://github.com/mermaid-js/mermaid/pull/5825) [`233e36c`](https://github.com/mermaid-js/mermaid/commit/233e36c9884fcce141a72ce7c845179781e18632) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - chore: Update render options
- Updated dependencies [[`6c5b7ce`](https://github.com/mermaid-js/mermaid/commit/6c5b7ce9f41c0fbd59fe03dbefc8418d97697f0a), [`9e3aa70`](https://github.com/mermaid-js/mermaid/commit/9e3aa705ae21fd4898504ab22d775a9e437b898e), [`de2c05c`](https://github.com/mermaid-js/mermaid/commit/de2c05cd5463af68d19dd7b6b3f1303d69ddb2dd)]:
- mermaid@11.3.0
## 0.1.4
### Patch Changes
- [#5847](https://github.com/mermaid-js/mermaid/pull/5847) [`dd03043`](https://github.com/mermaid-js/mermaid/commit/dd0304387e85fc57a9ebb666f89ef788c012c2c5) Thanks [@sidharthv96](https://github.com/sidharthv96)! - chore: fix render types
## 0.1.3
### Patch Changes
- [#5810](https://github.com/mermaid-js/mermaid/pull/5810) [`33a809f`](https://github.com/mermaid-js/mermaid/commit/33a809f09a9aa1f84ba06201ab550bad81c3ff65) Thanks [@knsv](https://github.com/knsv)! - fix: Updates to the default elk configuration
feat: exposing cycleBreakingStrategy to the configuration so that it can be modified suing the configuration.
- Updated dependencies [[`6ecdf7b`](https://github.com/mermaid-js/mermaid/commit/6ecdf7be688efdc53c52fea3ba891327242bc890), [`28bd07f`](https://github.com/mermaid-js/mermaid/commit/28bd07fdeb4fc981107d21317ec6160b31f80116), [`8e640da`](https://github.com/mermaid-js/mermaid/commit/8e640da5436e8ae013b11b1c1821a9afcc15d0d3), [`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048), [`16faef4`](https://github.com/mermaid-js/mermaid/commit/16faef4613b91a7d3a98a1563c25b57f9238acc7)]:
- mermaid@11.1.0
## 0.1.2
### Patch Changes

View File

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

View File

@@ -33,11 +33,10 @@ export const render = async (
};
graph.children.push(child);
nodeDb[node.id] = child;
const config = getConfig();
// Add the element to the DOM
if (!node.isGroup) {
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
const childNodeEl = await insertNode(nodeEl, node, node.dir);
boundingBox = childNodeEl.node().getBBox();
child.domId = childNodeEl;
child.width = boundingBox.width;
@@ -51,7 +50,7 @@ export const render = async (
// @ts-ignore TODO: fix this
const { shapeSvg, bbox } = await labelHelper(nodeEl, node, undefined, true);
labelData.width = bbox.width;
labelData.wrappingWidth = config.flowchart!.wrappingWidth;
labelData.wrappingWidth = getConfig().flowchart!.wrappingWidth;
// Give some padding for elk
labelData.height = bbox.height - 2;
labelData.labelNode = shapeSvg.node();
@@ -225,7 +224,7 @@ export const render = async (
* Add edges to graph based on parsed graph definition
*/
const addEdges = async function (
dataForLayout: { edges: any; direction?: string },
dataForLayout: { edges: any; direction: string },
graph: {
id?: string;
layoutOptions?: {
@@ -750,37 +749,17 @@ export const render = async (
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
'elk.algorithm': algorithm,
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
'nodePlacement.strategy': data4Layout.config.elk.nodePlacementStrategy,
'elk.layered.mergeEdges': data4Layout.config.elk.mergeEdges,
'elk.direction': 'DOWN',
'spacing.baseValue': 35,
'elk.layered.unnecessaryBendpoints': true,
'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy,
// 'spacing.nodeNode': 20,
// 'spacing.nodeNodeBetweenLayers': 25,
// 'spacing.edgeNode': 20,
// 'spacing.edgeNodeBetweenLayers': 10,
// 'spacing.edgeEdge': 10,
// 'spacing.edgeEdgeBetweenLayers': 20,
// 'spacing.nodeSelfLoop': 20,
// Tweaking options
// 'elk.layered.nodePlacement.favorStraightEdges': true,
// 'nodePlacement.feedbackEdges': true,
// 'elk.layered.wrapping.multiEdge.improveCuts': true,
// 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true,
// 'elk.layered.wrapping.strategy': 'MULTI_EDGE',
// 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY',
// 'elk.layered.mergeHierarchyEdges': true,
// 'elk.layered.feedbackEdges': true,
// 'elk.layered.crossingMinimization.semiInteractive': true,
// 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1,
// 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0,
// 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK',
// 'elk.insideSelfLoops.activate': true,
// 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE',
// 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES
// 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES
'spacing.baseValue': 30,
// 'spacing.nodeNode': 40,
// 'spacing.nodeNodeBetweenLayers': 45,
// 'spacing.edgeNode': 40,
// 'spacing.edgeNodeBetweenLayers': 30,
// 'spacing.edgeEdge': 30,
// 'spacing.edgeEdgeBetweenLayers': 40,
// 'spacing.nodeSelfLoop': 50,
},
children: [],
edges: [],
@@ -838,8 +817,8 @@ export const render = async (
...node.layoutOptions,
'elk.algorithm': algorithm,
'elk.direction': dir2ElkDirection(node.dir),
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,
'nodePlacement.strategy': data4Layout.config['elk.nodePlacement.strategy'],
'elk.layered.mergeEdges': data4Layout.config['elk.mergeEdges'],
'elk.hierarchyHandling': 'SEPARATE_CHILDREN',
};
}

View File

@@ -1,73 +1,5 @@
# mermaid
## 11.3.0
### Minor Changes
- [#5825](https://github.com/mermaid-js/mermaid/pull/5825) [`9e3aa70`](https://github.com/mermaid-js/mermaid/commit/9e3aa705ae21fd4898504ab22d775a9e437b898e) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - New Flowchart Shapes (with new syntax)
### Patch Changes
- [#5849](https://github.com/mermaid-js/mermaid/pull/5849) [`6c5b7ce`](https://github.com/mermaid-js/mermaid/commit/6c5b7ce9f41c0fbd59fe03dbefc8418d97697f0a) Thanks [@ReneLombard](https://github.com/ReneLombard)! - Fixed an issue when the mermaid classdiagram crashes when adding a . to the namespace.
Forexample
```mermaid
classDiagram
namespace Company.Project.Module {
class GenericClass~T~ {
+addItem(item: T)
+getItem() T
}
}
```
- [#5914](https://github.com/mermaid-js/mermaid/pull/5914) [`de2c05c`](https://github.com/mermaid-js/mermaid/commit/de2c05cd5463af68d19dd7b6b3f1303d69ddb2dd) Thanks [@aloisklink](https://github.com/aloisklink)! - Ban DOMPurify v3.1.7 as a dependency
## 11.2.1
### Patch Changes
- [#5856](https://github.com/mermaid-js/mermaid/pull/5856) [`bfd8c63`](https://github.com/mermaid-js/mermaid/commit/bfd8c63daaa8420e57da9953922b9f0c94123064) Thanks [@knsv](https://github.com/knsv)! - Fix for issue with calculation of label width when using in firefox
## 11.2.0
### Minor Changes
- [#5831](https://github.com/mermaid-js/mermaid/pull/5831) [`64abf29`](https://github.com/mermaid-js/mermaid/commit/64abf29ea870eaa47148197f95ce714f85bd7eea) Thanks [@sidharthv96](https://github.com/sidharthv96)! - feat: Return parsed config from mermaid.parse
### Patch Changes
- [#5838](https://github.com/mermaid-js/mermaid/pull/5838) [`5e75320`](https://github.com/mermaid-js/mermaid/commit/5e75320d49eab65aca630dcc3c04c8d620a8bbf7) Thanks [@bollwyvl](https://github.com/bollwyvl)! - fix: Replace $root with relative paths
## 11.1.1
### Patch Changes
- [#5828](https://github.com/mermaid-js/mermaid/pull/5828) [`4c43d21`](https://github.com/mermaid-js/mermaid/commit/4c43d21196f784b6f483ae635fc462329f3d176f) Thanks [@knsv](https://github.com/knsv)! - fix: Fix for issue where self-loops in the root of diagrams break the rendering
## 11.1.0
### Minor Changes
- [#5793](https://github.com/mermaid-js/mermaid/pull/5793) [`6ecdf7b`](https://github.com/mermaid-js/mermaid/commit/6ecdf7be688efdc53c52fea3ba891327242bc890) Thanks [@sidharthv96](https://github.com/sidharthv96)! - feat: Add support for iconify icons
- [#5711](https://github.com/mermaid-js/mermaid/pull/5711) [`8e640da`](https://github.com/mermaid-js/mermaid/commit/8e640da5436e8ae013b11b1c1821a9afcc15d0d3) Thanks [@NicolasNewman](https://github.com/NicolasNewman)! - feat(er): allow multi-line relationship labels
- [#5452](https://github.com/mermaid-js/mermaid/pull/5452) [`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048) Thanks [@NicolasNewman](https://github.com/NicolasNewman)! - New Diagram: Architecture
Adds architecture diagrams which allows users to show relations between services.
### Patch Changes
- [#5810](https://github.com/mermaid-js/mermaid/pull/5810) [`28bd07f`](https://github.com/mermaid-js/mermaid/commit/28bd07fdeb4fc981107d21317ec6160b31f80116) Thanks [@knsv](https://github.com/knsv)! - Fix for self loops in cluster
Supporting legacy defaultRenderer directive
- [#5789](https://github.com/mermaid-js/mermaid/pull/5789) [`16faef4`](https://github.com/mermaid-js/mermaid/commit/16faef4613b91a7d3a98a1563c25b57f9238acc7) Thanks [@sidharthv96](https://github.com/sidharthv96)! - chore: Move icons to architecture, remove full icon sets to reduce bundle size
- Updated dependencies [[`256a148`](https://github.com/mermaid-js/mermaid/commit/256a148bbf484fc7db6c19f94dd69d5d268ee048), [`7d8143b`](https://github.com/mermaid-js/mermaid/commit/7d8143b917ee3562149a0e0a821ed2d6f29cc05d)]:
- @mermaid-js/parser@0.3.0
## 11.0.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.3.0",
"version": "11.0.2",
"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",
@@ -68,16 +68,14 @@
},
"dependencies": {
"@braintree/sanitize-url": "^7.0.1",
"@iconify/utils": "^2.1.32",
"@mermaid-js/parser": "workspace:^",
"cytoscape": "^3.29.2",
"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.10",
"dayjs": "^1.11.10",
"dompurify": "^3.0.11 <3.1.7",
"dompurify": "^3.0.11",
"katex": "^0.16.9",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
@@ -89,9 +87,7 @@
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.0",
"@iconify/types": "^2.0.0",
"@types/cytoscape": "^3.21.4",
"@types/cytoscape-fcose": "^2.2.4",
"@types/d3": "^7.4.3",
"@types/d3-sankey": "^0.12.4",
"@types/d3-scale": "^4.0.8",

View File

@@ -5,34 +5,23 @@
* So contributors adding new features will only have to add the placeholder and not worry about updating the version number.
*
*/
import { readFile, writeFile } from 'fs/promises';
import { posix } from 'path';
import {
getFilesFromGlobs,
getGlobs,
MERMAID_RELEASE_VERSION,
readSyncedUTF8file,
getFilesFromGlobs,
SOURCE_DOCS_DIR,
readSyncedUTF8file,
MERMAID_RELEASE_VERSION,
} from './docs.mjs';
import { writeFile } from 'fs/promises';
const verifyOnly: boolean = process.argv.includes('--verify');
const versionPlaceholder = '<MERMAID_RELEASE_VERSION>';
const verifyDocumentation = async () => {
const fileContent = await readFile('./src/docs/community/contributing.md', 'utf-8');
if (!fileContent.includes(versionPlaceholder)) {
console.error(
`The placeholder ${versionPlaceholder} is not present in the contributing.md file.`
);
process.exit(1);
}
};
const main = async () => {
await verifyDocumentation();
const sourceDirGlob = posix.join('.', SOURCE_DOCS_DIR, '**');
const mdFileGlobs = getGlobs([posix.join(sourceDirGlob, '*.md')]);
mdFileGlobs.push('!**/community/contributing.md');
mdFileGlobs.push('!**/community/development.md', '!**/community/code.md');
const mdFiles = await getFilesFromGlobs(mdFileGlobs);
mdFiles.sort();
const mdFilesWithPlaceholder: string[] = [];

View File

@@ -99,16 +99,6 @@ export interface MermaidConfig {
*
*/
nodePlacementStrategy?: 'SIMPLE' | 'NETWORK_SIMPLEX' | 'LINEAR_SEGMENTS' | 'BRANDES_KOEPF';
/**
* This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops.
*
*/
cycleBreakingStrategy?:
| 'GREEDY'
| 'DEPTH_FIRST'
| 'INTERACTIVE'
| 'MODEL_ORDER'
| 'GREEDY_MODEL_ORDER';
};
darkMode?: boolean;
htmlLabels?: boolean;
@@ -191,7 +181,6 @@ export interface MermaidConfig {
quadrantChart?: QuadrantChartConfig;
xyChart?: XYChartConfig;
requirement?: RequirementDiagramConfig;
architecture?: ArchitectureDiagramConfig;
mindmap?: MindmapDiagramConfig;
gitGraph?: GitGraphDiagramConfig;
c4?: C4DiagramConfig;
@@ -1002,17 +991,6 @@ export interface RequirementDiagramConfig extends BaseDiagramConfig {
rect_padding?: number;
line_height?: number;
}
/**
* The object containing configurations specific for architecture diagrams
*
* This interface was referenced by `MermaidConfig`'s JSON-Schema
* via the `definition` "ArchitectureDiagramConfig".
*/
export interface ArchitectureDiagramConfig extends BaseDiagramConfig {
padding?: number;
iconSize?: number;
fontSize?: number;
}
/**
* The object containing configurations specific for mindmap diagrams
*

View File

@@ -87,7 +87,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
// insertCluster(clusters, graph.node(v));
} else {
log.info('Node - the non recursive path', v, node.id, node);
await insertNode(nodes, graph.node(v), { config: siteConfig, dir });
await insertNode(nodes, graph.node(v), dir);
}
}
})

View File

@@ -1131,7 +1131,7 @@ const shapes = {
let nodeElems = {};
export const insertNode = async (elem, node, renderOptions) => {
export const insertNode = async (elem, node, dir) => {
let newEl;
let el;
@@ -1144,9 +1144,9 @@ export const insertNode = async (elem, node, renderOptions) => {
target = node.linkTarget || '_blank';
}
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
el = await shapes[node.shape](newEl, node, renderOptions);
el = await shapes[node.shape](newEl, node, dir);
} else {
el = await shapes[node.shape](elem, node, renderOptions);
el = await shapes[node.shape](elem, node, dir);
newEl = el;
}
if (node.tooltip) {

View File

@@ -21,9 +21,8 @@ const config: RequiredDeep<MermaidConfig> = {
// TODO: Should we replace these with `null` so that they can go in the JSON Schema?
deterministicIDSeed: undefined,
elk: {
// mergeEdges is needed here to be considered
mergeEdges: false,
nodePlacementStrategy: 'BRANDES_KOEPF',
nodePlacementStrategy: 'SIMPLE',
},
themeCSS: undefined,

View File

@@ -22,7 +22,6 @@ import mindmap from '../diagrams/mindmap/detector.js';
import sankey from '../diagrams/sankey/sankeyDetector.js';
import { packet } from '../diagrams/packet/detector.js';
import block from '../diagrams/block/blockDetector.js';
import architecture from '../diagrams/architecture/architectureDetector.js';
import { registerLazyLoadedDiagrams } from './detectType.js';
import { registerDiagram } from './diagramAPI.js';
@@ -91,7 +90,6 @@ export const addDiagrams = () => {
sankey,
packet,
xychart,
block,
architecture
block
);
};

View File

@@ -1,333 +0,0 @@
import type { ArchitectureDiagramConfig } from '../../config.type.js';
import DEFAULT_CONFIG from '../../defaultConfig.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { D3Element } from '../../types.js';
import { ImperativeState } from '../../utils/imperativeState.js';
import {
clear as commonClear,
getAccDescription,
getAccTitle,
getDiagramTitle,
setAccDescription,
setAccTitle,
setDiagramTitle,
} from '../common/commonDb.js';
import type {
ArchitectureDB,
ArchitectureDirectionPair,
ArchitectureDirectionPairMap,
ArchitectureEdge,
ArchitectureGroup,
ArchitectureJunction,
ArchitectureNode,
ArchitectureService,
ArchitectureSpatialMap,
ArchitectureState,
} from './architectureTypes.js';
import {
getArchitectureDirectionPair,
isArchitectureDirection,
isArchitectureJunction,
isArchitectureService,
shiftPositionByArchitectureDirectionPair,
} from './architectureTypes.js';
const DEFAULT_ARCHITECTURE_CONFIG: Required<ArchitectureDiagramConfig> =
DEFAULT_CONFIG.architecture;
const state = new ImperativeState<ArchitectureState>(() => ({
nodes: {},
groups: {},
edges: [],
registeredIds: {},
config: DEFAULT_ARCHITECTURE_CONFIG,
dataStructures: undefined,
elements: {},
}));
const clear = (): void => {
state.reset();
commonClear();
};
const addService = function ({
id,
icon,
in: parent,
title,
iconText,
}: Omit<ArchitectureService, 'edges'>) {
if (state.records.registeredIds[id] !== undefined) {
throw new Error(
`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`
);
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The service [${id}] cannot be placed within itself`);
}
if (state.records.registeredIds[parent] === undefined) {
throw new Error(
`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`
);
}
if (state.records.registeredIds[parent] === 'node') {
throw new Error(`The service [${id}]'s parent is not a group`);
}
}
state.records.registeredIds[id] = 'node';
state.records.nodes[id] = {
id,
type: 'service',
icon,
iconText,
title,
edges: [],
in: parent,
};
};
const getServices = (): ArchitectureService[] =>
Object.values(state.records.nodes).filter<ArchitectureService>(isArchitectureService);
const addJunction = function ({ id, in: parent }: Omit<ArchitectureJunction, 'edges'>) {
state.records.registeredIds[id] = 'node';
state.records.nodes[id] = {
id,
type: 'junction',
edges: [],
in: parent,
};
};
const getJunctions = (): ArchitectureJunction[] =>
Object.values(state.records.nodes).filter<ArchitectureJunction>(isArchitectureJunction);
const getNodes = (): ArchitectureNode[] => Object.values(state.records.nodes);
const getNode = (id: string): ArchitectureNode | null => state.records.nodes[id];
const addGroup = function ({ id, icon, in: parent, title }: ArchitectureGroup) {
if (state.records.registeredIds[id] !== undefined) {
throw new Error(
`The group id [${id}] is already in use by another ${state.records.registeredIds[id]}`
);
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The group [${id}] cannot be placed within itself`);
}
if (state.records.registeredIds[parent] === undefined) {
throw new Error(
`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`
);
}
if (state.records.registeredIds[parent] === 'node') {
throw new Error(`The group [${id}]'s parent is not a group`);
}
}
state.records.registeredIds[id] = 'group';
state.records.groups[id] = {
id,
icon,
title,
in: parent,
};
};
const getGroups = (): ArchitectureGroup[] => {
return Object.values(state.records.groups);
};
const addEdge = function ({
lhsId,
rhsId,
lhsDir,
rhsDir,
lhsInto,
rhsInto,
lhsGroup,
rhsGroup,
title,
}: ArchitectureEdge<string>) {
if (!isArchitectureDirection(lhsDir)) {
throw new Error(
`Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${lhsDir}`
);
}
if (!isArchitectureDirection(rhsDir)) {
throw new Error(
`Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${rhsDir}`
);
}
if (state.records.nodes[lhsId] === undefined && state.records.groups[lhsId] === undefined) {
throw new Error(
`The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
if (state.records.nodes[rhsId] === undefined && state.records.groups[lhsId] === undefined) {
throw new Error(
`The right-hand id [${rhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
const lhsGroupId = state.records.nodes[lhsId].in;
const rhsGroupId = state.records.nodes[rhsId].in;
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
const edge = {
lhsId,
lhsDir,
lhsInto,
lhsGroup,
rhsId,
rhsDir,
rhsInto,
rhsGroup,
title,
};
state.records.edges.push(edge);
if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) {
state.records.nodes[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
state.records.nodes[rhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
}
};
const getEdges = (): ArchitectureEdge[] => state.records.edges;
/**
* Returns the current diagram's adjacency list & spatial map.
* If they have not been created, run the algorithms to generate them.
* @returns
*/
const getDataStructures = () => {
if (state.records.dataStructures === undefined) {
// Create an adjacency list of the diagram to perform BFS on
// Outer reduce applied on all services
// Inner reduce applied on the edges for a service
const adjList = Object.entries(state.records.nodes).reduce<
Record<string, ArchitectureDirectionPairMap>
>((prevOuter, [id, service]) => {
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
if (edge.lhsId === id) {
// source is LHS
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
if (pair) {
prevInner[pair] = edge.rhsId;
}
} else {
// source is RHS
const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir);
if (pair) {
prevInner[pair] = edge.lhsId;
}
}
return prevInner;
}, {});
return prevOuter;
}, {});
// Configuration for the initial pass of BFS
const firstId = Object.keys(adjList)[0];
const visited = { [firstId]: 1 };
const notVisited = Object.keys(adjList).reduce(
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
{} as Record<string, number>
);
// Perform BFS on the adjacency list
const BFS = (startingId: string): ArchitectureSpatialMap => {
const spatialMap = { [startingId]: [0, 0] };
const queue = [startingId];
while (queue.length > 0) {
const id = queue.shift();
if (id) {
visited[id] = 1;
delete notVisited[id];
const adj = adjList[id];
const [posX, posY] = spatialMap[id];
Object.entries(adj).forEach(([dir, rhsId]) => {
if (!visited[rhsId]) {
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair(
[posX, posY],
dir as ArchitectureDirectionPair
);
queue.push(rhsId);
}
});
}
}
return spatialMap;
};
const spatialMaps = [BFS(firstId)];
// If our diagram is disconnected, keep adding additional spatial maps until all disconnected graphs have been found
while (Object.keys(notVisited).length > 0) {
spatialMaps.push(BFS(Object.keys(notVisited)[0]));
}
state.records.dataStructures = {
adjList,
spatialMaps,
};
}
return state.records.dataStructures;
};
const setElementForId = (id: string, element: D3Element) => {
state.records.elements[id] = element;
};
const getElementById = (id: string) => state.records.elements[id];
export const db: ArchitectureDB = {
clear,
setDiagramTitle,
getDiagramTitle,
setAccTitle,
getAccTitle,
setAccDescription,
getAccDescription,
addService,
getServices,
addJunction,
getJunctions,
getNodes,
getNode,
addGroup,
getGroups,
addEdge,
getEdges,
setElementForId,
getElementById,
getDataStructures,
};
/**
* Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined
* @param field - the config field to access
* @returns
*/
export function getConfigField<T extends keyof ArchitectureDiagramConfig>(
field: T
): Required<ArchitectureDiagramConfig>[T] {
const arch = getConfig().architecture;
if (arch?.[field]) {
return arch[field] as Required<ArchitectureDiagramConfig>[T];
}
return DEFAULT_ARCHITECTURE_CONFIG[field];
}

View File

@@ -1,24 +0,0 @@
import type {
DiagramDetector,
DiagramLoader,
ExternalDiagramDefinition,
} from '../../diagram-api/types.js';
const id = 'architecture';
const detector: DiagramDetector = (txt) => {
return /^\s*architecture/.test(txt);
};
const loader: DiagramLoader = async () => {
const { diagram } = await import('./architectureDiagram.js');
return { id, diagram };
};
const architecture: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default architecture;

View File

@@ -1,12 +0,0 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import { parser } from './architectureParser.js';
import { db } from './architectureDb.js';
import styles from './architectureStyles.js';
import { renderer } from './architectureRenderer.js';
export const diagram: DiagramDefinition = {
parser,
db,
renderer,
styles,
};

View File

@@ -1,43 +0,0 @@
import { unknownIcon } from '../../rendering-util/icons.js';
import type { IconifyJSON } from '@iconify/types';
const wrapIcon = (icon: string) => {
return `<g><rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>${icon}</g>`;
};
export const architectureIcons: IconifyJSON = {
prefix: 'mermaid-architecture',
height: 80,
width: 80,
icons: {
database: {
body: wrapIcon(
'<path id="b" data-name="4" d="m20,57.86c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path id="c" data-name="3" d="m20,45.95c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path id="d" data-name="2" d="m20,34.05c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse id="e" data-name="1" cx="40" cy="22.14" rx="20" ry="7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="20" y1="57.86" x2="20" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="60" y1="57.86" x2="60" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>'
),
},
server: {
body: wrapIcon(
'<rect x="17.5" y="17.5" width="45" height="45" rx="2" ry="2" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="17.5" y1="32.5" x2="62.5" y2="32.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="17.5" y1="47.5" x2="62.5" y2="47.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><g><path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/><path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/></g><g><path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/><path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/></g><g><path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/><path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/></g><g><circle cx="32.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="27.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="22.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/></g><g><circle cx="32.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="27.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="22.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/></g><g><circle cx="32.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="27.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/><circle cx="22.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/></g>'
),
},
disk: {
body: wrapIcon(
'<rect x="20" y="15" width="40" height="50" rx="1" ry="1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="24" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="56" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="24" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="56" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="40" cy="33.75" rx="14" ry="14.58" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><ellipse cx="40" cy="33.75" rx="4" ry="4.17" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path d="m37.51,42.52l-4.83,13.22c-.26.71-1.1,1.02-1.76.64l-4.18-2.42c-.66-.38-.81-1.26-.33-1.84l9.01-10.8c.88-1.05,2.56-.08,2.09,1.2Z" style="fill: #fff; stroke-width: 0px;"/>'
),
},
internet: {
body: wrapIcon(
'<circle cx="40" cy="40" r="22.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="40" y1="17.5" x2="40" y2="62.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="17.5" y1="40" x2="62.5" y2="40" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path d="m39.99,17.51c-15.28,11.1-15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><path d="m40.01,17.51c15.28,11.1,15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="19.75" y1="30.1" x2="60.25" y2="30.1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/><line x1="19.75" y1="49.9" x2="60.25" y2="49.9" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>'
),
},
cloud: {
body: wrapIcon(
'<path d="m65,47.5c0,2.76-2.24,5-5,5H20c-2.76,0-5-2.24-5-5,0-1.87,1.03-3.51,2.56-4.36-.04-.21-.06-.42-.06-.64,0-2.6,2.48-4.74,5.65-4.97,1.65-4.51,6.34-7.76,11.85-7.76.86,0,1.69.08,2.5.23,2.09-1.57,4.69-2.5,7.5-2.5,6.1,0,11.19,4.38,12.28,10.17,2.14.56,3.72,2.51,3.72,4.83,0,.03,0,.07-.01.1,2.29.46,4.01,2.48,4.01,4.9Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>'
),
},
unknown: unknownIcon,
blank: {
body: wrapIcon(''),
},
},
};

View File

@@ -1,24 +0,0 @@
import type { Architecture } from '@mermaid-js/parser';
import { parse } from '@mermaid-js/parser';
import { log } from '../../logger.js';
import type { ParserDefinition } from '../../diagram-api/types.js';
import { populateCommonDb } from '../common/populateCommonDb.js';
import type { ArchitectureDB } from './architectureTypes.js';
import { db } from './architectureDb.js';
const populateDb = (ast: Architecture, db: ArchitectureDB) => {
populateCommonDb(ast, db);
ast.groups.map(db.addGroup);
ast.services.map((service) => db.addService({ ...service, type: 'service' }));
ast.junctions.map((service) => db.addJunction({ ...service, type: 'junction' }));
// @ts-ignore TODO our parser guarantees the type is L/R/T/B and not string. How to change to union type?
ast.edges.map(db.addEdge);
};
export const parser: ParserDefinition = {
parse: async (input: string): Promise<void> => {
const ast: Architecture = await parse('architecture', input);
log.debug(ast);
populateDb(ast, db);
},
};

View File

@@ -1,466 +0,0 @@
import { registerIconPacks } from '../../rendering-util/icons.js';
import type { Position } from 'cytoscape';
import cytoscape from 'cytoscape';
import type { FcoseLayoutOptions } from 'cytoscape-fcose';
import fcose from 'cytoscape-fcose';
import { select } from 'd3';
import type { DrawDefinition, SVG } from '../../diagram-api/types.js';
import type { Diagram } from '../../Diagram.js';
import { log } from '../../logger.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import { getConfigField } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js';
import type {
ArchitectureDataStructures,
ArchitectureJunction,
ArchitectureSpatialMap,
EdgeSingular,
EdgeSingularData,
NodeSingularData,
} from './architectureTypes.js';
import {
type ArchitectureDB,
type ArchitectureDirection,
type ArchitectureEdge,
type ArchitectureGroup,
type ArchitectureService,
ArchitectureDirectionName,
edgeData,
getOppositeArchitectureDirection,
isArchitectureDirectionXY,
isArchitectureDirectionY,
nodeData,
} from './architectureTypes.js';
import { drawEdges, drawGroups, drawJunctions, drawServices } from './svgDraw.js';
registerIconPacks([
{
name: architectureIcons.prefix,
icons: architectureIcons,
},
]);
cytoscape.use(fcose);
function addServices(services: ArchitectureService[], cy: cytoscape.Core) {
services.forEach((service) => {
cy.add({
group: 'nodes',
data: {
type: 'service',
id: service.id,
icon: service.icon,
label: service.title,
parent: service.in,
width: getConfigField('iconSize'),
height: getConfigField('iconSize'),
} as NodeSingularData,
classes: 'node-service',
});
});
}
function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core) {
junctions.forEach((junction) => {
cy.add({
group: 'nodes',
data: {
type: 'junction',
id: junction.id,
parent: junction.in,
width: getConfigField('iconSize'),
height: getConfigField('iconSize'),
} as NodeSingularData,
classes: 'node-junction',
});
});
}
function positionNodes(db: ArchitectureDB, cy: cytoscape.Core) {
cy.nodes().map((node) => {
const data = nodeData(node);
if (data.type === 'group') {
return;
}
data.x = node.position().x;
data.y = node.position().y;
const nodeElem = db.getElementById(data.id);
nodeElem.attr('transform', 'translate(' + (data.x || 0) + ',' + (data.y || 0) + ')');
});
}
function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) {
groups.forEach((group) => {
cy.add({
group: 'nodes',
data: {
type: 'group',
id: group.id,
icon: group.icon,
label: group.title,
parent: group.in,
} as NodeSingularData,
classes: 'node-group',
});
});
}
function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
edges.forEach((parsedEdge) => {
const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } =
parsedEdge;
const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir)
? 'segments'
: 'straight';
const edge: EdgeSingularData = {
id: `${lhsId}-${rhsId}`,
label: title,
source: lhsId,
sourceDir: lhsDir,
sourceArrow: lhsInto,
sourceGroup: lhsGroup,
sourceEndpoint:
lhsDir === 'L'
? '0 50%'
: lhsDir === 'R'
? '100% 50%'
: lhsDir === 'T'
? '50% 0'
: '50% 100%',
target: rhsId,
targetDir: rhsDir,
targetArrow: rhsInto,
targetGroup: rhsGroup,
targetEndpoint:
rhsDir === 'L'
? '0 50%'
: rhsDir === 'R'
? '100% 50%'
: rhsDir === 'T'
? '50% 0'
: '50% 100%',
};
cy.add({
group: 'edges',
data: edge,
classes: edgeType,
});
});
}
function getAlignments(spatialMaps: ArchitectureSpatialMap[]): fcose.FcoseAlignmentConstraint {
const alignments = spatialMaps.map((spatialMap) => {
const horizontalAlignments: Record<number, string[]> = {};
const verticalAlignments: Record<number, string[]> = {};
// Group service ids in an object with their x and y coordinate as the key
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
if (!horizontalAlignments[y]) {
horizontalAlignments[y] = [];
}
if (!verticalAlignments[x]) {
verticalAlignments[x] = [];
}
horizontalAlignments[y].push(id);
verticalAlignments[x].push(id);
});
// Merge the values of each object into a list if the inner list has at least 2 elements
return {
horiz: Object.values(horizontalAlignments).filter((arr) => arr.length > 1),
vert: Object.values(verticalAlignments).filter((arr) => arr.length > 1),
};
});
// Merge the alignment lists for each spatial map into one 2d array per axis
const [horizontal, vertical] = alignments.reduce(
([prevHoriz, prevVert], { horiz, vert }) => {
return [
[...prevHoriz, ...horiz],
[...prevVert, ...vert],
];
},
[[] as string[][], [] as string[][]]
);
return {
horizontal,
vertical,
};
}
function getRelativeConstraints(
spatialMaps: ArchitectureSpatialMap[]
): fcose.FcoseRelativePlacementConstraint[] {
const relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = [];
const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`;
const strToPos = (pos: string) => pos.split(',').map((p) => parseInt(p));
spatialMaps.forEach((spatialMap) => {
const invSpatialMap = Object.fromEntries(
Object.entries(spatialMap).map(([id, pos]) => [posToStr(pos), id])
);
// perform BFS
const queue = [posToStr([0, 0])];
const visited: Record<string, number> = {};
const directions: Record<ArchitectureDirection, number[]> = {
L: [-1, 0],
R: [1, 0],
T: [0, 1],
B: [0, -1],
};
while (queue.length > 0) {
const curr = queue.shift();
if (curr) {
visited[curr] = 1;
const currId = invSpatialMap[curr];
if (currId) {
const currPos = strToPos(curr);
Object.entries(directions).forEach(([dir, shift]) => {
const newPos = posToStr([currPos[0] + shift[0], currPos[1] + shift[1]]);
const newId = invSpatialMap[newPos];
// If there is an adjacent service to the current one and it has not yet been visited
if (newId && !visited[newPos]) {
queue.push(newPos);
// @ts-ignore cannot determine if left/right or top/bottom are paired together
relativeConstraints.push({
[ArchitectureDirectionName[dir as ArchitectureDirection]]: newId,
[ArchitectureDirectionName[
getOppositeArchitectureDirection(dir as ArchitectureDirection)
]]: currId,
gap: 1.5 * getConfigField('iconSize'),
});
}
});
}
}
}
});
return relativeConstraints;
}
function layoutArchitecture(
services: ArchitectureService[],
junctions: ArchitectureJunction[],
groups: ArchitectureGroup[],
edges: ArchitectureEdge[],
{ spatialMaps }: ArchitectureDataStructures
): Promise<cytoscape.Core> {
return new Promise((resolve) => {
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
const cy = cytoscape({
container: document.getElementById('cy'),
style: [
{
selector: 'edge',
style: {
'curve-style': 'straight',
label: 'data(label)',
'source-endpoint': 'data(sourceEndpoint)',
'target-endpoint': 'data(targetEndpoint)',
},
},
{
selector: 'edge.segments',
style: {
'curve-style': 'segments',
'segment-weights': '0',
'segment-distances': [0.5],
// @ts-ignore Incorrect library types
'edge-distances': 'endpoints',
'source-endpoint': 'data(sourceEndpoint)',
'target-endpoint': 'data(targetEndpoint)',
},
},
{
selector: 'node',
style: {
// @ts-ignore Incorrect library types
'compound-sizing-wrt-labels': 'include',
},
},
{
selector: 'node[label]',
style: {
'text-valign': 'bottom',
'text-halign': 'center',
'font-size': `${getConfigField('fontSize')}px`,
},
},
{
selector: '.node-service',
style: {
label: 'data(label)',
width: 'data(width)',
height: 'data(height)',
},
},
{
selector: '.node-junction',
style: {
width: 'data(width)',
height: 'data(height)',
},
},
{
selector: '.node-group',
style: {
// @ts-ignore Incorrect library types
padding: `${getConfigField('padding')}px`,
},
},
],
});
// Remove element after layout
renderEl.remove();
addGroups(groups, cy);
addServices(services, cy);
addJunctions(junctions, cy);
addEdges(edges, cy);
// Use the spatial map to create alignment arrays for fcose
const alignmentConstraint = getAlignments(spatialMaps);
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
const layout = cy.layout({
name: 'fcose',
quality: 'proof',
styleEnabled: false,
animate: false,
nodeDimensionsIncludeLabels: false,
// Adjust the edge parameters if it passes through the border of a group
// Hacky fix for: https://github.com/iVis-at-Bilkent/cytoscape.js-fcose/issues/67
idealEdgeLength(edge: EdgeSingular) {
const [nodeA, nodeB] = edge.connectedNodes();
const { parent: parentA } = nodeData(nodeA);
const { parent: parentB } = nodeData(nodeB);
const elasticity =
parentA === parentB ? 1.5 * getConfigField('iconSize') : 0.5 * getConfigField('iconSize');
return elasticity;
},
edgeElasticity(edge: EdgeSingular) {
const [nodeA, nodeB] = edge.connectedNodes();
const { parent: parentA } = nodeData(nodeA);
const { parent: parentB } = nodeData(nodeB);
const elasticity = parentA === parentB ? 0.45 : 0.001;
return elasticity;
},
alignmentConstraint,
relativePlacementConstraint,
} as FcoseLayoutOptions);
// Once the diagram has been generated and the service's position cords are set, adjust the XY edges to have a 90deg bend
layout.one('layoutstop', () => {
function getSegmentWeights(
source: Position,
target: Position,
pointX: number,
pointY: number
) {
let W, D;
const { x: sX, y: sY } = source;
const { x: tX, y: tY } = target;
D =
(pointY - sY + ((sX - pointX) * (sY - tY)) / (sX - tX)) /
Math.sqrt(1 + Math.pow((sY - tY) / (sX - tX), 2));
W = Math.sqrt(Math.pow(pointY - sY, 2) + Math.pow(pointX - sX, 2) - Math.pow(D, 2));
const distAB = Math.sqrt(Math.pow(tX - sX, 2) + Math.pow(tY - sY, 2));
W = W / distAB;
//check whether the point (pointX, pointY) is on right or left of the line src to tgt. for instance : a point C(X, Y) and line (AB). d=(xB-xA)(yC-yA)-(yB-yA)(xC-xA). if d>0, then C is on left of the line. if d<0, it is on right. if d=0, it is on the line.
let delta1 = (tX - sX) * (pointY - sY) - (tY - sY) * (pointX - sX);
switch (true) {
case delta1 >= 0:
delta1 = 1;
break;
case delta1 < 0:
delta1 = -1;
break;
}
//check whether the point (pointX, pointY) is "behind" the line src to tgt
let delta2 = (tX - sX) * (pointX - sX) + (tY - sY) * (pointY - sY);
switch (true) {
case delta2 >= 0:
delta2 = 1;
break;
case delta2 < 0:
delta2 = -1;
break;
}
D = Math.abs(D) * delta1; //ensure that sign of D is same as sign of delta1. Hence we need to take absolute value of D and multiply by delta1
W = W * delta2;
return {
distances: D,
weights: W,
};
}
cy.startBatch();
for (const edge of Object.values(cy.edges())) {
if (edge.data?.()) {
const { x: sX, y: sY } = edge.source().position();
const { x: tX, y: tY } = edge.target().position();
if (sX !== tX && sY !== tY) {
const sEP = edge.sourceEndpoint();
const tEP = edge.targetEndpoint();
const { sourceDir } = edgeData(edge);
const [pointX, pointY] = isArchitectureDirectionY(sourceDir)
? [sEP.x, tEP.y]
: [tEP.x, sEP.y];
const { weights, distances } = getSegmentWeights(sEP, tEP, pointX, pointY);
edge.style('segment-distances', distances);
edge.style('segment-weights', weights);
}
}
}
cy.endBatch();
layout.run();
});
layout.run();
cy.ready((e) => {
log.info('Ready', e);
resolve(cy);
});
});
}
export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) => {
const db = diagObj.db as ArchitectureDB;
const services = db.getServices();
const junctions = db.getJunctions();
const groups = db.getGroups();
const edges = db.getEdges();
const ds = db.getDataStructures();
const svg: SVG = selectSvgElement(id);
const edgesElem = svg.append('g');
edgesElem.attr('class', 'architecture-edges');
const servicesElem = svg.append('g');
servicesElem.attr('class', 'architecture-services');
const groupElem = svg.append('g');
groupElem.attr('class', 'architecture-groups');
await drawServices(db, servicesElem, services);
drawJunctions(db, servicesElem, junctions);
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
await drawEdges(edgesElem, cy);
await drawGroups(groupElem, cy);
positionNodes(db, cy);
setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
};
export const renderer = { draw };

View File

@@ -1,38 +0,0 @@
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
import type { ArchitectureStyleOptions } from './architectureTypes.js';
const getStyles: DiagramStylesProvider = (options: ArchitectureStyleOptions) =>
`
.edge {
stroke-width: ${options.archEdgeWidth};
stroke: ${options.archEdgeColor};
fill: none;
}
.arrow {
fill: ${options.archEdgeArrowColor};
}
.node-bkg {
fill: none;
stroke: ${options.archGroupBorderColor};
stroke-width: ${options.archGroupBorderWidth};
stroke-dasharray: 8;
}
.node-icon-text {
display: flex;
align-items: center;
}
.node-icon-text > div {
color: #fff;
margin: 1px;
height: fit-content;
text-align: center;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
}
`;
export default getStyles;

View File

@@ -1,351 +0,0 @@
import type { DiagramDB } from '../../diagram-api/types.js';
import type { ArchitectureDiagramConfig } from '../../config.type.js';
import type { D3Element } from '../../types.js';
import type cytoscape from 'cytoscape';
/*=======================================*\
| Architecture Diagram Types |
\*=======================================*/
export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B';
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>;
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>;
/**
* Contains LL, RR, TT, BB which are impossible connections
*/
export type InvalidArchitectureDirectionPair = `${ArchitectureDirection}${ArchitectureDirection}`;
export type ArchitectureDirectionPair = Exclude<
InvalidArchitectureDirectionPair,
'LL' | 'RR' | 'TT' | 'BB'
>;
export type ArchitectureDirectionPairXY = Exclude<
InvalidArchitectureDirectionPair,
'LL' | 'RR' | 'TT' | 'BB' | 'LR' | 'RL' | 'TB' | 'BT'
>;
export const ArchitectureDirectionName = {
L: 'left',
R: 'right',
T: 'top',
B: 'bottom',
} as const;
export const ArchitectureDirectionArrow = {
L: (scale: number) => `${scale},${scale / 2} 0,${scale} 0,0`,
R: (scale: number) => `0,${scale / 2} ${scale},0 ${scale},${scale}`,
T: (scale: number) => `0,0 ${scale},0 ${scale / 2},${scale}`,
B: (scale: number) => `${scale / 2},0 ${scale},${scale} 0,${scale}`,
} as const;
export const ArchitectureDirectionArrowShift = {
L: (orig: number, arrowSize: number) => orig - arrowSize + 2,
R: (orig: number, _arrowSize: number) => orig - 2,
T: (orig: number, arrowSize: number) => orig - arrowSize + 2,
B: (orig: number, _arrowSize: number) => orig - 2,
} as const;
export const getOppositeArchitectureDirection = function (
x: ArchitectureDirection
): ArchitectureDirection {
if (isArchitectureDirectionX(x)) {
return x === 'L' ? 'R' : 'L';
} else {
return x === 'T' ? 'B' : 'T';
}
};
export const isArchitectureDirection = function (x: unknown): x is ArchitectureDirection {
const temp = x as ArchitectureDirection;
return temp === 'L' || temp === 'R' || temp === 'T' || temp === 'B';
};
export const isArchitectureDirectionX = function (
x: ArchitectureDirection
): x is ArchitectureDirectionX {
const temp = x as ArchitectureDirectionX;
return temp === 'L' || temp === 'R';
};
export const isArchitectureDirectionY = function (
x: ArchitectureDirection
): x is ArchitectureDirectionY {
const temp = x as ArchitectureDirectionY;
return temp === 'T' || temp === 'B';
};
export const isArchitectureDirectionXY = function (
a: ArchitectureDirection,
b: ArchitectureDirection
) {
const aX_bY = isArchitectureDirectionX(a) && isArchitectureDirectionY(b);
const aY_bX = isArchitectureDirectionY(a) && isArchitectureDirectionX(b);
return aX_bY || aY_bX;
};
export const isArchitecturePairXY = function (
pair: ArchitectureDirectionPair
): pair is ArchitectureDirectionPairXY {
const lhs = pair[0] as ArchitectureDirection;
const rhs = pair[1] as ArchitectureDirection;
const aX_bY = isArchitectureDirectionX(lhs) && isArchitectureDirectionY(rhs);
const aY_bX = isArchitectureDirectionY(lhs) && isArchitectureDirectionX(rhs);
return aX_bY || aY_bX;
};
/**
* Verifies that the architecture direction pair does not contain an invalid match (LL, RR, TT, BB)
* @param x - architecture direction pair which could potentially be invalid
* @returns true if the pair is not LL, RR, TT, or BB
*/
export const isValidArchitectureDirectionPair = function (
x: InvalidArchitectureDirectionPair
): x is ArchitectureDirectionPair {
return x !== 'LL' && x !== 'RR' && x !== 'TT' && x !== 'BB';
};
export type ArchitectureDirectionPairMap = {
[key in ArchitectureDirectionPair]?: string;
};
/**
* Creates a pair of the directions of each side of an edge. This function should be used instead of manually creating it to ensure that the source is always the first character.
*
* Note: Undefined is returned when sourceDir and targetDir are the same. In theory this should never happen since the diagram parser throws an error if a user defines it as such.
* @param sourceDir - source direction
* @param targetDir - target direction
* @returns
*/
export const getArchitectureDirectionPair = function (
sourceDir: ArchitectureDirection,
targetDir: ArchitectureDirection
): ArchitectureDirectionPair | undefined {
const pair: `${ArchitectureDirection}${ArchitectureDirection}` = `${sourceDir}${targetDir}`;
return isValidArchitectureDirectionPair(pair) ? pair : undefined;
};
/**
* Given an x,y position for an arrow and the direction of the edge it belongs to, return a factor for slightly shifting the edge
* @param param0 - [x, y] coordinate pair
* @param pair - architecture direction pair
* @returns a new [x, y] coordinate pair
*/
export const shiftPositionByArchitectureDirectionPair = function (
[x, y]: number[],
pair: ArchitectureDirectionPair
): number[] {
const lhs = pair[0] as ArchitectureDirection;
const rhs = pair[1] as ArchitectureDirection;
if (isArchitectureDirectionX(lhs)) {
if (isArchitectureDirectionY(rhs)) {
return [x + (lhs === 'L' ? -1 : 1), y + (rhs === 'T' ? 1 : -1)];
} else {
return [x + (lhs === 'L' ? -1 : 1), y];
}
} else {
if (isArchitectureDirectionX(rhs)) {
return [x + (rhs === 'L' ? 1 : -1), y + (lhs === 'T' ? 1 : -1)];
} else {
return [x, y + (lhs === 'T' ? 1 : -1)];
}
}
};
/**
* Given the directional pair of an XY edge, get the scale factors necessary to shift the coordinates inwards towards the edge
* @param pair - XY pair of an edge
* @returns - number[] containing [+/- 1, +/- 1]
*/
export const getArchitectureDirectionXYFactors = function (
pair: ArchitectureDirectionPairXY
): number[] {
if (pair === 'LT' || pair === 'TL') {
return [1, 1];
} else if (pair === 'BL' || pair === 'LB') {
return [1, -1];
} else if (pair === 'BR' || pair === 'RB') {
return [-1, -1];
} else {
return [-1, 1];
}
};
export interface ArchitectureStyleOptions {
archEdgeColor: string;
archEdgeArrowColor: string;
archEdgeWidth: string;
archGroupBorderColor: string;
archGroupBorderWidth: string;
}
export interface ArchitectureService {
id: string;
type: 'service';
edges: ArchitectureEdge[];
icon?: string;
iconText?: string;
title?: string;
in?: string;
width?: number;
height?: number;
}
export interface ArchitectureJunction {
id: string;
type: 'junction';
edges: ArchitectureEdge[];
in?: string;
width?: number;
height?: number;
}
export type ArchitectureNode = ArchitectureService | ArchitectureJunction;
export const isArchitectureService = function (x: ArchitectureNode): x is ArchitectureService {
const temp = x as ArchitectureService;
return temp.type === 'service';
};
export const isArchitectureJunction = function (x: ArchitectureNode): x is ArchitectureJunction {
const temp = x as ArchitectureJunction;
return temp.type === 'junction';
};
export interface ArchitectureGroup {
id: string;
icon?: string;
title?: string;
in?: string;
}
export interface ArchitectureEdge<DT = ArchitectureDirection> {
lhsId: string;
lhsDir: DT;
lhsInto?: boolean;
lhsGroup?: boolean;
rhsId: string;
rhsDir: DT;
rhsInto?: boolean;
rhsGroup?: boolean;
title?: string;
}
export interface ArchitectureDB extends DiagramDB {
clear: () => void;
addService: (service: Omit<ArchitectureService, 'edges'>) => void;
getServices: () => ArchitectureService[];
addJunction: (service: Omit<ArchitectureJunction, 'edges'>) => void;
getJunctions: () => ArchitectureJunction[];
getNodes: () => ArchitectureNode[];
getNode: (id: string) => ArchitectureNode | null;
addGroup: (group: ArchitectureGroup) => void;
getGroups: () => ArchitectureGroup[];
addEdge: (edge: ArchitectureEdge) => void;
getEdges: () => ArchitectureEdge[];
setElementForId: (id: string, element: D3Element) => void;
getElementById: (id: string) => D3Element;
getDataStructures: () => ArchitectureDataStructures;
}
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
export type ArchitectureSpatialMap = Record<string, number[]>;
export interface ArchitectureDataStructures {
adjList: ArchitectureAdjacencyList;
spatialMaps: ArchitectureSpatialMap[];
}
export interface ArchitectureState extends Record<string, unknown> {
nodes: Record<string, ArchitectureNode>;
groups: Record<string, ArchitectureGroup>;
edges: ArchitectureEdge[];
registeredIds: Record<string, 'node' | 'group'>;
dataStructures?: ArchitectureDataStructures;
elements: Record<string, D3Element>;
config: ArchitectureDiagramConfig;
}
/*=======================================*\
| Cytoscape Override Types |
\*=======================================*/
export interface EdgeSingularData {
id: string;
label?: string;
source: string;
sourceDir: ArchitectureDirection;
sourceArrow?: boolean;
sourceGroup?: boolean;
target: string;
targetDir: ArchitectureDirection;
targetArrow?: boolean;
targetGroup?: boolean;
[key: string]: any;
}
export const edgeData = (edge: cytoscape.EdgeSingular) => {
return edge.data() as EdgeSingularData;
};
export interface EdgeSingular extends cytoscape.EdgeSingular {
_private: {
bodyBounds: unknown;
rscratch: {
startX: number;
startY: number;
midX: number;
midY: number;
endX: number;
endY: number;
};
};
data(): EdgeSingularData;
data<T extends keyof EdgeSingularData>(key: T): EdgeSingularData[T];
}
export type NodeSingularData =
| {
type: 'service';
id: string;
icon?: string;
label?: string;
parent?: string;
width: number;
height: number;
[key: string]: any;
}
| {
type: 'junction';
id: string;
parent?: string;
width: number;
height: number;
[key: string]: any;
}
| {
type: 'group';
id: string;
icon?: string;
label?: string;
parent?: string;
[key: string]: any;
};
export const nodeData = (node: cytoscape.NodeSingular) => {
return node.data() as NodeSingularData;
};
export interface NodeSingular extends cytoscape.NodeSingular {
_private: {
bodyBounds: {
h: number;
w: number;
x1: number;
x2: number;
y1: number;
y2: number;
};
children: cytoscape.NodeSingular[];
};
data(): NodeSingularData;
data<T extends keyof NodeSingularData>(key: T): NodeSingularData[T];
}

View File

@@ -1,370 +0,0 @@
import { getIconSVG } from '../../rendering-util/icons.js';
import type cytoscape from 'cytoscape';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import { createText } from '../../rendering-util/createText.js';
import type { D3Element } from '../../types.js';
import { db, getConfigField } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js';
import {
ArchitectureDirectionArrow,
ArchitectureDirectionArrowShift,
edgeData,
getArchitectureDirectionPair,
getArchitectureDirectionXYFactors,
isArchitectureDirectionX,
isArchitectureDirectionXY,
isArchitectureDirectionY,
isArchitecturePairXY,
nodeData,
type ArchitectureDB,
type ArchitectureJunction,
type ArchitectureService,
} from './architectureTypes.js';
export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) {
const padding = getConfigField('padding');
const iconSize = getConfigField('iconSize');
const halfIconSize = iconSize / 2;
const arrowSize = iconSize / 6;
const halfArrowSize = arrowSize / 2;
await Promise.all(
cy.edges().map(async (edge) => {
const {
source,
sourceDir,
sourceArrow,
sourceGroup,
target,
targetDir,
targetArrow,
targetGroup,
label,
} = edgeData(edge);
let { x: startX, y: startY } = edge[0].sourceEndpoint();
const { x: midX, y: midY } = edge[0].midpoint();
let { x: endX, y: endY } = edge[0].targetEndpoint();
// Adjust the edge distance if it has the {group} modifier
const groupEdgeShift = padding + 4;
// +18 comes from the service label height that extends the padding on the bottom side of each group
if (sourceGroup) {
if (isArchitectureDirectionX(sourceDir)) {
startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift;
} else {
startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
}
}
if (targetGroup) {
if (isArchitectureDirectionX(targetDir)) {
endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift;
} else {
endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
}
}
// Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node
if (!sourceGroup && db.getNode(source)?.type === 'junction') {
if (isArchitectureDirectionX(sourceDir)) {
startX += sourceDir === 'L' ? halfIconSize : -halfIconSize;
} else {
startY += sourceDir === 'T' ? halfIconSize : -halfIconSize;
}
}
if (!targetGroup && db.getNode(target)?.type === 'junction') {
if (isArchitectureDirectionX(targetDir)) {
endX += targetDir === 'L' ? halfIconSize : -halfIconSize;
} else {
endY += targetDir === 'T' ? halfIconSize : -halfIconSize;
}
}
if (edge[0]._private.rscratch) {
// const bounds = edge[0]._private.rscratch;
const g = edgesEl.insert('g');
g.insert('path')
.attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `)
.attr('class', 'edge');
if (sourceArrow) {
const xShift = isArchitectureDirectionX(sourceDir)
? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize)
: startX - halfArrowSize;
const yShift = isArchitectureDirectionY(sourceDir)
? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize)
: startY - halfArrowSize;
g.insert('polygon')
.attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize))
.attr('transform', `translate(${xShift},${yShift})`)
.attr('class', 'arrow');
}
if (targetArrow) {
const xShift = isArchitectureDirectionX(targetDir)
? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize)
: endX - halfArrowSize;
const yShift = isArchitectureDirectionY(targetDir)
? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize)
: endY - halfArrowSize;
g.insert('polygon')
.attr('points', ArchitectureDirectionArrow[targetDir](arrowSize))
.attr('transform', `translate(${xShift},${yShift})`)
.attr('class', 'arrow');
}
if (label) {
const axis = !isArchitectureDirectionXY(sourceDir, targetDir)
? isArchitectureDirectionX(sourceDir)
? 'X'
: 'Y'
: 'XY';
let width = 0;
if (axis === 'X') {
width = Math.abs(startX - endX);
} else if (axis === 'Y') {
// Reduce width by a factor of 1.5 to avoid overlapping service labels
width = Math.abs(startY - endY) / 1.5;
} else {
width = Math.abs(startX - endX) / 2;
}
const textElem = g.append('g');
await createText(
textElem,
label,
{
useHtmlLabels: false,
width,
classes: 'architecture-service-label',
},
getConfig()
);
textElem
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle');
if (axis === 'X') {
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')');
} else if (axis === 'Y') {
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)');
} else if (axis === 'XY') {
const pair = getArchitectureDirectionPair(sourceDir, targetDir);
if (pair && isArchitecturePairXY(pair)) {
const bboxOrig = textElem.node().getBoundingClientRect();
const [x, y] = getArchitectureDirectionXYFactors(pair);
textElem
.attr('dominant-baseline', 'auto')
.attr('transform', `rotate(${-1 * x * y * 45})`);
// Calculate the new width/height with the rotation applied, and transform to the proper position
const bboxNew = textElem.node().getBoundingClientRect();
textElem.attr(
'transform',
`
translate(${midX}, ${midY - bboxOrig.height / 2})
translate(${(x * bboxNew.width) / 2}, ${(y * bboxNew.height) / 2})
rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2})
`
);
}
}
}
}
})
);
};
export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) {
const padding = getConfigField('padding');
const groupIconSize = padding * 0.75;
const fontSize = getConfigField('fontSize');
const iconSize = getConfigField('iconSize');
const halfIconSize = iconSize / 2;
await Promise.all(
cy.nodes().map(async (node) => {
const data = nodeData(node);
if (data.type === 'group') {
const { h, w, x1, y1 } = node.boundingBox();
groupsEl
.append('rect')
.attr('x', x1 + halfIconSize)
.attr('y', y1 + halfIconSize)
.attr('width', w)
.attr('height', h)
.attr('class', 'node-bkg');
const groupLabelContainer = groupsEl.append('g');
let shiftedX1 = x1;
let shiftedY1 = y1;
if (data.icon) {
const bkgElem = groupLabelContainer.append('g');
bkgElem.html(
`<g>${await getIconSVG(data.icon, { height: groupIconSize, width: groupIconSize, fallbackPrefix: architectureIcons.prefix })}</g>`
);
bkgElem.attr(
'transform',
'translate(' +
(shiftedX1 + halfIconSize + 1) +
', ' +
(shiftedY1 + halfIconSize + 1) +
')'
);
shiftedX1 += groupIconSize;
// TODO: test with more values
// - 1 - 2 comes from the Y axis transform of the icon and label
shiftedY1 += fontSize / 2 - 1 - 2;
}
if (data.label) {
const textElem = groupLabelContainer.append('g');
await createText(
textElem,
data.label,
{
useHtmlLabels: false,
width: w,
classes: 'architecture-service-label',
},
getConfig()
);
textElem
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'start')
.attr('text-anchor', 'start');
textElem.attr(
'transform',
'translate(' +
(shiftedX1 + halfIconSize + 4) +
', ' +
(shiftedY1 + halfIconSize + 2) +
')'
);
}
}
})
);
};
export const drawServices = async function (
db: ArchitectureDB,
elem: D3Element,
services: ArchitectureService[]
): Promise<number> {
for (const service of services) {
const serviceElem = elem.append('g');
const iconSize = getConfigField('iconSize');
if (service.title) {
const textElem = serviceElem.append('g');
await createText(
textElem,
service.title,
{
useHtmlLabels: false,
width: iconSize * 1.5,
classes: 'architecture-service-label',
},
getConfig()
);
textElem
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle');
textElem.attr('transform', 'translate(' + iconSize / 2 + ', ' + iconSize + ')');
}
const bkgElem = serviceElem.append('g');
if (service.icon) {
// TODO: should a warning be given to end-users saying which icon names are available?
// if (!isIconNameInUse(service.icon)) {
// throw new Error(`Invalid SVG Icon name: "${service.icon}"`);
// }
bkgElem.html(
`<g>${await getIconSVG(service.icon, { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}</g>`
);
} else if (service.iconText) {
bkgElem.html(
`<g>${await getIconSVG('blank', { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}</g>`
);
const textElemContainer = bkgElem.append('g');
const fo = textElemContainer
.append('foreignObject')
.attr('width', iconSize)
.attr('height', iconSize);
const divElem = fo
.append('div')
.attr('class', 'node-icon-text')
.attr('style', `height: ${iconSize}px;`)
.append('div')
.html(service.iconText);
const fontSize =
parseInt(
window
.getComputedStyle(divElem.node(), null)
.getPropertyValue('font-size')
.replace(/\D/g, '')
) ?? 16;
divElem.attr('style', `-webkit-line-clamp: ${Math.floor((iconSize - 2) / fontSize)};`);
} else {
bkgElem
.append('path')
.attr('class', 'node-bkg')
.attr('id', 'node-' + service.id)
.attr(
'd',
`M0 ${iconSize} v${-iconSize} q0,-5 5,-5 h${iconSize} q5,0 5,5 v${iconSize} H0 Z`
);
}
serviceElem.attr('class', 'architecture-service');
const { width, height } = serviceElem._groups[0][0].getBBox();
service.width = width;
service.height = height;
db.setElementForId(service.id, serviceElem);
}
return 0;
};
export const drawJunctions = function (
db: ArchitectureDB,
elem: D3Element,
junctions: ArchitectureJunction[]
) {
junctions.forEach((junction) => {
const junctionElem = elem.append('g');
const iconSize = getConfigField('iconSize');
const bkgElem = junctionElem.append('g');
bkgElem
.append('rect')
.attr('id', 'node-' + junction.id)
.attr('fill-opacity', '0')
.attr('width', iconSize)
.attr('height', iconSize);
junctionElem.attr('class', 'architecture-junction');
const { width, height } = junctionElem._groups[0][0].getBBox();
junctionElem.width = width;
junctionElem.height = height;
db.setElementForId(junction.id, junctionElem);
});
};

View File

@@ -124,8 +124,7 @@ async function calculateBlockSize(
}
// Add the element to the DOM to size it
const config = getConfig();
const nodeEl = await insertNode(elem, node, { config });
const nodeEl = await insertNode(elem, node);
const boundingBox = nodeEl.node().getBBox();
const obj = db.getBlock(node.id);
obj.size = { width: boundingBox.width, height: boundingBox.height, x: 0, y: 0, node: nodeEl };
@@ -139,8 +138,7 @@ export async function insertBlockPositioned(elem: any, block: Block, db: any) {
// Add the element to the DOM to size it
const obj = db.getBlock(node.id);
if (obj.type !== 'space') {
const config = getConfig();
await insertNode(elem, node, { config });
await insertNode(elem, node);
block.intersect = node?.intersect;
positionNode(node);
}

View File

@@ -389,6 +389,7 @@ const setupToolTips = function (element: Element) {
// @ts-ignore - getBoundingClientRect is not part of the d3 type definition
const rect = this.getBoundingClientRect();
// @ts-expect-error - Incorrect types
tooltipElem.transition().duration(200).style('opacity', '.9');
tooltipElem
.text(el.attr('title'))
@@ -398,6 +399,7 @@ const setupToolTips = function (element: Element) {
el.classed('hover', true);
})
.on('mouseout', function () {
// @ts-expect-error - Incorrect types
tooltipElem.transition().duration(500).style('opacity', 0);
const el = select(this);
el.classed('hover', false);

View File

@@ -14,78 +14,6 @@ describe('given a basic class diagram, ', function () {
classDb.clear();
parser.yy = classDb;
});
it('should handle classes within namespaces', () => {
const str = `classDiagram
namespace Company.Project {
class User {
+login(username: String, password: String)
+logout()
}
}
namespace Company.Project.Module {
class Admin {
+addUser(user: User)
+removeUser(user: User)
}
}`;
parser.parse(str);
const user = classDb.getClass('User');
const admin = classDb.getClass('Admin');
// Check if the classes are correctly registered under their respective namespaces
expect(user.parent).toBe('Company.Project');
expect(admin.parent).toBe('Company.Project.Module');
expect(user.methods.length).toBe(2);
expect(admin.methods.length).toBe(2);
});
it('should handle generic classes within namespaces', () => {
const str = `classDiagram
namespace Company.Project.Module {
class GenericClass~T~ {
+addItem(item: T)
+getItem() T
}
}`;
parser.parse(str);
const genericClass = classDb.getClass('GenericClass');
expect(genericClass.type).toBe('T');
expect(genericClass.methods[0].getDisplayDetails().displayText).toBe('+addItem(item: T)');
expect(genericClass.methods[1].getDisplayDetails().displayText).toBe('+getItem() : T');
});
it('should handle nested namespaces and relationships', () => {
const str = ` classDiagram
namespace Company.Project.Module.SubModule {
class Report {
+generatePDF(data: List)
+generateCSV(data: List)
}
}
namespace Company.Project.Module {
class Admin {
+generateReport()
}
}
Admin --> Report : generates`;
parser.parse(str);
const report = classDb.getClass('Report');
const admin = classDb.getClass('Admin');
const relations = classDb.getRelations();
expect(report.parent).toBe('Company.Project.Module.SubModule');
expect(admin.parent).toBe('Company.Project.Module');
expect(relations[0].id1).toBe('Admin');
expect(relations[0].id2).toBe('Report');
expect(relations[0].title).toBe('generates');
});
it('should handle accTitle and accDescr', function () {
const str = `classDiagram
accTitle: My Title
@@ -120,22 +48,6 @@ describe('given a basic class diagram, ', function () {
}
});
it('should handle fully qualified class names in relationships', () => {
const str = `classDiagram
namespace Company.Project.Module {
class User
class Admin
}
Admin --> User : manages`;
parser.parse(str);
const relations = classDb.getRelations();
expect(relations[0].id1).toBe('Admin');
expect(relations[0].id2).toBe('User');
expect(relations[0].title).toBe('manages');
});
it('should handle backquoted class names', function () {
const str = 'classDiagram\n' + 'class `Car`';
@@ -481,23 +393,27 @@ class C13["With Città foreign language"]
Student "1" --o "1" IdCard : carries
Student "1" --o "1" Bike : rides`);
const studentClass = classDb.getClasses().get('Student');
// Check that the important properties match, but ignore the domId
expect(studentClass).toMatchObject({
id: 'Student',
label: 'Student',
members: [
expect.objectContaining({
id: 'idCard : IdCard',
visibility: '-',
}),
],
methods: [],
annotations: [],
cssClasses: [],
});
expect(classDb.getClasses().size).toBe(3);
expect(classDb.getClasses().get('Student')).toMatchInlineSnapshot(`
{
"annotations": [],
"cssClasses": [],
"domId": "classId-Student-134",
"id": "Student",
"label": "Student",
"members": [
ClassMember {
"classifier": "",
"id": "idCard : IdCard",
"memberType": "attribute",
"visibility": "-",
},
],
"methods": [],
"styles": [],
"type": "",
}
`);
expect(classDb.getRelations().length).toBe(2);
expect(classDb.getRelations()).toMatchInlineSnapshot(`
[

View File

@@ -241,13 +241,11 @@ classLabel
namespaceName
: alphaNumToken { $$=$1; }
| alphaNumToken DOT namespaceName { $$=$1+'.'+$3; }
| alphaNumToken namespaceName { $$=$1+$2; }
;
className
: alphaNumToken { $$=$1; }
| alphaNumToken DOT className { $$=$1+'.'+$3; }
| classLiteralName { $$=$1; }
| alphaNumToken className { $$=$1+$2; }
| alphaNumToken GENERICTYPE { $$=$1+'~'+$2+'~'; }
@@ -272,12 +270,12 @@ statement
;
namespaceStatement
: namespaceIdentifier STRUCT_START classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); }
| namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); }
: namespaceIdentifier STRUCT_START classStatements STRUCT_STOP {yy.addClassesToNamespace($1, $3);}
| namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP {yy.addClassesToNamespace($1, $4);}
;
namespaceIdentifier
: NAMESPACE namespaceName { $$=$2; yy.addNamespace($2); }
: NAMESPACE namespaceName {$$=$2; yy.addNamespace($2);}
;
classStatements

View File

@@ -519,8 +519,6 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
// Append a text node containing the label
const labelId = 'rel' + relCnt;
const labelText = rel.roleA.split(/<br ?\/>/g);
const labelNode = svg
.append('text')
.classed('er relationshipLabel', true)
@@ -530,20 +528,8 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
.style('text-anchor', 'middle')
.style('dominant-baseline', 'middle')
.style('font-family', getConfig().fontFamily)
.style('font-size', conf.fontSize + 'px');
if (labelText.length == 1) {
labelNode.text(rel.roleA);
} else {
const firstShift = -(labelText.length - 1) * 0.5;
labelText.forEach((txt, i) => {
labelNode
.append('tspan')
.attr('x', labelPoint.x)
.attr('dy', `${i === 0 ? firstShift : 1}em`)
.text(txt);
});
}
.style('font-size', conf.fontSize + 'px')
.text(rel.roleA);
// Figure out how big the opaque 'container' rectangle needs to be
const labelBBox = labelNode.node().getBBox();

View File

@@ -4,7 +4,6 @@ import { getConfig, defaultConfig } from '../../diagram-api/diagramAPI.js';
import common from '../common/common.js';
import type { Node, Edge } from '../../rendering-util/types.js';
import { log } from '../../logger.js';
import * as yaml from 'js-yaml';
import {
setAccTitle,
getAccTitle,
@@ -15,7 +14,6 @@ import {
getDiagramTitle,
} from '../common/commonDb.js';
import type { FlowVertex, FlowClass, FlowSubGraph, FlowText, FlowEdge, FlowLink } from './types.js';
import type { NodeMetaData } from '../../types.js';
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
let vertexCounter = 0;
@@ -62,10 +60,8 @@ export const addVertex = function (
style: string[],
classes: string[],
dir: string,
props = {},
shapeData: any
props = {}
) {
// console.log('addVertex', id, shapeData);
if (!id || id.trim().length === 0) {
return;
}
@@ -119,59 +115,6 @@ export const addVertex = function (
} else if (props !== undefined) {
Object.assign(vertex.props, props);
}
if (shapeData !== undefined) {
let yamlData;
// detect if shapeData contains a newline character
// console.log('shapeData', shapeData);
if (!shapeData.includes('\n')) {
// console.log('yamlData shapeData has no new lines', shapeData);
yamlData = '{\n' + shapeData + '\n}';
} else {
// console.log('yamlData shapeData has new lines', shapeData);
yamlData = shapeData + '\n';
}
// console.log('yamlData', yamlData);
const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as NodeMetaData;
if (doc.shape && (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_'))) {
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
}
// console.log('yamlData doc', doc);
if (doc?.shape) {
vertex.type = doc?.shape;
}
if (doc?.label) {
vertex.text = doc?.label;
}
if (doc?.icon) {
vertex.icon = doc?.icon;
if (!doc.label?.trim() && vertex.text === id) {
vertex.text = '';
}
}
if (doc?.form) {
vertex.form = doc?.form;
}
if (doc?.pos) {
vertex.pos = doc?.pos;
}
if (doc?.img) {
vertex.img = doc?.img;
if (!doc.label?.trim() && vertex.text === id) {
vertex.text = '';
}
}
if (doc?.constraint) {
vertex.constraint = doc.constraint;
}
if (doc.w) {
vertex.assetWidth = Number(doc.w);
}
if (doc.h) {
vertex.assetHeight = Number(doc.h);
}
}
};
/**
@@ -482,6 +425,7 @@ const setupToolTips = function (element: Element) {
}
const rect = (this as Element)?.getBoundingClientRect();
// @ts-ignore TODO: fix this
tooltipElem.transition().duration(200).style('opacity', '.9');
tooltipElem
.text(el.attr('title'))
@@ -491,6 +435,7 @@ const setupToolTips = function (element: Element) {
el.classed('hover', true);
})
.on('mouseout', function () {
// @ts-ignore TODO: fix this
tooltipElem.transition().duration(500).style('opacity', 0);
const el = select(this);
el.classed('hover', false);
@@ -817,21 +762,6 @@ export const lex = {
};
const getTypeFromVertex = (vertex: FlowVertex) => {
if (vertex.img) {
return 'imageSquare';
}
if (vertex.icon) {
if (vertex.form === 'circle') {
return 'iconCircle';
}
if (vertex.form === 'square') {
return 'iconSquare';
}
if (vertex.form === 'rounded') {
return 'iconRounded';
}
return 'icon';
}
if (vertex.type === 'square') {
return 'squareRect';
}
@@ -897,12 +827,6 @@ const addNodeFromVertex = (
link: vertex.link,
linkTarget: vertex.linkTarget,
tooltip: getTooltip(vertex.id),
icon: vertex.icon,
pos: vertex.pos,
img: vertex.img,
assetWidth: vertex.assetWidth,
assetHeight: vertex.assetHeight,
constraint: vertex.constraint,
});
}
};

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