mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-17 07:19:24 +02:00
Compare commits
123 Commits
lazy-load-
...
sidv/mindm
Author | SHA1 | Date | |
---|---|---|---|
![]() |
87cdc03e84 | ||
![]() |
775fb80c43 | ||
![]() |
aec1d80966 | ||
![]() |
58a53c0fa8 | ||
![]() |
b486177ca7 | ||
![]() |
957e64fe8a | ||
![]() |
7881d1e457 | ||
![]() |
16be835c9b | ||
![]() |
59cf085af5 | ||
![]() |
fc8db95597 | ||
![]() |
cc10e62ebd | ||
![]() |
bed95c77a9 | ||
![]() |
d348f2847c | ||
![]() |
4d46ea9801 | ||
![]() |
eec97d10af | ||
![]() |
ebef4a1416 | ||
![]() |
24605784f2 | ||
![]() |
3240f8ae56 | ||
![]() |
50f44c5cb0 | ||
![]() |
cc2f4f779a | ||
![]() |
00ce0fc034 | ||
![]() |
551b37f969 | ||
![]() |
bc5ef5fb7d | ||
![]() |
c20b8a0664 | ||
![]() |
9660b0e9fb | ||
![]() |
23e590e09b | ||
![]() |
60e4585e20 | ||
![]() |
ba436cc37a | ||
![]() |
960ea450e9 | ||
![]() |
8ffa7e6334 | ||
![]() |
2fb1876023 | ||
![]() |
f5692e742b | ||
![]() |
d7e7498fea | ||
![]() |
8ea1a1a077 | ||
![]() |
7cd281eea8 | ||
![]() |
1570eb7b73 | ||
![]() |
5060c9f390 | ||
![]() |
1fea43e125 | ||
![]() |
ef47cc5b6f | ||
![]() |
e876c35ef9 | ||
![]() |
800cb65706 | ||
![]() |
25ff005dd7 | ||
![]() |
72c266b242 | ||
![]() |
828f69f011 | ||
![]() |
ad4b079d63 | ||
![]() |
5d4d7c5fbf | ||
![]() |
238b15df9d | ||
![]() |
6e2deb1fa7 | ||
![]() |
1255c0064b | ||
![]() |
d2300d375b | ||
![]() |
235797a97c | ||
![]() |
508fbccdb4 | ||
![]() |
ad0cb7ff3c | ||
![]() |
bc258793ac | ||
![]() |
10d2e0a62f | ||
![]() |
738abc8946 | ||
![]() |
de5ad8644e | ||
![]() |
97b39bca95 | ||
![]() |
3698b30809 | ||
![]() |
069437842b | ||
![]() |
ee13c7666d | ||
![]() |
a23a7edd26 | ||
![]() |
e21da2ec7f | ||
![]() |
2b40bc0a48 | ||
![]() |
7e1006db26 | ||
![]() |
44f463c4f4 | ||
![]() |
6029bdeee9 | ||
![]() |
0261e7464a | ||
![]() |
a22158b2e2 | ||
![]() |
8a3bd5bcb8 | ||
![]() |
514d12d48c | ||
![]() |
01fac85cde | ||
![]() |
18283bc48f | ||
![]() |
f31db315b8 | ||
![]() |
8a2aea219e | ||
![]() |
034fe80411 | ||
![]() |
6d9b695ada | ||
![]() |
9c1da3bca1 | ||
![]() |
0e61395aa9 | ||
![]() |
8215c7d98e | ||
![]() |
ee45ab2e6c | ||
![]() |
b1f3e21d28 | ||
![]() |
a4b0e6b87b | ||
![]() |
9fe7152d98 | ||
![]() |
0859f28a14 | ||
![]() |
56b9aab5ce | ||
![]() |
aacc40e525 | ||
![]() |
b0ed5e9be2 | ||
![]() |
3777ccb305 | ||
![]() |
6b736f0bb0 | ||
![]() |
2e72c0bf6e | ||
![]() |
a1757ba217 | ||
![]() |
5390c409d0 | ||
![]() |
4f91c9a8de | ||
![]() |
8cf4efc190 | ||
![]() |
405df09e43 | ||
![]() |
86adf96021 | ||
![]() |
a0c3de568b | ||
![]() |
c10eb5af79 | ||
![]() |
3c4acd2184 | ||
![]() |
ab1573053e | ||
![]() |
c404f6fe34 | ||
![]() |
d24256e1c2 | ||
![]() |
e494e2dc56 | ||
![]() |
ab2e727db9 | ||
![]() |
b9fcb66d28 | ||
![]() |
12e4819c49 | ||
![]() |
0b54366705 | ||
![]() |
c99fd2baa9 | ||
![]() |
868cb529aa | ||
![]() |
2d01548d02 | ||
![]() |
6fb92f6f3c | ||
![]() |
f4a5b80eff | ||
![]() |
74c7a8585d | ||
![]() |
8c1046169f | ||
![]() |
2389f4a285 | ||
![]() |
24fb2337d7 | ||
![]() |
cc55a82b64 | ||
![]() |
1f8fb56409 | ||
![]() |
5865c890b5 | ||
![]() |
00053b8e97 | ||
![]() |
1615c6d9f9 | ||
![]() |
43f3784c83 |
@@ -52,24 +52,25 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": "./**/*.html",
|
||||
"rules": {
|
||||
"no-undef": "off",
|
||||
"jsdoc/require-jsdoc": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["./cypress/**", "./demos/**"],
|
||||
"files": ["cypress/**", "demos/**"],
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["./**/*.spec.{ts,js}", "./cypress/**", "./demos/**", "./**/docs/**"],
|
||||
"files": ["*.spec.{ts,js}", "cypress/**", "demos/**", "**/docs/**"],
|
||||
"rules": {
|
||||
"jsdoc/require-jsdoc": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.html", "*.md", "**/*.md/*"],
|
||||
"rules": {
|
||||
"no-var": "error",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
28
.github/workflows/docs.yml
vendored
Normal file
28
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Documentation Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- 'packages/mermaid/src/docs/**/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- 'packages/mermaid/src/docs/**/*'
|
||||
jobs:
|
||||
spellcheck:
|
||||
name: 'Docs: Spellcheck'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Check out the code
|
||||
- uses: actions/setup-node@v1
|
||||
name: Setup node
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: npm install -g cspell
|
||||
name: Install cSpell
|
||||
- run: cspell --config ./cSpell.json "packages/mermaid/src/docs/**/*.md" --no-progress
|
||||
name: Run cSpell
|
2
.github/workflows/e2e-applitools.yml
vendored
2
.github/workflows/e2e-applitools.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- if: ${{ ! env.USE_APPLI }}
|
||||
name: Warn if not using Applitools
|
||||
|
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
containers: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
@@ -13,7 +13,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
- name: Install Yarn
|
||||
run: npm i yarn --global
|
||||
|
||||
|
2
.github/workflows/release-publish.yml
vendored
2
.github/workflows/release-publish.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
- name: Install Yarn
|
||||
run: npm i yarn --global
|
||||
|
||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -32,3 +32,6 @@ cypress/snapshots/
|
||||
.eslintcache
|
||||
.tsbuildinfo
|
||||
tsconfig.tsbuildinfo
|
||||
knsv*.html
|
||||
|
||||
local*.html
|
||||
|
@@ -6,6 +6,7 @@ import pkg from '../package.json' assert { type: 'json' };
|
||||
|
||||
const { dependencies } = pkg;
|
||||
const watch = process.argv.includes('--watch');
|
||||
const mermaidOnly = process.argv.includes('--mermaid');
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
type OutputOptions = Exclude<
|
||||
@@ -129,14 +130,19 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
||||
const main = async () => {
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
for (const pkg of packageNames) {
|
||||
if (mermaidOnly && pkg !== 'mermaid') {
|
||||
continue;
|
||||
}
|
||||
await buildPackage(pkg);
|
||||
}
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
build(getBuildConfig({ minify: false, watch, core: true, entryName: 'mermaid' }));
|
||||
if (!mermaidOnly) {
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
}
|
||||
} else {
|
||||
void main();
|
||||
}
|
||||
|
@@ -6,12 +6,23 @@ So you want to help? That's great!
|
||||
|
||||
Here are a few things to know to get you started on the right path.
|
||||
|
||||
Below link will help you making a copy of the repository in your local system.
|
||||
|
||||
https://docs.github.com/en/get-started/quickstart/fork-a-repo
|
||||
|
||||
## Requirements
|
||||
|
||||
- [volta](https://volta.sh/) to manage node versions.
|
||||
- [Node.js](https://nodejs.org/en/). `volta install node`
|
||||
- [pnpm](https://pnpm.io/) package manager. `volta install pnpm`
|
||||
|
||||
## Development Installation
|
||||
|
||||
```bash
|
||||
git clone git@github.com:mermaid-js/mermaid.git
|
||||
cd mermaid
|
||||
pnpm install
|
||||
# npx is required for first install as volta support for pnpm is not added yet.
|
||||
npx pnpm install
|
||||
pnpm test
|
||||
```
|
||||
|
||||
@@ -39,16 +50,16 @@ Less strict here, it is OK to commit directly in the `develop` branch if you are
|
||||
|
||||
The documentation is written in **Markdown**. For more information about Markdown [see the GitHub Markdown help page](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax).
|
||||
|
||||
### Documentation source files are in /src/docs
|
||||
### Documentation source files are in [`/packages/mermaid/src/docs`](packages/mermaid/src/docs)
|
||||
|
||||
The source files for the project documentation are located in the `/src/docs` directory. This is where you should make changes.
|
||||
The files under `/src/docs` are processed to generate the published documentation, and the resulting files are put into the `/docs` directory.
|
||||
The source files for the project documentation are located in the [`/packages/mermaid/src/docs`](packages/mermaid/src/docs) directory. This is where you should make changes.
|
||||
The files under `/packages/mermaid/src/docs` are processed to generate the published documentation, and the resulting files are put into the `/docs` directory.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
classDef default fill:#fff,color:black,stroke:black
|
||||
|
||||
source["files in /src/docs\n(changes should be done here)"] -- automatic processing\nto generate the final documentation--> published["files in /docs\ndisplayed on the official documentation site"]
|
||||
source["files in /packages/mermaid/src/docs\n(changes should be done here)"] -- automatic processing\nto generate the final documentation--> published["files in /docs\ndisplayed on the official documentation site"]
|
||||
|
||||
```
|
||||
|
||||
@@ -137,7 +148,7 @@ it('should render forks and joins', () => {
|
||||
|
||||
Finally, if it is not in the documentation, no one will know about it and then **no one will use it**. Wouldn't that be sad? With all the effort that was put into the feature?
|
||||
|
||||
The source files for documentation are in `/src/docs` and are written in markdown. Just pick the right section and start typing. See the [Committing Documentation](#committing-documentation) section for more about how the documentation is generated.
|
||||
The source files for documentation are in `/packages/mermaid/src/docs` and are written in markdown. Just pick the right section and start typing. See the [Committing Documentation](#committing-documentation) section for more about how the documentation is generated.
|
||||
|
||||
#### Adding to or changing the documentation organization
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# mermaid [](https://travis-ci.org/mermaid-js/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
# mermaid
|
||||
|
||||
# Whoa, whats going on here?
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
# Whoa, what's going on here?
|
||||
|
||||
We are transforming the Mermaid repository to a so called mono-repo. This is a part of the effort to decouple the diagrams from the core of mermaid. This will:
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
# mermaid [](https://travis-ci.org/mermaid-js/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
# mermaid
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
[English](./README.md) | 简体中文
|
||||
|
||||
|
@@ -1,3 +1 @@
|
||||
import { vi } from 'vitest';
|
||||
|
||||
// export const render = vi.fn();
|
||||
// DO NOT delete this file. It is used by vitest to mock the dagre-d3 module.
|
||||
|
95
cSpell.json
Normal file
95
cSpell.json
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"language": "en",
|
||||
"words": [
|
||||
"customizability",
|
||||
"Gantt",
|
||||
"jison",
|
||||
"knsv",
|
||||
"Knut",
|
||||
"mindmap",
|
||||
"Mindmaps",
|
||||
"mitigations",
|
||||
"sandboxed",
|
||||
"Sveidqvist",
|
||||
"verdana",
|
||||
"Visio"
|
||||
],
|
||||
"ignoreWords": [
|
||||
"Adamiecki",
|
||||
"applitools",
|
||||
"Asciidoctor",
|
||||
"Astah",
|
||||
"Bisheng",
|
||||
"codedoc",
|
||||
"Docsy",
|
||||
"Doku",
|
||||
"Gitea",
|
||||
"Gitgraph",
|
||||
"Grav",
|
||||
"Inkdrop",
|
||||
"Jaoude",
|
||||
"mdbook",
|
||||
"mermerd",
|
||||
"mkdocs",
|
||||
"phpbb",
|
||||
"Plantuml",
|
||||
"Playfair's",
|
||||
"Podlite",
|
||||
"redmine",
|
||||
"sphinxcontrib",
|
||||
"Tuleap"
|
||||
],
|
||||
"patterns": [
|
||||
{
|
||||
"name": "Markdown links",
|
||||
"pattern": "\\((.*)\\)",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Markdown code blocks",
|
||||
"pattern": "/^(\\s*`{3,}).*[\\s\\S]*?^\\1/gmx",
|
||||
"description": "Taken from the cSpell example at https://cspell.org/configuration/patterns/#verbose-regular-expressions"
|
||||
},
|
||||
{
|
||||
"name": "Inline code blocks",
|
||||
"pattern": "\\`([^\\`\\r\\n]+?)\\`",
|
||||
"description": "https://stackoverflow.com/questions/41274241/how-to-capture-inline-markdown-code-but-not-a-markdown-code-fence-with-regex"
|
||||
},
|
||||
{
|
||||
"name": "Link contents",
|
||||
"pattern": "\\<a(.*)\\>",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Snippet references",
|
||||
"pattern": "-- snippet:(.*)",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Snippet references 2",
|
||||
"pattern": "\\<\\[sample:(.*)",
|
||||
"description": "another kind of snippet reference"
|
||||
},
|
||||
{
|
||||
"name": "Multi-line code blocks",
|
||||
"pattern": "/^\\s*```[\\s\\S]*?^\\s*```/gm"
|
||||
},
|
||||
{
|
||||
"name": "HTML Tags",
|
||||
"pattern": "<[^>]*>",
|
||||
"description": "Reference: https://stackoverflow.com/questions/11229831/regular-expression-to-remove-html-tags-from-a-string"
|
||||
}
|
||||
],
|
||||
"ignoreRegExpList": [
|
||||
"Markdown links",
|
||||
"Markdown code blocks",
|
||||
"Inline code blocks",
|
||||
"Link contents",
|
||||
"Snippet references",
|
||||
"Snippet references 2",
|
||||
"Multi-line code blocks",
|
||||
"HTML Tags"
|
||||
],
|
||||
"ignorePaths": ["packages/mermaid/src/docs/CHANGELOG.md"]
|
||||
}
|
@@ -75,16 +75,6 @@ classDiagram
|
||||
<pre id="diagram" class="mermaid2">
|
||||
mindmap
|
||||
root
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
A2
|
||||
B2
|
||||
C2
|
||||
D2
|
||||
E2
|
||||
child1((Circle))
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
|
@@ -14,16 +14,14 @@
|
||||
mermaid.init({ startOnLoad: false });
|
||||
|
||||
mermaid.mermaidAPI.initialize({ securityLevel: 'strict' });
|
||||
(async () => {
|
||||
try {
|
||||
console.log('rendering');
|
||||
await mermaid.mermaidAPI.render('graphDiv', `>`);
|
||||
} catch (e) {}
|
||||
try {
|
||||
console.log('rendering');
|
||||
mermaid.mermaidAPI.render('graphDiv', `>`);
|
||||
} catch (e) {}
|
||||
|
||||
await mermaid.mermaidAPI.render('graphDiv', `graph LR\n a --> b`, (html) => {
|
||||
document.getElementById('graph').innerHTML = html;
|
||||
});
|
||||
})();
|
||||
mermaid.mermaidAPI.render('graphDiv', `graph LR\n a --> b`, (html) => {
|
||||
document.getElementById('graph').innerHTML = html;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -19,10 +19,9 @@
|
||||
function rerender(text) {
|
||||
const graphText = `graph TD
|
||||
A[${text}] -->|Get money| B(Go shopping)`;
|
||||
mermaid.mermaidAPI.render('id', graphText).then((svg) => {
|
||||
console.log('\x1b[35m%s\x1b[0m', '>> graph', svg);
|
||||
document.getElementById('graph').innerHTML = svg;
|
||||
});
|
||||
const graph = mermaid.mermaidAPI.render('id', graphText);
|
||||
console.log('\x1b[35m%s\x1b[0m', '>> graph', graph);
|
||||
document.getElementById('graph').innerHTML = graph;
|
||||
}
|
||||
</script>
|
||||
<button id="rerender" onclick="rerender('Saturday')">Rerender</button>
|
||||
|
14
cypress/platform/sidv.html
Normal file
14
cypress/platform/sidv.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
<pre class="mermaid">
|
||||
none
|
||||
hello world
|
||||
</pre>
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
logLevel: 1,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'classDiagram\n';
|
||||
let diagram = 'classDiagram\n';
|
||||
diagram += 'class Square~<img/src';
|
||||
diagram += "='1'/onerror=xssAttack()>~{\n";
|
||||
diagram += 'id A\n';
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'stateDiagram-v2\n';
|
||||
let diagram = 'stateDiagram-v2\n';
|
||||
diagram += 's2 : This is a state description<img/src';
|
||||
diagram += "='1'/onerror=xssAttack()>";
|
||||
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'stateDiagram-v2\n';
|
||||
let diagram = 'stateDiagram-v2\n';
|
||||
diagram += 's2 : A<img/src';
|
||||
diagram += "='1'/onerror=xssAttack()>";
|
||||
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'stateDiagram-v2\n';
|
||||
let diagram = 'stateDiagram-v2\n';
|
||||
diagram += 'if_state --> False: if n < 0<img/src';
|
||||
diagram += "='1'/onerror=xssAttack()>";
|
||||
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'classDiagram\n';
|
||||
let diagram = 'classDiagram\n';
|
||||
diagram += 'classA <-- classB : <ifr';
|
||||
diagram += "ame/srcdoc='<scr";
|
||||
diagram += 'ipt>parent.xssAttack(`XSS`)</';
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = `sequenceDiagram
|
||||
let diagram = `sequenceDiagram
|
||||
participant John
|
||||
links John: {"XSS": "javas`;
|
||||
diagram += `cript:alert('AudioParam')"}`;
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = `sequenceDiagram
|
||||
let diagram = `sequenceDiagram
|
||||
participant Alice
|
||||
links Alice: { "Click me!" : "javasjavascript:cript:alert('goose')" }`;
|
||||
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = `sequenceDiagram
|
||||
let diagram = `sequenceDiagram
|
||||
participant Alice
|
||||
link Alice: Click Me!@javasjavascript:cript:alert("goose")`;
|
||||
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = `classDiagram
|
||||
let diagram = `classDiagram
|
||||
Class "<img/src='x'/onerror=xssAttack(1)>" <--> "<img/src='x'/onerror=xssAttack(2)>" C2: Cool label`;
|
||||
|
||||
// // var diagram = "stateDiagram-v2\n";
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = `classDiagram
|
||||
let diagram = `classDiagram
|
||||
class Shape{
|
||||
<<<img/src='1'/`;
|
||||
|
||||
|
@@ -54,9 +54,9 @@
|
||||
startOnLoad: true,
|
||||
useMaxWidth: true,
|
||||
});
|
||||
var cnt = 0;
|
||||
var a;
|
||||
var handler = setInterval(() => {
|
||||
let cnt = 0;
|
||||
let a;
|
||||
const handler = setInterval(() => {
|
||||
cnt++;
|
||||
a = {};
|
||||
if (typeof a.polluted !== 'undefined') {
|
||||
|
@@ -96,7 +96,7 @@
|
||||
// var diagram = ` graph TD
|
||||
// A --> B["<a href='javasc`;
|
||||
// diagram += `ript#colon;xssAttack()'>AAA</a>"]`;
|
||||
var diagram = ` graph TD
|
||||
let diagram = ` graph TD
|
||||
A --> B["<a href='javasc`;
|
||||
diagram += `ript#colon;xssAttack()'>AAA</a>"]`;
|
||||
// diagram += '//via.placeholder.com/64\' width=64 />"]';
|
||||
|
@@ -96,7 +96,7 @@
|
||||
// var diagram = ` graph TD
|
||||
// A --> B["<a href='javasc`;
|
||||
// diagram += `ript#colon;xssAttack()'>AAA</a>"]`;
|
||||
var diagram = ` graph TD
|
||||
let diagram = ` graph TD
|
||||
A --> B["<a href='javasc`;
|
||||
diagram += `ript#9;t#colon;xssAttack()'>AAA</a>"]`;
|
||||
// diagram += '//via.placeholder.com/64\' width=64 />"]';
|
||||
|
@@ -42,9 +42,9 @@
|
||||
startOnLoad: true,
|
||||
useMaxWidth: true,
|
||||
});
|
||||
var cnt = 0;
|
||||
var a;
|
||||
var handler = setInterval(() => {
|
||||
let cnt = 0;
|
||||
let a;
|
||||
const handler = setInterval(() => {
|
||||
cnt++;
|
||||
a = {};
|
||||
if (typeof a.polluted !== 'undefined') {
|
||||
|
@@ -85,7 +85,7 @@
|
||||
alert('It worked');
|
||||
}
|
||||
|
||||
var diagram = '%%{init: {"flowchart": {"htmlLabels": "true"}} }%%\n';
|
||||
let diagram = '%%{init: {"flowchart": {"htmlLabels": "true"}} }%%\n';
|
||||
diagram += 'flowchart\n';
|
||||
diagram += 'A["<ifra';
|
||||
diagram += "me srcdoc='<scrip";
|
||||
|
@@ -92,7 +92,7 @@
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
var diagram = 'graph LR\n';
|
||||
let diagram = 'graph LR\n';
|
||||
diagram += 'B-->D("<img onerror=location=`java';
|
||||
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";
|
||||
diagram += 'script\x3a;xssAttack\u0028\u0029` src=x>");\n';
|
||||
|
@@ -92,7 +92,7 @@
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
var diagram = 'graph LR\n';
|
||||
let diagram = 'graph LR\n';
|
||||
diagram += 'A(<img/src/onerror=xssAttack`1`>)';
|
||||
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";
|
||||
console.log(diagram);
|
||||
|
@@ -92,7 +92,7 @@
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
var diagram = 'graph LR\n';
|
||||
let diagram = 'graph LR\n';
|
||||
diagram += " B(<a href='<";
|
||||
diagram += 'script></';
|
||||
diagram += "script>Javascript:xssAttack`1`'>Click)";
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'stateDiagram-v2\n';
|
||||
let diagram = 'stateDiagram-v2\n';
|
||||
diagram += "<img/src='1'/onerror=xssAttack()> --> B";
|
||||
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";
|
||||
console.log(diagram);
|
||||
|
@@ -93,7 +93,7 @@
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
var diagram = 'stateDiagram-v2\n';
|
||||
let diagram = 'stateDiagram-v2\n';
|
||||
diagram += "<img/src='1'/onerror=xssAttack()> --> B";
|
||||
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";
|
||||
console.log(diagram);
|
||||
|
@@ -277,7 +277,7 @@
|
||||
<script>
|
||||
function testClick(nodeId) {
|
||||
console.log('clicked', nodeId);
|
||||
var originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
let originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
document.querySelector('body').style.backgroundColor = 'yellow';
|
||||
setTimeout(function () {
|
||||
document.querySelector('body').style.backgroundColor = originalBgColor;
|
||||
|
@@ -46,7 +46,7 @@
|
||||
<script>
|
||||
function testClick(nodeId) {
|
||||
console.log('clicked', nodeId);
|
||||
var originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
let originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
document.querySelector('body').style.backgroundColor = 'yellow';
|
||||
setTimeout(function () {
|
||||
document.querySelector('body').style.backgroundColor = originalBgColor;
|
||||
|
@@ -1513,7 +1513,7 @@
|
||||
<script>
|
||||
function testClick(nodeId) {
|
||||
console.log('clicked', nodeId);
|
||||
var originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
let originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
document.querySelector('body').style.backgroundColor = 'yellow';
|
||||
setTimeout(function () {
|
||||
document.querySelector('body').style.backgroundColor = originalBgColor;
|
||||
|
@@ -174,7 +174,7 @@
|
||||
}
|
||||
function testClick(nodeId) {
|
||||
console.log('clicked', nodeId);
|
||||
var originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
let originalBgColor = document.querySelector('body').style.backgroundColor;
|
||||
document.querySelector('body').style.backgroundColor = 'yellow';
|
||||
setTimeout(function () {
|
||||
document.querySelector('body').style.backgroundColor = originalBgColor;
|
||||
|
@@ -10,7 +10,7 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
|
||||
|
||||
<img src="img/header.png" alt="" />
|
||||
|
||||
[](https://travis-ci.org/mermaid-js/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
<!-- Mermaid book banner -->
|
||||
|
||||
@@ -271,16 +271,16 @@ To Deploy Mermaid:
|
||||
|
||||
### [Mermaid API](./Setup.md):
|
||||
|
||||
**To deploy mermaid without a bundler, one can insert a `script` tag with an absolute address and a `mermaidAPI` call into the HTML like so:**
|
||||
**To deploy mermaid without a bundler, one can insert a `script` tag with an absolute address and a `mermaid.initialize` call into the HTML like so:**
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
```
|
||||
|
||||
**Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into SVG charts.**
|
||||
**Doing so will command the mermaid parser to look for the `<div>` or `<pre>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into SVG charts.**
|
||||
|
||||
**Examples can be found at** [Other examples](/examples)
|
||||
|
||||
@@ -347,7 +347,7 @@ Update version number in `package.json`.
|
||||
npm publish
|
||||
```
|
||||
|
||||
The above command generates files into the `dist` folder and publishes them to npmjs.org.
|
||||
The above command generates files into the `dist` folder and publishes them to \<npmjs.org>.
|
||||
|
||||
## Related projects
|
||||
|
||||
@@ -363,7 +363,7 @@ Detailed information about how to contribute can be found in the [contribution g
|
||||
|
||||
## Security and safe diagrams
|
||||
|
||||
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitise the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
|
||||
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitize the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
|
||||
|
||||
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing JavaScript in the code from being executed. This is a great step forward for better security.
|
||||
|
||||
|
@@ -74,15 +74,15 @@ Theme , the CSS style sheet
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| ------------- | --------------------------------- | ------ | -------- | ------------------------------------------ |
|
||||
| securityLevel | Level of trust for parsed diagram | string | Required | 'sandbox', 'strict', 'loose', 'antiscript' |
|
||||
| securityLevel | Level of trust for parsed diagram | string | Required | `sandbox`, `strict`, `loose`, `antiscript` |
|
||||
|
||||
**Notes**:
|
||||
|
||||
- **strict**: (**default**) tags in text are encoded, click functionality is disabled
|
||||
- **loose**: tags in text are allowed, click functionality is enabled
|
||||
- **antiscript**: html tags in text are allowed, (only script element is removed), click
|
||||
- **`strict`**: (**default**) tags in text are encoded, click functionality is disabled
|
||||
- **`loose`**: tags in text are allowed, click functionality is enabled
|
||||
- **`antiscript`**: html tags in text are allowed, (only script element is removed), click
|
||||
functionality is enabled
|
||||
- **sandbox**: With this security level all rendering takes place in a sandboxed iframe. This
|
||||
- **`sandbox`**: With this security level all rendering takes place in a sandboxed iframe. This
|
||||
prevent any JavaScript from running in the context. This may hinder interactive functionality
|
||||
of the diagram like scripts, popups in sequence diagram or links to other tabs/targets etc.
|
||||
|
||||
@@ -121,11 +121,11 @@ Default value: \['secure', 'securityLevel', 'startOnLoad', 'maxTextSize']
|
||||
|
||||
This option controls if the generated ids of nodes in the SVG are generated randomly or based
|
||||
on a seed. If set to false, the IDs are generated based on the current date and thus are not
|
||||
deterministic. This is the default behaviour.
|
||||
deterministic. This is the default behavior.
|
||||
|
||||
**Notes**:
|
||||
|
||||
This matters if your files are checked into sourcecontrol e.g. git and should not change unless
|
||||
This matters if your files are checked into source control e.g. git and should not change unless
|
||||
content is changed.
|
||||
|
||||
Default value: false
|
||||
@@ -212,16 +212,16 @@ Default value: true
|
||||
|
||||
### defaultRenderer
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | --------------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | `dagre-d3`, `dagre-wrapper` |
|
||||
|
||||
**Notes:**
|
||||
|
||||
Decides which rendering engine that is to be used for the rendering. Legal values are:
|
||||
dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid
|
||||
`dagre-d3` `dagre-wrapper` - wrapper for `dagre` implemented in mermaid
|
||||
|
||||
Default value: 'dagre-wrapper'
|
||||
Default value: `dagre-wrapper`
|
||||
|
||||
## sequence
|
||||
|
||||
@@ -737,16 +737,16 @@ Default value: true
|
||||
|
||||
## defaultRenderer
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | --------------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | `dagre-d3`, `dagre-wrapper` |
|
||||
|
||||
**Notes**:
|
||||
|
||||
Decides which rendering engine that is to be used for the rendering. Legal values are:
|
||||
dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid
|
||||
`dagre-d3` `dagre-wrapper` - wrapper for `dagre` implemented in mermaid
|
||||
|
||||
Default value: 'dagre-d3'
|
||||
Default value: `dagre-d3`
|
||||
|
||||
## useMaxWidth
|
||||
|
||||
@@ -763,16 +763,16 @@ Default value: true
|
||||
|
||||
## defaultRenderer
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | --------------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | `dagre-d3`, `dagre-wrapper` |
|
||||
|
||||
**Notes:**
|
||||
|
||||
Decides which rendering engine that is to be used for the rendering. Legal values are:
|
||||
dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid
|
||||
`dagre-d3` `dagre-wrapper` - wrapper for `dagre` implemented in mermaid
|
||||
|
||||
Default value: 'dagre-d3'
|
||||
Default value: `dagre-d3`
|
||||
|
||||
## er
|
||||
|
||||
@@ -994,7 +994,7 @@ Default value: 4
|
||||
| --------------- | ----------- | ------- | -------- | ------------------ |
|
||||
| c4BoundaryInRow | See Notes | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** How many boundarys to place in each row.
|
||||
**Notes:** How many boundaries to place in each row.
|
||||
|
||||
Default value: 2
|
||||
|
||||
@@ -1561,7 +1561,7 @@ Returns **void**
|
||||
|
||||
```html
|
||||
<script>
|
||||
var config = {
|
||||
const config = {
|
||||
theme: 'default',
|
||||
logLevel: 'fatal',
|
||||
securityLevel: 'strict',
|
||||
|
@@ -19,7 +19,7 @@ The diagram authors can now add the accessibility options in the diagram definit
|
||||
- `accTitle: "Your Accessibility Title"` or
|
||||
- `accDescr: "Your Accessibility Description"`
|
||||
|
||||
**When these two options are defined, they will add a coressponding `<title>` and `<desc>` tag in the SVG.**
|
||||
**When these two options are defined, they will add a corresponding `<title>` and `<desc>` tag in the SVG.**
|
||||
|
||||
Let us take a look at the following example with a flowchart diagram:
|
||||
|
||||
|
@@ -220,7 +220,7 @@ The following unfinished features are not supported in the short term.
|
||||
|
||||
- - \[x] RelIndex \* Compatible with C4-Plantuml syntax, but ignores the index parameter. The sequence number is determined by the order in which the rel statements are written.
|
||||
|
||||
- \[ ] Custom tags/stereotypes support and skinparam updates
|
||||
- \[ ] Custom tags/stereotypes support and skin param updates
|
||||
|
||||
- - \[ ] AddElementTag(tagStereo, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new element tag. The styles of the tagged elements are updated and the tag is displayed in the calculated legend.
|
||||
|
||||
|
@@ -589,7 +589,7 @@ click Shape2 call callbackFunction() "This is a tooltip for a callback"
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callbackFunction = function () {
|
||||
const callbackFunction = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
</script>
|
||||
@@ -653,10 +653,10 @@ Beginner's tip—a full example using interactive links in an HTML page:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
securityLevel: 'loose',
|
||||
};
|
||||
|
@@ -649,7 +649,7 @@ A node can have click events bound that lead to either a JavaScript callback or
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callback = function (nodeId) {
|
||||
const callback = function (nodeId) {
|
||||
alert('A callback was triggered on ' + nodeId);
|
||||
};
|
||||
</script>
|
||||
@@ -727,10 +727,10 @@ Beginner's tip—here's a full example of using interactive links in HTML:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
|
@@ -264,6 +264,20 @@ flowchart LR
|
||||
A --- B
|
||||
```
|
||||
|
||||
### An invisisble link
|
||||
|
||||
This can be a usefull tool in some instances where you want to alter the default positining of a node.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A ~~~ B
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A ~~~ B
|
||||
```
|
||||
|
||||
### Text on links
|
||||
|
||||
```mermaid-example
|
||||
@@ -695,7 +709,7 @@ Examples of tooltip usage below:
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
</script>
|
||||
@@ -771,10 +785,10 @@ Beginner's tip—a full example using interactive links in a html context:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' },
|
||||
securityLevel: 'loose',
|
||||
|
@@ -391,13 +391,13 @@ Beginner's tip—a full example using interactive links in an html context:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var printArguments = function (arg1, arg2, arg3) {
|
||||
const printArguments = function (arg1, arg2, arg3) {
|
||||
alert('printArguments called with arguments: ' + arg1 + ', ' + arg2 + ', ' + arg3);
|
||||
};
|
||||
var printTask = function (taskId) {
|
||||
const printTask = function (taskId) {
|
||||
alert('taskId: ' + taskId);
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
securityLevel: 'loose',
|
||||
};
|
||||
|
@@ -21,15 +21,13 @@
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"
|
||||
/>
|
||||
<script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script>
|
||||
<!-- <script src="http://localhost:9000/mermaid.js"></script> -->
|
||||
<script
|
||||
defer=""
|
||||
data-domain="mermaid-js.github.io"
|
||||
src="https://plausible.io/js/plausible.js"
|
||||
></script>
|
||||
<script>
|
||||
var require = {
|
||||
const require = {
|
||||
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs' },
|
||||
};
|
||||
</script>
|
||||
@@ -50,17 +48,46 @@
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
// import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9.2.0-rc6/dist/mermaid.esm.min.mjs';
|
||||
// import mermaid from 'http://localhost:9000/mermaid.esm.mjs';
|
||||
console.log(mermaid); // eslint-disable-line
|
||||
window.mermaid = mermaid;
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: true,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
lazyLoadedDiagrams: [
|
||||
'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@9.2.0-rc3/dist/mermaid-mindmap-detector.esm.mjs',
|
||||
// 'http://localhost:9000/mermaid-mindmap-detector.esm.mjs',
|
||||
],
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
|
||||
async function loadMermaid() {
|
||||
await mermaid.initialize(conf);
|
||||
console.log('mermaid initialized'); // eslint-disable-line
|
||||
}
|
||||
mermaid.parseError = (e) => {
|
||||
console.log('parse error', e); // eslint-disable-line
|
||||
};
|
||||
await loadMermaid();
|
||||
</script>
|
||||
<script>
|
||||
var initEditor = exports.default;
|
||||
var parser = new DOMParser();
|
||||
var currentCodeExample = 0;
|
||||
var colorize = [];
|
||||
let initEditor = exports.default;
|
||||
let parser = new DOMParser();
|
||||
let currentCodeExample = 0;
|
||||
let colorize = [];
|
||||
let num = 0;
|
||||
|
||||
function colorizeEverything(html) {
|
||||
initEditor(monaco);
|
||||
return new Promise((resolve, reject) => {
|
||||
monaco.editor.setTheme('mermaid');
|
||||
var parsed = parser.parseFromString(html, 'text/html').body;
|
||||
const parsed = parser.parseFromString(html, 'text/html').body;
|
||||
Promise.all(
|
||||
[...parsed.querySelectorAll('pre[id*="code"]')].map((codeBlock) =>
|
||||
monaco.editor.colorize(codeBlock.innerText, 'mermaid')
|
||||
@@ -95,13 +122,12 @@
|
||||
renderer: {
|
||||
code: function (code, lang) {
|
||||
if (lang === 'mermaid-example') {
|
||||
console.log('An example'); // eslint-disable-line
|
||||
currentCodeExample++;
|
||||
colorize.push(currentCodeExample);
|
||||
return '<pre id="code' + currentCodeExample + '">' + escapeHTML(code) + '</pre>';
|
||||
} else if (lang === 'mermaid') {
|
||||
return (
|
||||
'<pre class="mermaid">' + mermaid.render('mermaid-svg-' + num++, code) + '</pre>'
|
||||
);
|
||||
return '<pre class="mermaid">' + code + '</pre>';
|
||||
}
|
||||
return this.origin.code.apply(this, arguments);
|
||||
},
|
||||
@@ -117,9 +143,13 @@
|
||||
function (hook, vm) {
|
||||
hook.beforeEach(function (html) {
|
||||
url = 'https://github.com/mermaid-js/mermaid/blob/develop/src/docs/' + vm.route.file;
|
||||
var editHtml = '[:memo: Edit this Page](' + url + ')\n';
|
||||
const editHtml = '[:memo: Edit this Page](' + url + ')\n';
|
||||
return editHtml + html;
|
||||
});
|
||||
// Invoked on each page load after new HTML has been appended to the DOM
|
||||
hook.doneEach(function () {
|
||||
window.mermaid.init();
|
||||
});
|
||||
|
||||
hook.afterEach(function (html, next) {
|
||||
next(html);
|
||||
@@ -135,29 +165,17 @@
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
var num = 0;
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: false,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
mermaid.initialize(conf);
|
||||
</script>
|
||||
<script>
|
||||
window.onhashchange = function (a) {
|
||||
//code
|
||||
if (location) {
|
||||
ga('send', 'pageview', location.hash);
|
||||
}
|
||||
// if (location && ga) {
|
||||
// ga('send', 'pageview', location.hash);
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script>
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script> -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-coffeescript.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -33,7 +33,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Mermaid Macro](https://www.redmine.org/plugins/redmine_mermaid_macro)
|
||||
- [redmine-mermaid](https://github.com/styz/redmine_mermaid)
|
||||
- [markdown-for-mermaid-plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
|
||||
- [Jetsbrain IDE eg Pycharm](https://www.jetbrains.com/go/guide/tips/mermaid-js-support-in-markdown/)
|
||||
- [JetBrains IDE eg Pycharm](https://www.jetbrains.com/go/guide/tips/mermaid-js-support-in-markdown/)
|
||||
- [mermerd](https://github.com/KarnerTh/mermerd)
|
||||
|
||||
## CRM/ERP/Similar
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
# Mindmap
|
||||
|
||||
> Mindmap: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stabel except for the icon integration which is the experimental part.
|
||||
> Mindmap: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
|
||||
|
||||
"A mind map is a diagram used to visually organize information into a hierarchy, showing relationships among pieces of the whole. It is often created around a single concept, drawn as an image in the center of a blank page, to which associated representations of ideas such as images, words and parts of words are added. Major ideas are connected directly to the central concept, and other ideas branch out from those major ideas." Wikipedia
|
||||
|
||||
@@ -26,7 +26,6 @@ mindmap
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
|
||||
```
|
||||
|
||||
```mermaid
|
||||
@@ -47,14 +46,13 @@ mindmap
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax for creating Mindmaps is simple and relies on indentation for setting the levels in the hierarchy.
|
||||
|
||||
In the following example you can see how there are 3 dufferent levels. One with starting at the left of the text and another level with two rows starting at the same column, defining the node A. At the end there is one more level where the text is indented further then the prevoius lines defining the nodes B and C.
|
||||
In the following example you can see how there are 3 different levels of indentation. The leftmost indentation is the root of the mindmap. There can only be one root and if you by misstake add two of them on the same level there will be a syntax error. Rows with larger indentation will be connected as children to the previous row with lower indentation. Based on that you can see in the example how the nodes B and C both are children to node A whci in turn is a child of the node Root.
|
||||
|
||||
mindmap
|
||||
Root
|
||||
@@ -62,7 +60,7 @@ In the following example you can see how there are 3 dufferent levels. One with
|
||||
B
|
||||
C
|
||||
|
||||
In summary is a simple text outline where there are one node at the root level called `Root` which has one child `A`. `A` in turn has two children `B`and `C`. In the diagram below we can see this rendered as a mindmap.
|
||||
In the diagram below you can see the example rendered as a mindmap.
|
||||
|
||||
```mermaid-example
|
||||
mindmap
|
||||
@@ -220,7 +218,7 @@ The actual indentation does not really matter only compared with the previous ro
|
||||
B
|
||||
C
|
||||
|
||||
This outline is unclear as `B` clearly is a child of `A` but when we move on to `C` the clarity is lost. `C` is not a child of `B` with a higher indentation nor does it have the same indentation as `B`. The only thing that is clear is that the first node with smaller indentation, indicating a parent, is A. Then Mermaid relies on this known truth and compensates for the unclear indentation and selects `A` as a parent of `C` leading till the same diagram with `B` and `C` as siblings.
|
||||
This outline is unclear as `B` clearly is a child of `A` but when we move on to `C` the clarity is lost. `C` is not a child of `B` with a higher indentation nor does it have the same indentation as `B`. The only thing that is clear is that the first node with smaller indentation, indicating a parent, is A. Mermaid will rely on this known truth and compensates for the unclear indentation and selects `A` as a parent of `C` leading till the same diagram with `B` and `C` as siblings.
|
||||
|
||||
```mermaid-example
|
||||
mindmap
|
||||
|
@@ -75,23 +75,13 @@ The API works by pulling rendering instructions from the source `mermaid.js` in
|
||||
|
||||
### Requirements for the Mermaid API.
|
||||
|
||||
When writing the .html file, we give three instructions inside the html code to the web browser:
|
||||
When writing the .html file, we give two instructions inside the html code to the web browser:
|
||||
|
||||
a. A reference for fetching the online mermaid renderer, through the `mermaid.js` or `mermaid.min.js`.
|
||||
a. The mermaid code for the diagram we want to create.
|
||||
|
||||
b. The mermaid code for the diagram we want to create.
|
||||
b. The importing of mermaid library through the `mermaid.esm.js` or `mermaid.esm.min.mjs` and the `mermaid.initialize()` call, which dictates the appearance of diagrams and also starts the rendering process .
|
||||
|
||||
c. The `mermaid.initialize()` call, which dictates the appearance of diagrams and also starts the rendering process .
|
||||
|
||||
**a. A reference to the external CDN in a `<script src>` tag, or a reference to mermaid.js as a separate file.:**
|
||||
|
||||
```html
|
||||
<body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
</body>
|
||||
```
|
||||
|
||||
**b. The embedded mermaid diagram definition inside a `<pre class="mermaid">`:**
|
||||
**a. The embedded mermaid diagram definition inside a `<pre class="mermaid">`:**
|
||||
|
||||
```html
|
||||
<body>
|
||||
@@ -107,13 +97,14 @@ c. The `mermaid.initialize()` call, which dictates the appearance of diagrams an
|
||||
|
||||
**Notes**: Every Mermaid chart/graph/diagram definition, should have separate `<pre>` tags.
|
||||
|
||||
**c. The `mermaid.initialize()` call.**
|
||||
**b. The import of mermaid and the `mermaid.initialize()` call.**
|
||||
|
||||
`mermaid.initialize()` call takes all the definitions contained in all the `<pre class="mermaid">` tags that it finds in the html body and renders them into diagrams. Example:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<script>
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
</body>
|
||||
@@ -135,11 +126,6 @@ Rendering in Mermaid is initialized by `mermaid.initialize()` call. You can plac
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
|
||||
Here is one mermaid diagram:
|
||||
<pre class="mermaid">
|
||||
graph TD
|
||||
@@ -156,6 +142,11 @@ Rendering in Mermaid is initialized by `mermaid.initialize()` call. You can plac
|
||||
B -->|tcp_456| C[Server1]
|
||||
B -->|tcp_456| D[Server2]
|
||||
</pre>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -181,8 +172,8 @@ In this example mermaid.js is referenced in `src` as a separate JavaScript file,
|
||||
B --> C[Server1]
|
||||
B --> D[Server2]
|
||||
</pre>
|
||||
<script src="The\Path\In\Your\Package\mermaid.js"></script>
|
||||
<script>
|
||||
<script type="module">
|
||||
import mermaid from 'The/Path/In/Your/Package/mermaid.esm.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
</body>
|
||||
@@ -206,4 +197,4 @@ In this example mermaid.js is referenced in `src` as a separate JavaScript file,
|
||||
|
||||
**Comments from Knut Sveidqvist, creator of mermaid:**
|
||||
|
||||
- In early versions of mermaid, the `<script src>` tag was invoked in the `<head>` part of the web page. Nowadays we can place it in the `<body>` as seen above. Older parts of the documentation frequently reflects the previous way which still works.
|
||||
- In early versions of mermaid, the `<script>` tag was invoked in the `<head>` part of the web page. Nowadays we can place it in the `<body>` as seen above. Older parts of the documentation frequently reflects the previous way which still works.
|
||||
|
@@ -39,23 +39,9 @@ We have compiled some Video [Tutorials](./Tutorials.md) on how to use the mermai
|
||||
|
||||
> Note:This topic explored in greater depth in the [User Guide for Beginners](./n00b-gettingStarted.md)
|
||||
|
||||
The easiest way to integrate mermaid on a web page requires three elements:
|
||||
The easiest way to integrate mermaid on a web page requires two elements:
|
||||
|
||||
1. Inclusion of the mermaid address in the html page using a `script` tag, in the `src` section.Example:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
```
|
||||
|
||||
2. The `mermaidAPI` call, in a separate `script` tag. Example:
|
||||
|
||||
```html
|
||||
<script>
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
```
|
||||
|
||||
3. A graph definition, inside `<div>` tags labeled `class=mermaid`. Example:
|
||||
- A graph definition, inside `<pre>` tags labeled `class=mermaid`. Example:
|
||||
|
||||
```html
|
||||
<pre class="mermaid">
|
||||
@@ -66,8 +52,18 @@ The easiest way to integrate mermaid on a web page requires three elements:
|
||||
</pre>
|
||||
```
|
||||
|
||||
**Following these directions, mermaid starts at page load and (when the page has loaded) it will
|
||||
locate the graph definitions inside the `div` tags with `class="mermaid"` and return diagrams in SVG form, following given definitions.**
|
||||
- Inclusion of the mermaid address in the html page body using a `script` tag as an ESM import, and the `mermaidAPI` call.
|
||||
|
||||
Example:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
```
|
||||
|
||||
**Following these directions, mermaid starts at page load and (when the page has loaded) it will locate the graph definitions inside the `pre` tags with `class="mermaid"` and return diagrams in SVG form, following given definitions.**
|
||||
|
||||
## Simple full example:
|
||||
|
||||
@@ -84,8 +80,8 @@ locate the graph definitions inside the `div` tags with `class="mermaid"` and re
|
||||
B-->C[fa:fa-ban forbidden]
|
||||
B-->D(fa:fa-spinner);
|
||||
</pre>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
</body>
|
||||
@@ -204,18 +200,17 @@ fetch the graph definition from the site (perhaps from a textarea), render it an
|
||||
The example below show an outline of how this could be used. The example just logs the resulting SVG to the JavaScript console.
|
||||
|
||||
```html
|
||||
<script src="mermaid.js"></script>
|
||||
|
||||
<script>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.mjs';
|
||||
mermaid.mermaidAPI.initialize({ startOnLoad: false });
|
||||
$(function () {
|
||||
$(async function () {
|
||||
// Example of using the API var
|
||||
element = document.querySelector('#graphDiv');
|
||||
var insertSvg = function (svgCode, bindFunctions) {
|
||||
const insertSvg = function (svgCode, bindFunctions) {
|
||||
element.innerHTML = svgCode;
|
||||
};
|
||||
var graphDefinition = 'graph TB\na-->b';
|
||||
var graph = mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
|
||||
const graphDefinition = 'graph TB\na-->b';
|
||||
const graph = await mermaid.mermaidAPI.render('graphDiv', graphDefinition, insertSvg);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
@@ -339,7 +334,7 @@ on what kind of integration you use.
|
||||
```html
|
||||
<script src="../dist/mermaid.js"></script>
|
||||
<script>
|
||||
var config = { startOnLoad: true, flowchart: { useMaxWidth: false, htmlLabels: true } };
|
||||
let config = { startOnLoad: true, flowchart: { useMaxWidth: false, htmlLabels: true } };
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
```
|
||||
|
17
package.json
17
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mermaid-monorepo",
|
||||
"private": true,
|
||||
"version": "9.2.0-rc2",
|
||||
"version": "9.2.0-rc4",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid.core.mjs",
|
||||
"module": "dist/mermaid.core.mjs",
|
||||
@@ -26,12 +26,12 @@
|
||||
"git graph"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build:mermaid": "ts-node-esm --transpileOnly --project=.vite/tsconfig.json .vite/build.ts --mermaid",
|
||||
"build:vite": "ts-node-esm --transpileOnly --project=.vite/tsconfig.json .vite/build.ts",
|
||||
"build:types": "concurrently \"tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly\" \"tsc -p ./packages/mermaid-mindmap/tsconfig.json --emitDeclarationOnly\"",
|
||||
"build:watch": "pnpm build:vite --watch",
|
||||
"build": "pnpm clean; concurrently \"pnpm build:vite\" \"pnpm build:types\"",
|
||||
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server\"",
|
||||
"build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"",
|
||||
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
|
||||
"docs:build": "ts-node-esm --transpileOnly packages/mermaid/src/docs.mts",
|
||||
"docs:verify": "pnpm docs:build --verify",
|
||||
"todo-postbuild": "documentation build src/mermaidAPI.ts src/config.ts src/defaultConfig.ts --shallow -f md --markdown-toc false > src/docs/Setup.md && prettier --write src/docs/Setup.md",
|
||||
@@ -48,7 +48,8 @@
|
||||
"e2e": "start-server-and-test dev http://localhost:9000/ cypress",
|
||||
"ci": "vitest run",
|
||||
"test": "pnpm lint && vitest run",
|
||||
"test:watch": "vitest --coverage --watch",
|
||||
"test:watch": "vitest --watch",
|
||||
"test:coverage": "vitest --coverage",
|
||||
"prepublishOnly": "pnpm build && pnpm test",
|
||||
"prepare": "concurrently \"husky install\" \"pnpm build\"",
|
||||
"pre-commit": "lint-staged"
|
||||
@@ -97,6 +98,7 @@
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/jsdom": "^20.0.0",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/mdast": "^3.0.10",
|
||||
"@types/prettier": "^2.7.1",
|
||||
"@types/stylis": "^4.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.39.0",
|
||||
@@ -151,5 +153,8 @@
|
||||
"sideEffects": [
|
||||
"**/*.css",
|
||||
"**/*.scss"
|
||||
]
|
||||
],
|
||||
"volta": {
|
||||
"node": "18.5.0"
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mermaid-js/mermaid-mindmap",
|
||||
"version": "9.2.0-rc2",
|
||||
"version": "9.2.0-rc3",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "dist/mermaid-mindmap.core.mjs",
|
||||
"module": "dist/mermaid-mindmap.core.mjs",
|
||||
@@ -58,6 +58,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^7.4.0",
|
||||
"mermaid": "workspace:*",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"resolutions": {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { MermaidConfig } from 'mermaid/dist/config.type';
|
||||
|
||||
const warning = (s: string) => {
|
||||
// Todo remove debug code
|
||||
console.error('Log function was called before initialization', s); // eslint-disable-line
|
||||
@@ -24,7 +26,7 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
|
||||
};
|
||||
|
||||
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
|
||||
export let getConfig: () => object;
|
||||
export let getConfig: () => MermaidConfig;
|
||||
export let sanitizeText: (str: string) => string;
|
||||
// eslint-disable @typescript-eslint/no-explicit-any
|
||||
export let setupGraphViewbox: (
|
||||
|
@@ -1,16 +1,30 @@
|
||||
/** Created by knut on 15-01-14. */
|
||||
import { sanitizeText, getConfig, log } from './mermaidUtils';
|
||||
import type { DetailedError } from 'mermaid/dist/utils';
|
||||
|
||||
let nodes = [];
|
||||
interface Node {
|
||||
id: number;
|
||||
nodeId: string;
|
||||
level: number;
|
||||
descr: string;
|
||||
type: number;
|
||||
children: Node[];
|
||||
width: number;
|
||||
padding: number;
|
||||
icon?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let nodes: Node[] = [];
|
||||
let cnt = 0;
|
||||
let elements = {};
|
||||
let elements: Record<number, HTMLElement> = {};
|
||||
export const clear = () => {
|
||||
nodes = [];
|
||||
cnt = 0;
|
||||
elements = {};
|
||||
};
|
||||
|
||||
const getParent = function (level) {
|
||||
const getParent = function (level: number) {
|
||||
for (let i = nodes.length - 1; i >= 0; i--) {
|
||||
if (nodes[i].level < level) {
|
||||
return nodes[i];
|
||||
@@ -23,28 +37,21 @@ const getParent = function (level) {
|
||||
export const getMindmap = () => {
|
||||
return nodes.length > 0 ? nodes[0] : null;
|
||||
};
|
||||
export const addNode = (level, id, descr, type) => {
|
||||
|
||||
export const addNode = (level: number, id: string, descr: string, type: number) => {
|
||||
log.info('addNode', level, id, descr, type);
|
||||
const conf = getConfig();
|
||||
const node = {
|
||||
const padding = conf.mindmap?.padding ?? 15;
|
||||
const node: Node = {
|
||||
id: cnt++,
|
||||
nodeId: sanitizeText(id),
|
||||
level,
|
||||
descr: sanitizeText(descr),
|
||||
type,
|
||||
children: [],
|
||||
width: getConfig().mindmap.maxNodeWidth,
|
||||
width: getConfig().mindmap?.maxNodeWidth ?? 200,
|
||||
padding: type === nodeType.ROUNDED_RECT || type === nodeType.RECT ? 2 * padding : padding,
|
||||
};
|
||||
switch (node.type) {
|
||||
case nodeType.ROUNDED_RECT:
|
||||
node.padding = 2 * conf.mindmap.padding;
|
||||
break;
|
||||
case nodeType.RECT:
|
||||
node.padding = 2 * conf.mindmap.padding;
|
||||
break;
|
||||
default:
|
||||
node.padding = conf.mindmap.padding;
|
||||
}
|
||||
const parent = getParent(level);
|
||||
if (parent) {
|
||||
parent.children.push(node);
|
||||
@@ -56,9 +63,10 @@ export const addNode = (level, id, descr, type) => {
|
||||
nodes.push(node);
|
||||
} else {
|
||||
// Syntax error ... there can only bee one root
|
||||
let error = new Error(
|
||||
const error = new Error(
|
||||
'There can be only one root. No parent could be found for ("' + node.descr + '")'
|
||||
);
|
||||
// @ts-ignore TODO: Add mermaid error
|
||||
error.hash = {
|
||||
text: 'branch ' + name,
|
||||
token: 'branch ' + name,
|
||||
@@ -81,7 +89,7 @@ export const nodeType = {
|
||||
BANG: 5,
|
||||
};
|
||||
|
||||
export const getType = (startStr, endStr) => {
|
||||
export const getType = (startStr: string, endStr: string): number => {
|
||||
log.debug('In get type', startStr, endStr);
|
||||
switch (startStr) {
|
||||
case '[':
|
||||
@@ -99,11 +107,11 @@ export const getType = (startStr, endStr) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setElementForId = (id, element) => {
|
||||
export const setElementForId = (id: number, element: HTMLElement) => {
|
||||
elements[id] = element;
|
||||
};
|
||||
|
||||
export const decorateNode = (decoration) => {
|
||||
export const decorateNode = (decoration: { icon: string; class: string }) => {
|
||||
const node = nodes[nodes.length - 1];
|
||||
if (decoration && decoration.icon) {
|
||||
node.icon = sanitizeText(decoration.icon);
|
||||
@@ -113,7 +121,7 @@ export const decorateNode = (decoration) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const type2Str = (type) => {
|
||||
export const type2Str = (type: number) => {
|
||||
switch (type) {
|
||||
case nodeType.DEFAULT:
|
||||
return 'no-border';
|
||||
@@ -132,13 +140,15 @@ export const type2Str = (type) => {
|
||||
}
|
||||
};
|
||||
|
||||
export let parseError;
|
||||
export const setErrorHandler = (handler) => {
|
||||
export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void;
|
||||
export let parseError: ParseErrorFunction;
|
||||
export const setErrorHandler = (handler: ParseErrorFunction) => {
|
||||
parseError = handler;
|
||||
};
|
||||
|
||||
// Expose logger to grammar
|
||||
export const getLogger = () => log;
|
||||
|
||||
export const getNodeById = (id) => nodes[id];
|
||||
export const getElementById = (id) => elements[id];
|
||||
export const getNodeById = (id: number): Node => nodes[id];
|
||||
export const getElementById = (id: number | string): HTMLElement =>
|
||||
elements[typeof id === 'string' ? parseInt(id) : id];
|
@@ -34,7 +34,7 @@ function drawNodes(svg, mindmap, section, conf) {
|
||||
* @param cy
|
||||
*/
|
||||
function drawEdges(edgesEl, cy) {
|
||||
cy.edges().map((edge, id) => {
|
||||
cy?.edges().map((edge, id) => {
|
||||
const data = edge.data();
|
||||
if (edge[0]._private.bodyBounds) {
|
||||
const bounds = edge[0]._private.rscratch;
|
||||
@@ -100,9 +100,10 @@ function addNodes(mindmap, cy, conf, level) {
|
||||
*/
|
||||
function layoutMindmap(node, conf) {
|
||||
return new Promise((resolve) => {
|
||||
if (node.children.length === 0) {
|
||||
return node;
|
||||
}
|
||||
// if (node.children.length === 0) {
|
||||
// resolve(node);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Add temporary render element
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "9.2.0-rc2",
|
||||
"version": "9.2.0-rc6",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "./dist/mermaid.core.mjs",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@@ -72,7 +72,8 @@
|
||||
"lodash": "^4.17.21",
|
||||
"moment-mini": "^2.24.0",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2",
|
||||
"stylis": "^4.1.2"
|
||||
"stylis": "^4.1.2",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-cypress": "^3.25.7",
|
||||
@@ -86,6 +87,7 @@
|
||||
"@types/lodash": "^4.14.185",
|
||||
"@types/prettier": "^2.7.0",
|
||||
"@types/stylis": "^4.0.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||
"@typescript-eslint/parser": "^5.37.0",
|
||||
"concurrently": "^7.4.0",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import * as configApi from './config';
|
||||
import { log } from './logger';
|
||||
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { DiagramNotFoundError, getDiagram, registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { detectType, getDiagramLoader } from './diagram-api/detectType';
|
||||
import { isDetailedError } from './utils';
|
||||
export class Diagram {
|
||||
@@ -8,11 +8,18 @@ export class Diagram {
|
||||
parser;
|
||||
renderer;
|
||||
db;
|
||||
private detectTypeFailed = false;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
constructor(public txt: string, parseError?: Function) {
|
||||
const cnf = configApi.getConfig();
|
||||
this.txt = txt;
|
||||
this.type = detectType(txt, cnf);
|
||||
try {
|
||||
this.type = detectType(txt, cnf);
|
||||
} catch (e) {
|
||||
this.handleError(e, parseError);
|
||||
this.type = 'error';
|
||||
this.detectTypeFailed = true;
|
||||
}
|
||||
const diagram = getDiagram(this.type);
|
||||
log.debug('Type ' + this.type);
|
||||
// Setup diagram
|
||||
@@ -32,31 +39,39 @@ export class Diagram {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
parse(text: string, parseError?: Function): boolean {
|
||||
if (this.detectTypeFailed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
text = text + '\n';
|
||||
this.db.clear();
|
||||
this.parser.parse(text);
|
||||
return true;
|
||||
} catch (error) {
|
||||
// Is this the correct way to access mermiad's parseError()
|
||||
// method ? (or global.mermaid.parseError()) ?
|
||||
if (parseError) {
|
||||
if (isDetailedError(error)) {
|
||||
// handle case where error string and hash were
|
||||
// wrapped in object like`const error = { str, hash };`
|
||||
parseError(error.str, error.hash);
|
||||
} else {
|
||||
// assume it is just error string and pass it on
|
||||
parseError(error);
|
||||
}
|
||||
} else {
|
||||
// No mermaid.parseError() handler defined, so re-throw it
|
||||
throw error;
|
||||
}
|
||||
this.handleError(error, parseError);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
handleError(error: unknown, parseError?: Function) {
|
||||
// Is this the correct way to access mermiad's parseError()
|
||||
// method ? (or global.mermaid.parseError()) ?
|
||||
if (parseError) {
|
||||
if (isDetailedError(error)) {
|
||||
// handle case where error string and hash were
|
||||
// wrapped in object like`const error = { str, hash };`
|
||||
parseError(error.str, error.hash);
|
||||
} else {
|
||||
// assume it is just error string and pass it on
|
||||
parseError(error);
|
||||
}
|
||||
} else {
|
||||
// No mermaid.parseError() handler defined, so re-throw it
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
getParser() {
|
||||
return this.parser;
|
||||
}
|
||||
@@ -66,37 +81,36 @@ export class Diagram {
|
||||
}
|
||||
}
|
||||
|
||||
export default Diagram;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export const getDiagramFromText = async (txt: string, parseError?: Function) => {
|
||||
export const getDiagramFromText = (
|
||||
txt: string,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
parseError?: Function
|
||||
): Diagram | Promise<Diagram> => {
|
||||
const type = detectType(txt, configApi.getConfig());
|
||||
try {
|
||||
// Trying to find the diagram
|
||||
getDiagram(type);
|
||||
return new Diagram(txt, parseError);
|
||||
} catch (error) {
|
||||
if (!(error instanceof DiagramNotFoundError)) {
|
||||
log.error(error);
|
||||
throw error;
|
||||
}
|
||||
const loader = getDiagramLoader(type);
|
||||
if (!loader) {
|
||||
throw new Error(`Diagram ${type} not found.`);
|
||||
throw new Error(`Loader for ${type} not found.`);
|
||||
}
|
||||
// Diagram not avaiable, loading it
|
||||
// const path = getPathForDiagram(type);
|
||||
const { diagram } = await loader(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
registerDiagram(
|
||||
type,
|
||||
{
|
||||
db: diagram.db,
|
||||
renderer: diagram.renderer,
|
||||
parser: diagram.parser,
|
||||
styles: diagram.styles,
|
||||
},
|
||||
diagram.injectUtils
|
||||
);
|
||||
// await loadDiagram('./packages/mermaid-mindmap/dist/mermaid-mindmap.js');
|
||||
// await loadDiagram(path + 'mermaid-' + type + '.js');
|
||||
// new diagram will try getDiagram again and if fails then it is a valid throw
|
||||
// TODO: Uncomment for v10
|
||||
// // Diagram not available, loading it
|
||||
// const { diagram } = await loader();
|
||||
// registerDiagram(type, diagram, undefined, diagram.injectUtils);
|
||||
// // new diagram will try getDiagram again and if fails then it is a valid throw
|
||||
return loader().then(({ diagram }) => {
|
||||
registerDiagram(type, diagram, undefined, diagram.injectUtils);
|
||||
return new Diagram(txt, parseError);
|
||||
});
|
||||
}
|
||||
// If either of the above worked, we have the diagram
|
||||
// logic and can continue
|
||||
return new Diagram(txt, parseError);
|
||||
// return new Diagram(txt, parseError);
|
||||
};
|
||||
|
||||
export default Diagram;
|
||||
|
@@ -11,17 +11,13 @@ import Diagram from '../Diagram';
|
||||
// Normally, we could just do the following to get the original `parse()`
|
||||
// implementation, however, requireActual returns a promise and it's not documented how to use withing mock file.
|
||||
|
||||
let hasLoadedDiagrams = false;
|
||||
/**
|
||||
* @param text
|
||||
* @param parseError
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function parse(text: string, parseError?: Function): boolean {
|
||||
if (!hasLoadedDiagrams) {
|
||||
addDiagrams();
|
||||
hasLoadedDiagrams = true;
|
||||
}
|
||||
addDiagrams();
|
||||
const diagram = new Diagram(text, parseError);
|
||||
return diagram.parse(text, parseError);
|
||||
}
|
||||
|
@@ -3,7 +3,8 @@
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export interface MermaidConfig {
|
||||
lazyLoadedDiagrams?: any;
|
||||
lazyLoadedDiagrams?: string[];
|
||||
loadExternalDiagramsAtStartup?: boolean;
|
||||
theme?: string;
|
||||
themeVariables?: any;
|
||||
themeCSS?: string;
|
||||
|
@@ -438,6 +438,9 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
|
||||
case 'thick':
|
||||
strokeClasses = 'edge-thickness-thick';
|
||||
break;
|
||||
case 'invisible':
|
||||
strokeClasses = 'edge-thickness-thick';
|
||||
break;
|
||||
default:
|
||||
strokeClasses = '';
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import { MermaidConfig } from '../config.type';
|
||||
import { log } from '../logger';
|
||||
import { DetectorRecord, DiagramDetector, DiagramLoader } from './types';
|
||||
|
||||
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
|
||||
export type DiagramLoader = (() => any) | null;
|
||||
export type DetectorRecord = { detector: DiagramDetector; loader: DiagramLoader };
|
||||
const directive =
|
||||
/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi;
|
||||
const anyComment = /\s*%%.*\n/gm;
|
||||
@@ -34,26 +33,22 @@ const detectors: Record<string, DetectorRecord> = {};
|
||||
*/
|
||||
export const detectType = function (text: string, config?: MermaidConfig): string {
|
||||
text = text.replace(directive, '').replace(anyComment, '\n');
|
||||
|
||||
// console.log(detectors);
|
||||
|
||||
for (const [key, { detector }] of Object.entries(detectors)) {
|
||||
const diagram = detector(text, config);
|
||||
if (diagram) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
// TODO: #3391
|
||||
// throw new Error(`No diagram type detected for text: ${text}`);
|
||||
return 'flowchart';
|
||||
|
||||
throw new Error(`No diagram type detected for text: ${text}`);
|
||||
};
|
||||
|
||||
export const addDetector = (
|
||||
key: string,
|
||||
detector: DiagramDetector,
|
||||
loader: DiagramLoader | null
|
||||
) => {
|
||||
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
|
||||
if (detectors[key]) {
|
||||
throw new Error(`Detector with key ${key} already exists`);
|
||||
}
|
||||
detectors[key] = { detector, loader };
|
||||
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
|
||||
};
|
||||
|
||||
export const getDiagramLoader = (key: string) => detectors[key].loader;
|
||||
|
@@ -1,16 +1,4 @@
|
||||
import {
|
||||
registerDiagram,
|
||||
registerDetector,
|
||||
DiagramDefinition,
|
||||
DiagramDetector,
|
||||
} from './diagramAPI';
|
||||
|
||||
// // @ts-ignore: TODO Fix ts errors
|
||||
// import mindmapParser from '../diagrams/mindmap/parser/mindmap';
|
||||
// import * as mindmapDb from '../diagrams/mindmap/mindmapDb';
|
||||
// import { mindmapDetector } from '../diagrams/mindmap/mindmapDetector';
|
||||
// import mindmapRenderer from '../diagrams/mindmap/mindmapRenderer';
|
||||
// import mindmapStyles from '../diagrams/mindmap/styles';
|
||||
import { registerDiagram } from './diagramAPI';
|
||||
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
import gitGraphParser from '../diagrams/git/parser/gitGraph';
|
||||
@@ -106,17 +94,15 @@ import { setConfig } from '../config';
|
||||
import errorRenderer from '../diagrams/error/errorRenderer';
|
||||
import errorStyles from '../diagrams/error/styles';
|
||||
|
||||
const registerDiagramAndDetector = (
|
||||
id: string,
|
||||
diagram: DiagramDefinition,
|
||||
detector: DiagramDetector
|
||||
) => {
|
||||
registerDiagram(id, diagram);
|
||||
registerDetector(id, detector);
|
||||
};
|
||||
|
||||
let hasLoadedDiagrams = false;
|
||||
export const addDiagrams = () => {
|
||||
registerDiagramAndDetector(
|
||||
if (hasLoadedDiagrams) {
|
||||
return;
|
||||
}
|
||||
// This is added here to avoid race-conditions.
|
||||
// We could optimize the loading logic somehow.
|
||||
hasLoadedDiagrams = true;
|
||||
registerDiagram(
|
||||
'error',
|
||||
// Special diagram with error messages but setup as a regular diagram
|
||||
{
|
||||
@@ -140,7 +126,7 @@ export const addDiagrams = () => {
|
||||
(text) => text.toLowerCase().trim() === 'error'
|
||||
);
|
||||
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'c4',
|
||||
{
|
||||
parser: c4Parser,
|
||||
@@ -153,7 +139,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
c4Detector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'class',
|
||||
{
|
||||
parser: classParser,
|
||||
@@ -170,7 +156,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
classDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'classDiagram',
|
||||
{
|
||||
parser: classParser,
|
||||
@@ -187,7 +173,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
classDetectorV2
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'er',
|
||||
{
|
||||
parser: erParser,
|
||||
@@ -197,7 +183,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
erDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'gantt',
|
||||
{
|
||||
parser: ganttParser,
|
||||
@@ -207,7 +193,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
ganttDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'info',
|
||||
{
|
||||
parser: infoParser,
|
||||
@@ -217,7 +203,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
infoDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'pie',
|
||||
{
|
||||
parser: pieParser,
|
||||
@@ -227,7 +213,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
pieDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'requirement',
|
||||
{
|
||||
parser: requirementParser,
|
||||
@@ -237,7 +223,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
requirementDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'sequence',
|
||||
{
|
||||
parser: sequenceParser,
|
||||
@@ -260,7 +246,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
sequenceDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'state',
|
||||
{
|
||||
parser: stateParser,
|
||||
@@ -277,7 +263,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
stateDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'stateDiagram',
|
||||
{
|
||||
parser: stateParser,
|
||||
@@ -294,7 +280,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
stateDetectorV2
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'journey',
|
||||
{
|
||||
parser: journeyParser,
|
||||
@@ -309,7 +295,7 @@ export const addDiagrams = () => {
|
||||
journeyDetector
|
||||
);
|
||||
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'flowchart',
|
||||
{
|
||||
parser: flowParser,
|
||||
@@ -329,7 +315,7 @@ export const addDiagrams = () => {
|
||||
},
|
||||
flowDetector
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'flowchart-v2',
|
||||
{
|
||||
parser: flowParser,
|
||||
@@ -350,14 +336,9 @@ export const addDiagrams = () => {
|
||||
},
|
||||
flowDetectorV2
|
||||
);
|
||||
registerDiagramAndDetector(
|
||||
registerDiagram(
|
||||
'gitGraph',
|
||||
{ parser: gitGraphParser, db: gitGraphDb, renderer: gitGraphRenderer, styles: gitGraphStyles },
|
||||
gitGraphDetector
|
||||
);
|
||||
// registerDiagram(
|
||||
// 'mindmap',
|
||||
// { parser: mindmapParser, db: mindmapDb, renderer: mindmapRenderer, styles: mindmapStyles },
|
||||
// mindmapDetector
|
||||
// );
|
||||
};
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { detectType, DiagramDetector } from './detectType';
|
||||
import { getDiagram, registerDiagram, registerDetector } from './diagramAPI';
|
||||
import { detectType } from './detectType';
|
||||
import { getDiagram, registerDiagram } from './diagramAPI';
|
||||
import { addDiagrams } from './diagram-orchestration';
|
||||
import { DiagramDetector } from './types';
|
||||
|
||||
addDiagrams();
|
||||
|
||||
@@ -15,17 +16,22 @@ describe('DiagramAPI', () => {
|
||||
|
||||
it('should handle diagram registrations', () => {
|
||||
expect(() => getDiagram('loki')).toThrow();
|
||||
expect(() => detectType('loki diagram')).not.toThrow(); // TODO: #3391
|
||||
expect(() => detectType('loki diagram')).toThrow(
|
||||
'No diagram type detected for text: loki diagram'
|
||||
);
|
||||
const detector: DiagramDetector = (str: string) => {
|
||||
return str.match('loki') !== null;
|
||||
};
|
||||
registerDetector('loki', detector);
|
||||
registerDiagram('loki', {
|
||||
db: {},
|
||||
parser: {},
|
||||
renderer: {},
|
||||
styles: {},
|
||||
});
|
||||
registerDiagram(
|
||||
'loki',
|
||||
{
|
||||
db: {},
|
||||
parser: {},
|
||||
renderer: {},
|
||||
styles: {},
|
||||
},
|
||||
detector
|
||||
);
|
||||
expect(getDiagram('loki')).not.toBeNull();
|
||||
expect(detectType('loki diagram')).toBe('loki');
|
||||
});
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { addDetector, DiagramDetector as _DiagramDetector } from './detectType';
|
||||
import { addDetector } from './detectType';
|
||||
import { log as _log, setLogLevel as _setLogLevel } from '../logger';
|
||||
import { getConfig as _getConfig } from '../config';
|
||||
import { sanitizeText as _sanitizeText } from '../diagrams/common/common';
|
||||
import { MermaidConfig } from '../config.type';
|
||||
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox';
|
||||
import { addStylesForDiagram } from '../styles';
|
||||
import { DiagramDefinition, DiagramDetector } from './types';
|
||||
|
||||
/*
|
||||
Packaging and exposing resources for externa diagrams so that they can import
|
||||
@@ -13,41 +13,19 @@ import { addStylesForDiagram } from '../styles';
|
||||
*/
|
||||
export const log = _log;
|
||||
export const setLogLevel = _setLogLevel;
|
||||
export type DiagramDetector = _DiagramDetector;
|
||||
export const getConfig = _getConfig;
|
||||
export const sanitizeText = (text: string) => _sanitizeText(text, getConfig());
|
||||
export const setupGraphViewbox = _setupGraphViewbox;
|
||||
|
||||
export interface InjectUtils {
|
||||
_log: any;
|
||||
_setLogLevel: any;
|
||||
_getConfig: any;
|
||||
_sanitizeText: any;
|
||||
_setupGraphViewbox: any;
|
||||
}
|
||||
|
||||
export interface DiagramDefinition {
|
||||
db: any;
|
||||
renderer: any;
|
||||
parser: any;
|
||||
styles: any;
|
||||
init?: (config: MermaidConfig) => void;
|
||||
injectUtils?: (utils: InjectUtils) => void;
|
||||
}
|
||||
|
||||
const diagrams: Record<string, DiagramDefinition> = {};
|
||||
const connectCallbacks: Record<string, any> = {}; // TODO fix, eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
export interface Detectors {
|
||||
[key: string]: DiagramDetector;
|
||||
}
|
||||
|
||||
export const registerDetector = (id: string, detector: DiagramDetector) => {
|
||||
addDetector(id, detector, null);
|
||||
};
|
||||
|
||||
export const registerDiagram = (
|
||||
id: string,
|
||||
diagram: DiagramDefinition,
|
||||
detector?: DiagramDetector,
|
||||
callback?: (
|
||||
_log: any,
|
||||
_setLogLevel: any,
|
||||
@@ -56,35 +34,36 @@ export const registerDiagram = (
|
||||
_setupGraphViewbox: any
|
||||
) => void
|
||||
) => {
|
||||
log.debug(`Registering diagram ${id}`);
|
||||
if (diagrams[id]) {
|
||||
log.warn(`Diagram ${id} already registered.`);
|
||||
// The error throw is commented out to as it breaks pages where you have multiple diagrams,
|
||||
// it can happen that rendering of the same type of diagram is initiated while the previous
|
||||
// one is still being imported. import deals with this and only one diagram is imported in
|
||||
// the end.
|
||||
// throw new Error(`Diagram ${id} already registered.`);
|
||||
}
|
||||
diagrams[id] = diagram;
|
||||
if (detector) {
|
||||
addDetector(id, detector);
|
||||
}
|
||||
addStylesForDiagram(id, diagram.styles);
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);
|
||||
}
|
||||
log.debug(`Registered diagram ${id}. ${Object.keys(diagrams).join(', ')} diagrams registered.`);
|
||||
};
|
||||
|
||||
export const getDiagram = (name: string): DiagramDefinition => {
|
||||
log.debug(`Getting diagram ${name}. ${Object.keys(diagrams).join(', ')} diagrams registered.`);
|
||||
if (name in diagrams) {
|
||||
return diagrams[name];
|
||||
}
|
||||
throw new Error(`Diagram ${name} not found.`);
|
||||
throw new DiagramNotFoundError(name);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sScriptSrc
|
||||
*/
|
||||
export const loadDiagram = (sScriptSrc: string) =>
|
||||
new Promise((resolve) => {
|
||||
const oHead = document.getElementsByTagName('HEAD')[0];
|
||||
const oScript = document.createElement('script');
|
||||
oScript.type = 'text/javascript';
|
||||
oScript.src = sScriptSrc;
|
||||
oHead.appendChild(oScript);
|
||||
oScript.onload = () => {
|
||||
resolve(true);
|
||||
};
|
||||
});
|
||||
export class DiagramNotFoundError extends Error {
|
||||
constructor(message: string) {
|
||||
super(`Diagram ${message} not found.`);
|
||||
}
|
||||
}
|
||||
|
@@ -1,227 +0,0 @@
|
||||
export const lineBreakRegex = /<br\s*\/?>/gi;
|
||||
|
||||
/**
|
||||
* Caches results of functions based on input
|
||||
*
|
||||
* @param {Function} fn Function to run
|
||||
* @param {Function} resolver Function that resolves to an ID given arguments the `fn` takes
|
||||
* @returns {Function} An optimized caching function
|
||||
*/
|
||||
const memoize = (fn, resolver) => {
|
||||
let cache = {};
|
||||
return (...args) => {
|
||||
let n = resolver ? resolver.apply(this, args) : args[0];
|
||||
if (n in cache) {
|
||||
return cache[n];
|
||||
} else {
|
||||
let result = fn(...args);
|
||||
cache[n] = result;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* This calculates the width of the given text, font size and family.
|
||||
*
|
||||
* @param {any} text - The text to calculate the width of
|
||||
* @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the resulting size
|
||||
* @returns {any} - The width for the given text
|
||||
*/
|
||||
export const calculateTextWidth = function (text, config) {
|
||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
||||
return calculateTextDimensions(text, config).width;
|
||||
};
|
||||
|
||||
export const getTextObj = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: undefined,
|
||||
anchor: 'start',
|
||||
style: '#666',
|
||||
width: 100,
|
||||
height: 100,
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
valign: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds text to an element
|
||||
*
|
||||
* @param {SVGElement} elem Element to add text to
|
||||
* @param {{
|
||||
* text: string;
|
||||
* x: number;
|
||||
* y: number;
|
||||
* anchor: 'start' | 'middle' | 'end';
|
||||
* fontFamily: string;
|
||||
* fontSize: string | number;
|
||||
* fontWeight: string | number;
|
||||
* fill: string;
|
||||
* class: string | undefined;
|
||||
* textMargin: number;
|
||||
* }} textData
|
||||
* @returns {SVGTextElement} Text element with given styling and content
|
||||
*/
|
||||
export const drawSimpleText = function (elem, textData) {
|
||||
// Remove and ignore br:s
|
||||
const nText = textData.text.replace(lineBreakRegex, ' ');
|
||||
|
||||
const textElem = elem.append('text');
|
||||
textElem.attr('x', textData.x);
|
||||
textElem.attr('y', textData.y);
|
||||
textElem.style('text-anchor', textData.anchor);
|
||||
textElem.style('font-family', textData.fontFamily);
|
||||
textElem.style('font-size', textData.fontSize);
|
||||
textElem.style('font-weight', textData.fontWeight);
|
||||
textElem.attr('fill', textData.fill);
|
||||
if (typeof textData.class !== 'undefined') {
|
||||
textElem.attr('class', textData.class);
|
||||
}
|
||||
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x + textData.textMargin * 2);
|
||||
span.attr('fill', textData.fill);
|
||||
span.text(nText);
|
||||
|
||||
return textElem;
|
||||
};
|
||||
|
||||
/**
|
||||
* This calculates the dimensions of the given text, font size, font family, font weight, and margins.
|
||||
*
|
||||
* @param {any} text - The text to calculate the width of
|
||||
* @param {any} config - The config for fontSize, fontFamily, fontWeight, and margin all impacting
|
||||
* the resulting size
|
||||
* @returns - The width for the given text
|
||||
*/
|
||||
export const calculateTextDimensions = memoize(
|
||||
function (text, config) {
|
||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
||||
const { fontSize, fontFamily, fontWeight } = config;
|
||||
if (!text) {
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
|
||||
// We can't really know if the user supplied font family will render on the user agent;
|
||||
// thus, we'll take the max width between the user supplied font family, and a default
|
||||
// of sans-serif.
|
||||
const fontFamilies = ['sans-serif', fontFamily];
|
||||
const lines = text.split(common.lineBreakRegex);
|
||||
let dims = [];
|
||||
|
||||
const body = select('body');
|
||||
// We don't want to leak DOM elements - if a removal operation isn't available
|
||||
// for any reason, do not continue.
|
||||
if (!body.remove) {
|
||||
return { width: 0, height: 0, lineHeight: 0 };
|
||||
}
|
||||
|
||||
const g = body.append('svg');
|
||||
|
||||
for (let fontFamily of fontFamilies) {
|
||||
let cheight = 0;
|
||||
let dim = { width: 0, height: 0, lineHeight: 0 };
|
||||
for (let line of lines) {
|
||||
const textObj = getTextObj();
|
||||
textObj.text = line;
|
||||
const textElem = drawSimpleText(g, textObj)
|
||||
.style('font-size', fontSize)
|
||||
.style('font-weight', fontWeight)
|
||||
.style('font-family', fontFamily);
|
||||
|
||||
let bBox = (textElem._groups || textElem)[0][0].getBBox();
|
||||
dim.width = Math.round(Math.max(dim.width, bBox.width));
|
||||
cheight = Math.round(bBox.height);
|
||||
dim.height += cheight;
|
||||
dim.lineHeight = Math.round(Math.max(dim.lineHeight, cheight));
|
||||
}
|
||||
dims.push(dim);
|
||||
}
|
||||
|
||||
g.remove();
|
||||
|
||||
let index =
|
||||
isNaN(dims[1].height) ||
|
||||
isNaN(dims[1].width) ||
|
||||
isNaN(dims[1].lineHeight) ||
|
||||
(dims[0].height > dims[1].height &&
|
||||
dims[0].width > dims[1].width &&
|
||||
dims[0].lineHeight > dims[1].lineHeight)
|
||||
? 0
|
||||
: 1;
|
||||
return dims[index];
|
||||
},
|
||||
(text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||
);
|
||||
|
||||
const breakString = memoize(
|
||||
(word, maxWidth, hyphenCharacter = '-', config) => {
|
||||
config = Object.assign(
|
||||
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0 },
|
||||
config
|
||||
);
|
||||
const characters = word.split('');
|
||||
const lines = [];
|
||||
let currentLine = '';
|
||||
characters.forEach((character, index) => {
|
||||
const nextLine = `${currentLine}${character}`;
|
||||
const lineWidth = calculateTextWidth(nextLine, config);
|
||||
if (lineWidth >= maxWidth) {
|
||||
const currentCharacter = index + 1;
|
||||
const isLastLine = characters.length === currentCharacter;
|
||||
const hyphenatedNextLine = `${nextLine}${hyphenCharacter}`;
|
||||
lines.push(isLastLine ? nextLine : hyphenatedNextLine);
|
||||
currentLine = '';
|
||||
} else {
|
||||
currentLine = nextLine;
|
||||
}
|
||||
});
|
||||
return { hyphenatedStrings: lines, remainingWord: currentLine };
|
||||
},
|
||||
(word, maxWidth, hyphenCharacter = '-', config) =>
|
||||
`${word}-${maxWidth}-${hyphenCharacter}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||
);
|
||||
|
||||
export const wrapLabel = memoize(
|
||||
(label, maxWidth, config) => {
|
||||
if (!label) {
|
||||
return label;
|
||||
}
|
||||
config = Object.assign(
|
||||
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '<br/>' },
|
||||
config
|
||||
);
|
||||
if (lineBreakRegex.test(label)) {
|
||||
return label;
|
||||
}
|
||||
const words = label.split(' ');
|
||||
const completedLines = [];
|
||||
let nextLine = '';
|
||||
words.forEach((word, index) => {
|
||||
const wordLength = calculateTextWidth(`${word} `, config);
|
||||
const nextLineLength = calculateTextWidth(nextLine, config);
|
||||
if (wordLength > maxWidth) {
|
||||
const { hyphenatedStrings, remainingWord } = breakString(word, maxWidth, '-', config);
|
||||
completedLines.push(nextLine, ...hyphenatedStrings);
|
||||
nextLine = remainingWord;
|
||||
} else if (nextLineLength + wordLength >= maxWidth) {
|
||||
completedLines.push(nextLine);
|
||||
nextLine = word;
|
||||
} else {
|
||||
nextLine = [nextLine, word].filter(Boolean).join(' ');
|
||||
}
|
||||
const currentWord = index + 1;
|
||||
const isLastWord = currentWord === words.length;
|
||||
if (isLastWord) {
|
||||
completedLines.push(nextLine);
|
||||
}
|
||||
});
|
||||
return completedLines.filter((line) => line !== '').join(config.joinWith);
|
||||
},
|
||||
(label, maxWidth, config) =>
|
||||
`${label}-${maxWidth}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}-${config.joinWith}`
|
||||
);
|
26
packages/mermaid/src/diagram-api/types.ts
Normal file
26
packages/mermaid/src/diagram-api/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { MermaidConfig } from '../config.type';
|
||||
|
||||
export interface InjectUtils {
|
||||
_log: any;
|
||||
_setLogLevel: any;
|
||||
_getConfig: any;
|
||||
_sanitizeText: any;
|
||||
_setupGraphViewbox: any;
|
||||
}
|
||||
|
||||
export interface DiagramDefinition {
|
||||
db: any;
|
||||
renderer: any;
|
||||
parser: any;
|
||||
styles: any;
|
||||
init?: (config: MermaidConfig) => void;
|
||||
injectUtils?: (utils: InjectUtils) => void;
|
||||
}
|
||||
|
||||
export interface DetectorRecord {
|
||||
detector: DiagramDetector;
|
||||
loader?: DiagramLoader;
|
||||
}
|
||||
|
||||
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
|
||||
export type DiagramLoader = (() => Promise<{ id: string; diagram: DiagramDefinition }>) | null;
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const c4Detector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*C4Context|C4Container|C4Component|C4Dynamic|C4Deployment/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const classDetectorV2: DiagramDetector = (txt, config) => {
|
||||
// If we have confgured to use dagre-wrapper then we should return true in this function for classDiagram code thus making it use the new class diagram
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const classDetector: DiagramDetector = (txt, config) => {
|
||||
// If we have confgured to use dagre-wrapper then we should never return true in this function
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const erDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*erDiagram/) !== null;
|
||||
|
@@ -429,8 +429,7 @@ export const clear = function (ver = 'gen-1') {
|
||||
vertices = {};
|
||||
classes = {};
|
||||
edges = [];
|
||||
funs = [];
|
||||
funs.push(setupToolTips);
|
||||
funs = [setupToolTips];
|
||||
subGraphs = [];
|
||||
subGraphLookup = {};
|
||||
subCount = 0;
|
||||
@@ -457,8 +456,8 @@ export const defaultStyle = function () {
|
||||
export const addSubGraph = function (_id, list, _title) {
|
||||
// console.log('addSubGraph', _id, list, _title);
|
||||
let id = _id.trim();
|
||||
let title = _title;
|
||||
if (_id === _title && _title.match(/\s/)) {
|
||||
let title = _title.trim();
|
||||
if (id === title && title.match(/\s/)) {
|
||||
id = undefined;
|
||||
}
|
||||
/** @param a */
|
||||
@@ -675,6 +674,10 @@ const destructEndLink = (_str) => {
|
||||
stroke = 'thick';
|
||||
}
|
||||
|
||||
if (line[0] === '~') {
|
||||
stroke = 'invisible';
|
||||
}
|
||||
|
||||
let dots = countChar('.', line);
|
||||
|
||||
if (dots) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const flowDetectorV2: DiagramDetector = (txt, config) => {
|
||||
// If we have confgured to use dagre-wrapper then we should return true in this function for graph code thus making it use the new flowchart diagram
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const flowDetector: DiagramDetector = (txt, config) => {
|
||||
// If we have confired to only use new flow charts this function shohuld always return false
|
||||
|
@@ -280,6 +280,11 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.pattern = 'solid';
|
||||
edgeData.style = 'stroke-width: 3.5px;fill:none;';
|
||||
break;
|
||||
case 'invisible':
|
||||
edgeData.thickness = 'invisible';
|
||||
edgeData.pattern = 'solid';
|
||||
edgeData.style = 'stroke-width: 0;fill:none;';
|
||||
break;
|
||||
}
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
const styles = getStylesFromArray(edge.style);
|
||||
|
@@ -120,6 +120,7 @@ that id.
|
||||
\s*[xo<]?\-\-+[-xo>]\s* return 'LINK';
|
||||
\s*[xo<]?\=\=+[=xo>]\s* return 'LINK';
|
||||
\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK';
|
||||
\s*\~\~[\~]+\s* return 'LINK';
|
||||
\s*[xo<]?\-\-\s* return 'START_LINK';
|
||||
\s*[xo<]?\=\=\s* return 'START_LINK';
|
||||
\s*[xo<]?\-\.\s* return 'START_LINK';
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const ganttDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*gantt/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const gitGraphDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*gitGraph/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const infoDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*info/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const pieDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*pie/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const requirementDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*requirement(Diagram)?/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const sequenceDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*sequenceDiagram/) !== null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const stateDetectorV2: DiagramDetector = (text, config) => {
|
||||
if (text.match(/^\s*stateDiagram-v2/) !== null) return true;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const stateDetector: DiagramDetector = (txt, config) => {
|
||||
// If we have confired to only use new state diagrams this function should always return false
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { DiagramDetector } from '../../diagram-api/detectType';
|
||||
import type { DiagramDetector } from '../../diagram-api/types';
|
||||
|
||||
export const journeyDetector: DiagramDetector = (txt) => {
|
||||
return txt.match(/^\s*journey/) !== null;
|
||||
|
@@ -41,6 +41,10 @@ import { remark } from 'remark';
|
||||
// @ts-ignore No typescript declaration file
|
||||
import flatmap from 'unist-util-flatmap';
|
||||
|
||||
const MERMAID_MAJOR_VERSION = (
|
||||
JSON.parse(readFileSync('packages/mermaid/package.json', 'utf8')).version as string
|
||||
).split('.')[0];
|
||||
|
||||
// These paths are from the root of the mono-repo, not from the
|
||||
// mermaid sub-directory
|
||||
const SOURCE_DOCS_DIR = 'packages/mermaid/src/docs';
|
||||
@@ -144,7 +148,7 @@ const readSyncedUTF8file = (filename: string): string => {
|
||||
* @param file {string} name of the file that will be verified
|
||||
*/
|
||||
const transformMarkdown = (file: string) => {
|
||||
const doc = readSyncedUTF8file(file);
|
||||
const doc = readSyncedUTF8file(file).replace(/<MERMAID_VERSION>/g, MERMAID_MAJOR_VERSION);
|
||||
const ast: Root = remark.parse(doc);
|
||||
const out = flatmap(ast, (c: Code) => {
|
||||
if (c.type !== 'code') {
|
||||
@@ -185,7 +189,10 @@ const transformHtml = (filename: string) => {
|
||||
* @returns {string} The contents of the file with the comment inserted
|
||||
*/
|
||||
const insertAutoGeneratedComment = (fileName: string): string => {
|
||||
const fileContents = readSyncedUTF8file(fileName);
|
||||
const fileContents = readSyncedUTF8file(fileName).replace(
|
||||
/<MERMAID_VERSION>/g,
|
||||
MERMAID_MAJOR_VERSION
|
||||
);
|
||||
const jsdom = new JSDOM(fileContents);
|
||||
const htmlDoc = jsdom.window.document;
|
||||
const autoGeneratedComment = jsdom.window.document.createComment(AUTOGENERATED_TEXT);
|
||||
|
@@ -8,7 +8,7 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
|
||||
|
||||
<img src="img/header.png" alt="" />
|
||||
|
||||
[](https://travis-ci.org/mermaid-js/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
<!-- Mermaid book banner -->
|
||||
|
||||
@@ -187,16 +187,16 @@ To Deploy Mermaid:
|
||||
|
||||
### [Mermaid API](./Setup.md):
|
||||
|
||||
**To deploy mermaid without a bundler, one can insert a `script` tag with an absolute address and a `mermaidAPI` call into the HTML like so:**
|
||||
**To deploy mermaid without a bundler, one can insert a `script` tag with an absolute address and a `mermaid.initialize` call into the HTML like so:**
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
<script type="module">
|
||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@<MERMAID_VERSION>/dist/mermaid.esm.min.mjs';
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
```
|
||||
|
||||
**Doing so will command the mermaid parser to look for the `<div>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into SVG charts.**
|
||||
**Doing so will command the mermaid parser to look for the `<div>` or `<pre>` tags with `class="mermaid"`. From these tags mermaid will try to read the diagram/chart definitions and render them into SVG charts.**
|
||||
|
||||
**Examples can be found at** [Other examples](/examples)
|
||||
|
||||
@@ -263,7 +263,7 @@ Update version number in `package.json`.
|
||||
npm publish
|
||||
```
|
||||
|
||||
The above command generates files into the `dist` folder and publishes them to npmjs.org.
|
||||
The above command generates files into the `dist` folder and publishes them to <npmjs.org>.
|
||||
|
||||
## Related projects
|
||||
|
||||
@@ -279,7 +279,7 @@ Detailed information about how to contribute can be found in the [contribution g
|
||||
|
||||
## Security and safe diagrams
|
||||
|
||||
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitise the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
|
||||
For public sites, it can be precarious to retrieve text from users on the internet, storing that content for presentation in a browser at a later stage. The reason is that the user content can contain embedded malicious scripts that will run when the data is presented. For Mermaid this is a risk, specially as mermaid diagrams contain many characters that are used in html which makes the standard sanitation unusable as it also breaks the diagrams. We still make an effort to sanitize the incoming code and keep refining the process but it is hard to guarantee that there are no loop holes.
|
||||
|
||||
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing JavaScript in the code from being executed. This is a great step forward for better security.
|
||||
|
||||
|
@@ -72,15 +72,15 @@ Theme , the CSS style sheet
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| ------------- | --------------------------------- | ------ | -------- | ------------------------------------------ |
|
||||
| securityLevel | Level of trust for parsed diagram | string | Required | 'sandbox', 'strict', 'loose', 'antiscript' |
|
||||
| securityLevel | Level of trust for parsed diagram | string | Required | `sandbox`, `strict`, `loose`, `antiscript` |
|
||||
|
||||
**Notes**:
|
||||
|
||||
- **strict**: (**default**) tags in text are encoded, click functionality is disabled
|
||||
- **loose**: tags in text are allowed, click functionality is enabled
|
||||
- **antiscript**: html tags in text are allowed, (only script element is removed), click
|
||||
- **`strict`**: (**default**) tags in text are encoded, click functionality is disabled
|
||||
- **`loose`**: tags in text are allowed, click functionality is enabled
|
||||
- **`antiscript`**: html tags in text are allowed, (only script element is removed), click
|
||||
functionality is enabled
|
||||
- **sandbox**: With this security level all rendering takes place in a sandboxed iframe. This
|
||||
- **`sandbox`**: With this security level all rendering takes place in a sandboxed iframe. This
|
||||
prevent any JavaScript from running in the context. This may hinder interactive functionality
|
||||
of the diagram like scripts, popups in sequence diagram or links to other tabs/targets etc.
|
||||
|
||||
@@ -119,11 +119,11 @@ Default value: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize']
|
||||
|
||||
This option controls if the generated ids of nodes in the SVG are generated randomly or based
|
||||
on a seed. If set to false, the IDs are generated based on the current date and thus are not
|
||||
deterministic. This is the default behaviour.
|
||||
deterministic. This is the default behavior.
|
||||
|
||||
**Notes**:
|
||||
|
||||
This matters if your files are checked into sourcecontrol e.g. git and should not change unless
|
||||
This matters if your files are checked into source control e.g. git and should not change unless
|
||||
content is changed.
|
||||
|
||||
Default value: false
|
||||
@@ -210,16 +210,16 @@ Default value: true
|
||||
|
||||
### defaultRenderer
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | --------------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | `dagre-d3`, `dagre-wrapper` |
|
||||
|
||||
**Notes:**
|
||||
|
||||
Decides which rendering engine that is to be used for the rendering. Legal values are:
|
||||
dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid
|
||||
`dagre-d3` `dagre-wrapper` - wrapper for `dagre` implemented in mermaid
|
||||
|
||||
Default value: 'dagre-wrapper'
|
||||
Default value: `dagre-wrapper`
|
||||
|
||||
## sequence
|
||||
|
||||
@@ -735,16 +735,16 @@ Default value: true
|
||||
|
||||
## defaultRenderer
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | --------------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | `dagre-d3`, `dagre-wrapper` |
|
||||
|
||||
**Notes**:
|
||||
|
||||
Decides which rendering engine that is to be used for the rendering. Legal values are:
|
||||
dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid
|
||||
`dagre-d3` `dagre-wrapper` - wrapper for `dagre` implemented in mermaid
|
||||
|
||||
Default value: 'dagre-d3'
|
||||
Default value: `dagre-d3`
|
||||
|
||||
## useMaxWidth
|
||||
|
||||
@@ -761,16 +761,16 @@ Default value: true
|
||||
|
||||
## defaultRenderer
|
||||
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper |
|
||||
| Parameter | Description | Type | Required | Values |
|
||||
| --------------- | ----------- | ------- | -------- | --------------------------- |
|
||||
| defaultRenderer | See notes | boolean | 4 | `dagre-d3`, `dagre-wrapper` |
|
||||
|
||||
**Notes:**
|
||||
|
||||
Decides which rendering engine that is to be used for the rendering. Legal values are:
|
||||
dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid
|
||||
`dagre-d3` `dagre-wrapper` - wrapper for `dagre` implemented in mermaid
|
||||
|
||||
Default value: 'dagre-d3'
|
||||
Default value: `dagre-d3`
|
||||
|
||||
## er
|
||||
|
||||
@@ -992,7 +992,7 @@ Default value: 4
|
||||
| --------------- | ----------- | ------- | -------- | ------------------ |
|
||||
| c4BoundaryInRow | See Notes | Integer | Required | Any Positive Value |
|
||||
|
||||
**Notes:** How many boundarys to place in each row.
|
||||
**Notes:** How many boundaries to place in each row.
|
||||
|
||||
Default value: 2
|
||||
|
||||
@@ -1559,7 +1559,7 @@ Returns **void**
|
||||
|
||||
```html
|
||||
<script>
|
||||
var config = {
|
||||
const config = {
|
||||
theme: 'default',
|
||||
logLevel: 'fatal',
|
||||
securityLevel: 'strict',
|
||||
|
@@ -17,7 +17,7 @@ The diagram authors can now add the accessibility options in the diagram definit
|
||||
- `accTitle: "Your Accessibility Title"` or
|
||||
- `accDescr: "Your Accessibility Description"`
|
||||
|
||||
**When these two options are defined, they will add a coressponding `<title>` and `<desc>` tag in the SVG.**
|
||||
**When these two options are defined, they will add a corresponding `<title>` and `<desc>` tag in the SVG.**
|
||||
|
||||
Let us take a look at the following example with a flowchart diagram:
|
||||
|
||||
|
@@ -131,7 +131,7 @@ The following unfinished features are not supported in the short term.
|
||||
- - [x] Rel_Back
|
||||
- - [x] RelIndex \* Compatible with C4-Plantuml syntax, but ignores the index parameter. The sequence number is determined by the order in which the rel statements are written.
|
||||
|
||||
- [ ] Custom tags/stereotypes support and skinparam updates
|
||||
- [ ] Custom tags/stereotypes support and skin param updates
|
||||
- - [ ] AddElementTag(tagStereo, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new element tag. The styles of the tagged elements are updated and the tag is displayed in the calculated legend.
|
||||
- - [ ] AddRelTag(tagStereo, ?textColor, ?lineColor, ?lineStyle, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new Relationship tag. The styles of the tagged relationships are updated and the tag is displayed in the calculated legend.
|
||||
- - [x] UpdateElementStyle(elementName, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): This call updates the default style of the elements (component, ...) and creates no additional legend entry.
|
||||
|
@@ -399,7 +399,7 @@ click Shape2 call callbackFunction() "This is a tooltip for a callback"
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callbackFunction = function () {
|
||||
const callbackFunction = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
</script>
|
||||
@@ -451,10 +451,10 @@ Beginner's tip—a full example using interactive links in an HTML page:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
securityLevel: 'loose',
|
||||
};
|
||||
|
@@ -416,7 +416,7 @@ click nodeId call callback()
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callback = function (nodeId) {
|
||||
const callback = function (nodeId) {
|
||||
alert('A callback was triggered on ' + nodeId);
|
||||
};
|
||||
</script>
|
||||
@@ -471,10 +471,10 @@ Beginner's tip—here's a full example of using interactive links in HTML:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
flowchart: {
|
||||
useMaxWidth: true,
|
||||
|
@@ -167,6 +167,15 @@ flowchart LR
|
||||
A --- B
|
||||
```
|
||||
|
||||
### An invisisble link
|
||||
|
||||
This can be a usefull tool in some instances where you want to alter the default positining of a node.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A ~~~ B
|
||||
```
|
||||
|
||||
### Text on links
|
||||
|
||||
```mermaid-example
|
||||
@@ -442,7 +451,7 @@ Examples of tooltip usage below:
|
||||
|
||||
```html
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
</script>
|
||||
@@ -495,10 +504,10 @@ Beginner's tip—a full example using interactive links in a html context:
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
var callback = function () {
|
||||
const callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
var config = {
|
||||
const config = {
|
||||
startOnLoad: true,
|
||||
flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' },
|
||||
securityLevel: 'loose',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user