mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
Merge branch 'develop' into UpdateClassMemberHandling
This commit is contained in:
commit
f4ffd5c965
@ -5,3 +5,4 @@ cypress.config.js
|
|||||||
cypress/plugins/index.js
|
cypress/plugins/index.js
|
||||||
coverage
|
coverage
|
||||||
*.json
|
*.json
|
||||||
|
node_modules
|
||||||
|
26
.github/ISSUE_TEMPLATE/diagram_proposal.yml
vendored
26
.github/ISSUE_TEMPLATE/diagram_proposal.yml
vendored
@ -3,6 +3,7 @@ description: Suggest a new Diagram Type to add to Mermaid.
|
|||||||
labels:
|
labels:
|
||||||
- 'Status: Triage'
|
- 'Status: Triage'
|
||||||
- 'Type: Enhancement'
|
- 'Type: Enhancement'
|
||||||
|
- 'Type: New Diagram'
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
@ -17,6 +18,14 @@ body:
|
|||||||
- Use a clear and concise title
|
- Use a clear and concise title
|
||||||
- Fill out the text fields with as much detail as possible.
|
- Fill out the text fields with as much detail as possible.
|
||||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||||
|
|
||||||
|
## Example issues
|
||||||
|
|
||||||
|
Refer to the discussions here to get an idea of how the diagram syntax is created.
|
||||||
|
|
||||||
|
- https://github.com/mermaid-js/mermaid/issues/4269
|
||||||
|
- https://github.com/mermaid-js/mermaid/issues/4282
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Proposal
|
label: Proposal
|
||||||
@ -35,8 +44,17 @@ body:
|
|||||||
description: If applicable, add screenshots to show possible examples of how the diagram may look like.
|
description: If applicable, add screenshots to show possible examples of how the diagram may look like.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Code Sample
|
label: Syntax
|
||||||
description: |-
|
description: |-
|
||||||
If applicable, add a code sample for how to implement this new diagram.
|
If possible, include a syntax which could be used to write the diagram.
|
||||||
The text will automatically be rendered as JavaScript code.
|
Try to add one or two examples of valid use-cases here.
|
||||||
render: javascript
|
- type: dropdown
|
||||||
|
id: implementation
|
||||||
|
attributes:
|
||||||
|
label: Implementation
|
||||||
|
description: |-
|
||||||
|
Would you like to implement this yourself, or is it a proposal for the community?
|
||||||
|
If there is no corresponding PR from your side after 30 days, the diagram will be open for everyone to implement.
|
||||||
|
options:
|
||||||
|
- I will try and implement it myself.
|
||||||
|
- This is a proposal which I'd love to see built into mermaid by the wonderful community.
|
||||||
|
@ -44,6 +44,11 @@ const packageOptions = {
|
|||||||
packageName: 'mermaid-example-diagram',
|
packageName: 'mermaid-example-diagram',
|
||||||
file: 'detector.ts',
|
file: 'detector.ts',
|
||||||
},
|
},
|
||||||
|
'mermaid-zenuml': {
|
||||||
|
name: 'mermaid-zenuml',
|
||||||
|
packageName: 'mermaid-zenuml',
|
||||||
|
file: 'detector.ts',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
interface BuildOptions {
|
interface BuildOptions {
|
||||||
@ -146,6 +151,7 @@ if (watch) {
|
|||||||
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
||||||
if (!mermaidOnly) {
|
if (!mermaidOnly) {
|
||||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||||
|
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
||||||
}
|
}
|
||||||
} else if (visualize) {
|
} else if (visualize) {
|
||||||
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
|
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
|
||||||
|
@ -15,6 +15,7 @@ async function createServer() {
|
|||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.static('./packages/mermaid/dist'));
|
app.use(express.static('./packages/mermaid/dist'));
|
||||||
|
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
||||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||||
app.use(vite.middlewares);
|
app.use(vite.middlewares);
|
||||||
app.use(express.static('demos'));
|
app.use(express.static('demos'));
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"alois",
|
"alois",
|
||||||
"aloisklink",
|
"aloisklink",
|
||||||
"antiscript",
|
"antiscript",
|
||||||
|
"antlr",
|
||||||
"appli",
|
"appli",
|
||||||
"applitools",
|
"applitools",
|
||||||
"asciidoctor",
|
"asciidoctor",
|
||||||
@ -86,6 +87,7 @@
|
|||||||
"mkdocs",
|
"mkdocs",
|
||||||
"mmorel",
|
"mmorel",
|
||||||
"mult",
|
"mult",
|
||||||
|
"nextra",
|
||||||
"orlandoni",
|
"orlandoni",
|
||||||
"pathe",
|
"pathe",
|
||||||
"pbrolin",
|
"pbrolin",
|
||||||
@ -137,7 +139,8 @@
|
|||||||
"vitepress",
|
"vitepress",
|
||||||
"vueuse",
|
"vueuse",
|
||||||
"xlink",
|
"xlink",
|
||||||
"yash"
|
"yash",
|
||||||
|
"zenuml"
|
||||||
],
|
],
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{ "name": "Markdown links", "pattern": "\\((.*)\\)", "description": "" },
|
{ "name": "Markdown links", "pattern": "\\((.*)\\)", "description": "" },
|
||||||
|
@ -60,7 +60,7 @@ export const renderGraph = (graphStr, options, api) => {
|
|||||||
openURLAndVerifyRendering(url, options);
|
openURLAndVerifyRendering(url, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openURLAndVerifyRendering = (url, options, validation = undefined) => {
|
export const openURLAndVerifyRendering = (url, options, validation = undefined) => {
|
||||||
const useAppli = Cypress.env('useAppli');
|
const useAppli = Cypress.env('useAppli');
|
||||||
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { urlSnapshotTest } from '../../helpers/util.js';
|
import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.js';
|
||||||
|
|
||||||
describe('CSS injections', () => {
|
describe('CSS injections', () => {
|
||||||
it('should not allow CSS injections outside of the diagram', () => {
|
it('should not allow CSS injections outside of the diagram', () => {
|
||||||
@ -13,4 +13,11 @@ describe('CSS injections', () => {
|
|||||||
flowchart: { htmlLabels: false },
|
flowchart: { htmlLabels: false },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('should not allow manipulating styletags using arrowheads', () => {
|
||||||
|
openURLAndVerifyRendering('http://localhost:9000/xss23-css.html', {
|
||||||
|
logLevel: 1,
|
||||||
|
arrowMarkerAbsolute: false,
|
||||||
|
flowchart: { htmlLabels: true },
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -684,6 +684,20 @@ A --> B
|
|||||||
{ titleTopMargin: 0 }
|
{ titleTopMargin: 0 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('elk: should include classes on the edges', () => {
|
||||||
|
renderGraph(
|
||||||
|
`flowchart-elk TD
|
||||||
|
A --> B --> C --> D
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
cy.get('svg').should((svg) => {
|
||||||
|
const edges = svg.querySelectorAll('.edges > path');
|
||||||
|
edges.forEach((edge) => {
|
||||||
|
expect(edge).to.have.class('flowchart-link');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('Markdown strings flowchart-elk (#4220)', () => {
|
describe('Markdown strings flowchart-elk (#4220)', () => {
|
||||||
describe('html labels', () => {
|
describe('html labels', () => {
|
||||||
it('With styling and classes', () => {
|
it('With styling and classes', () => {
|
||||||
|
@ -52,6 +52,17 @@ root[A root with a long text that wraps to keep the node size in check]
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('a root with wrapping text and long words that exceed width', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`mindmap
|
||||||
|
root[A few smaller words but then averylongsetofcharacterswithoutwhitespacetoseparate that we expect to wrapontonextlinesandnotexceedwidthparameters]
|
||||||
|
`,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
shouldHaveRoot
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('a root with an icon', () => {
|
it('a root with an icon', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`mindmap
|
`mindmap
|
||||||
|
@ -88,6 +88,16 @@ context('Sequence diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('should handle empty lines', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
sequenceDiagram
|
||||||
|
Alice->>John: Hello John<br/>
|
||||||
|
John-->>Alice: Great<br/><br/>day!
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
it('should handle line breaks and wrap annotations', () => {
|
it('should handle line breaks and wrap annotations', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
|
19
cypress/integration/rendering/zenuml.spec.js
Normal file
19
cypress/integration/rendering/zenuml.spec.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { imgSnapshotTest } from '../../helpers/util.js';
|
||||||
|
|
||||||
|
describe('Zen UML', () => {
|
||||||
|
it('Basic Zen UML diagram', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
zenuml
|
||||||
|
A.method() {
|
||||||
|
if(x) {
|
||||||
|
B.method() {
|
||||||
|
selfCall() { return X }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -1,5 +1,6 @@
|
|||||||
import mermaid2 from './mermaid.esm.mjs';
|
import mermaid2 from './mermaid.esm.mjs';
|
||||||
import externalExample from '../../packages/mermaid-example-diagram/dist/mermaid-example-diagram.core.mjs';
|
import externalExample from '../../packages/mermaid-example-diagram/dist/mermaid-example-diagram.core.mjs';
|
||||||
|
import zenUml from '../../packages/mermaid-zenuml/dist/mermaid-zenuml.core.mjs';
|
||||||
|
|
||||||
function b64ToUtf8(str) {
|
function b64ToUtf8(str) {
|
||||||
return decodeURIComponent(escape(window.atob(str)));
|
return decodeURIComponent(escape(window.atob(str)));
|
||||||
@ -44,7 +45,7 @@ const contentLoaded = async function () {
|
|||||||
document.getElementsByTagName('body')[0].appendChild(div);
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
await mermaid2.registerExternalDiagrams([externalExample]);
|
await mermaid2.registerExternalDiagrams([externalExample, zenUml]);
|
||||||
mermaid2.initialize(graphObj.mermaid);
|
mermaid2.initialize(graphObj.mermaid);
|
||||||
await mermaid2.run();
|
await mermaid2.run();
|
||||||
}
|
}
|
||||||
|
85
cypress/platform/xss23-css.html
Normal file
85
cypress/platform/xss23-css.html
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
|
||||||
|
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
/* background: rgb(221, 208, 208); */
|
||||||
|
/* background:#333; */
|
||||||
|
font-family: 'Arial';
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
.mermaid2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.mermaid svg {
|
||||||
|
/* font-size: 18px !important; */
|
||||||
|
}
|
||||||
|
.malware {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 150px;
|
||||||
|
background: red;
|
||||||
|
color: black;
|
||||||
|
display: flex;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 72px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>Security check</div>
|
||||||
|
<div class="flex">
|
||||||
|
<div id="diagram" class="mermaid"></div>
|
||||||
|
<div id="graph-div"></div>
|
||||||
|
<div id="res" class=""></div>
|
||||||
|
</div>
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
mermaid.parseError = function (err, hash) {
|
||||||
|
// console.error('Mermaid error: ', err);
|
||||||
|
};
|
||||||
|
mermaid.initialize({
|
||||||
|
theme: 'base',
|
||||||
|
startOnLoad: false,
|
||||||
|
flowcharts: { htmlLabels: true },
|
||||||
|
});
|
||||||
|
function callback() {
|
||||||
|
alert('It worked');
|
||||||
|
}
|
||||||
|
function xssAttack() {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.id = 'the-malware';
|
||||||
|
div.className = 'malware';
|
||||||
|
div.innerHTML = 'XSS Succeeded';
|
||||||
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
|
throw new Error('XSS Succeeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
let diagram = `graph TD
|
||||||
|
A[["a marker-end=#quot;url(<s title='#<style>*{background:red}</style>'>b"]]
|
||||||
|
`;
|
||||||
|
const el = document.querySelector('#graph-div');
|
||||||
|
console.log(diagram);
|
||||||
|
const { svg } = await mermaid.render('graph-div', diagram);
|
||||||
|
document.querySelector('#res').innerHTML = svg;
|
||||||
|
window.rendered = true;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -66,6 +66,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<h2><a href="./state.html">State</a></h2>
|
<h2><a href="./state.html">State</a></h2>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<h2><a href="./zenuml.html">ZenUML</a></h2>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
53
demos/zenuml.html
Normal file
53
demos/zenuml.html
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>Mermaid Zenuml Test Page</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>Zenuml demos</h1>
|
||||||
|
<pre class="mermaid">
|
||||||
|
zenuml
|
||||||
|
title Sync Messages (Design Pattern: Adapter)
|
||||||
|
@Starter(Client)
|
||||||
|
Adapter.interfaceMethod() {
|
||||||
|
translateParameter(parameter)
|
||||||
|
|
||||||
|
result = Implementation.implementationMethod()
|
||||||
|
|
||||||
|
translateResult()
|
||||||
|
return translatedResult
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<pre class="mermaid">
|
||||||
|
zenuml
|
||||||
|
title Async Messages (SPA Authentication)
|
||||||
|
// ```
|
||||||
|
// GET https://${account.namespace}/authorize/?
|
||||||
|
// response_type=token
|
||||||
|
// &client_id=${account.clientId}
|
||||||
|
// &redirect_url=YOUR_CALLBACK_URL
|
||||||
|
// &state=VALUE_THAT_SURVIVES_REDIRECTS
|
||||||
|
// &scope=openid
|
||||||
|
// ```
|
||||||
|
Browser->Auth0: 1. initiate the authentication
|
||||||
|
Auth0->"Identity Provider": 2. OAuth2 / SAML, etc
|
||||||
|
"Identity Provider"->"Identity Provider": 3. user gets authenticated
|
||||||
|
Auth0->Browser: 4. redirect to ${YOUR_CALLBACK_URL}/#id_token=e68...
|
||||||
|
Browser->Auth0: 5. validate id_token and get user profile
|
||||||
|
Browser->"Your API": 6. call API sending JWT in Authorization header
|
||||||
|
"Your API"->"Your API": 7. validate token
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
import zenuml from './mermaid-zenuml.esm.mjs';
|
||||||
|
await mermaid.registerExternalDiagrams([zenuml]);
|
||||||
|
mermaid.initialize({
|
||||||
|
logLevel: 3,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1054
docs/CHANGELOG.md
1054
docs/CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -96,7 +96,7 @@ mermaid.initialize(config);
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667)
|
[mermaidAPI.ts:670](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L670)
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ Return the last node appended
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:306](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L306)
|
[mermaidAPI.ts:309](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L309)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:285](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L285)
|
[mermaidAPI.ts:288](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L288)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -320,4 +320,4 @@ Remove any existing elements from the given document
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:356](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L356)
|
[mermaidAPI.ts:359](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L359)
|
||||||
|
@ -16,6 +16,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) (**Native support**)
|
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) (**Native support**)
|
||||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||||
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
||||||
|
- [GitHub Writer](https://github.com/ckeditor/github-writer)
|
||||||
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) (**Native support**)
|
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) (**Native support**)
|
||||||
- [Gitea](https://gitea.io) (**Native support**)
|
- [Gitea](https://gitea.io) (**Native support**)
|
||||||
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
||||||
@ -58,6 +59,8 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [hexo-filter-mermaid-diagrams](https://github.com/webappdevelp/hexo-filter-mermaid-diagrams)
|
- [hexo-filter-mermaid-diagrams](https://github.com/webappdevelp/hexo-filter-mermaid-diagrams)
|
||||||
- [hexo-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
- [hexo-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
||||||
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
||||||
|
- [Nextra](https://nextra.site/)
|
||||||
|
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||||
|
|
||||||
## CMS
|
## CMS
|
||||||
|
|
||||||
@ -142,6 +145,8 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Named block =Diagram](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
- [Named block =Diagram](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
||||||
- [GNU Nano](https://www.nano-editor.org/)
|
- [GNU Nano](https://www.nano-editor.org/)
|
||||||
- [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid)
|
- [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid)
|
||||||
|
- [CKEditor](https://github.com/ckeditor/ckeditor5)
|
||||||
|
- [CKEditor 5 Mermaid plugin](https://github.com/ckeditor/ckeditor5-mermaid)
|
||||||
|
|
||||||
## Document Generation
|
## Document Generation
|
||||||
|
|
||||||
|
BIN
docs/syntax/img/zenuml-participant-annotators.png
Normal file
BIN
docs/syntax/img/zenuml-participant-annotators.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 255 KiB |
@ -46,9 +46,6 @@ quadrantChart
|
|||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> In place of `<text>` you can use text like `this is a sample text` or inside **double quotes** like `"This type of text may contain unicode like ❤"`.
|
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant.
|
> If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant.
|
||||||
> If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant.
|
> If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant.
|
||||||
@ -152,7 +149,7 @@ Points are used to plot a circle inside the quadrantChart. The syntax is `<text>
|
|||||||
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
||||||
quadrantChart
|
quadrantChart
|
||||||
x-axis Urgent --> Not Urgent
|
x-axis Urgent --> Not Urgent
|
||||||
y-axis Not Important --> important
|
y-axis Not Important --> "Important ❤"
|
||||||
quadrant-1 Plan
|
quadrant-1 Plan
|
||||||
quadrant-2 Do
|
quadrant-2 Do
|
||||||
quadrant-3 Deligate
|
quadrant-3 Deligate
|
||||||
@ -163,7 +160,7 @@ quadrantChart
|
|||||||
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
||||||
quadrantChart
|
quadrantChart
|
||||||
x-axis Urgent --> Not Urgent
|
x-axis Urgent --> Not Urgent
|
||||||
y-axis Not Important --> important
|
y-axis Not Important --> "Important ❤"
|
||||||
quadrant-1 Plan
|
quadrant-1 Plan
|
||||||
quadrant-2 Do
|
quadrant-2 Do
|
||||||
quadrant-3 Deligate
|
quadrant-3 Deligate
|
||||||
|
472
docs/syntax/zenuml.md
Normal file
472
docs/syntax/zenuml.md
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
> **Warning**
|
||||||
|
>
|
||||||
|
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||||
|
>
|
||||||
|
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/zenuml.md](../../packages/mermaid/src/docs/syntax/zenuml.md).
|
||||||
|
|
||||||
|
# ZenUML
|
||||||
|
|
||||||
|
> A Sequence diagram is an interaction diagram that shows how processes operate with one another and in what order.
|
||||||
|
|
||||||
|
Mermaid can render sequence diagrams with [ZenUML](https://zenuml.com). Note that ZenUML uses a different
|
||||||
|
syntax than the original Sequence Diagram in mermaid.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Demo
|
||||||
|
Alice->John: Hello John, how are you?
|
||||||
|
John->Alice: Great!
|
||||||
|
Alice->John: See you later!
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Demo
|
||||||
|
Alice->John: Hello John, how are you?
|
||||||
|
John->Alice: Great!
|
||||||
|
Alice->John: See you later!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
### Participants
|
||||||
|
|
||||||
|
The participants can be defined implicitly as in the first example on this page. The participants or actors are
|
||||||
|
rendered in order of appearance in the diagram source text. Sometimes you might want to show the participants in a
|
||||||
|
different order than how they appear in the first message. It is possible to specify the actor's order of
|
||||||
|
appearance by doing the following:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Declare participant (optional)
|
||||||
|
Bob
|
||||||
|
Alice
|
||||||
|
Alice->Bob: Hi Bob
|
||||||
|
Bob->Alice: Hi Alice
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Declare participant (optional)
|
||||||
|
Bob
|
||||||
|
Alice
|
||||||
|
Alice->Bob: Hi Bob
|
||||||
|
Bob->Alice: Hi Alice
|
||||||
|
```
|
||||||
|
|
||||||
|
### Annotators
|
||||||
|
|
||||||
|
If you specifically want to use symbols instead of just rectangles with text you can do so by using the annotator syntax to declare participants as per below.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Annotators
|
||||||
|
@Actor Alice
|
||||||
|
@Database Bob
|
||||||
|
Alice->Bob: Hi Bob
|
||||||
|
Bob->Alice: Hi Alice
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Annotators
|
||||||
|
@Actor Alice
|
||||||
|
@Database Bob
|
||||||
|
Alice->Bob: Hi Bob
|
||||||
|
Bob->Alice: Hi Alice
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are the available annotators:
|
||||||
|
![img.png](img/zenuml-participant-annotators.png)
|
||||||
|
|
||||||
|
### Aliases
|
||||||
|
|
||||||
|
The participants can have a convenient identifier and a descriptive label.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Aliases
|
||||||
|
A as Alice
|
||||||
|
J as John
|
||||||
|
A->J: Hello John, how are you?
|
||||||
|
J->A: Great!
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Aliases
|
||||||
|
A as Alice
|
||||||
|
J as John
|
||||||
|
A->J: Hello John, how are you?
|
||||||
|
J->A: Great!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Messages
|
||||||
|
|
||||||
|
Messages can be one of:
|
||||||
|
|
||||||
|
1. Sync message
|
||||||
|
2. Async message
|
||||||
|
3. Creation message
|
||||||
|
4. Reply message
|
||||||
|
|
||||||
|
### Sync message
|
||||||
|
|
||||||
|
You can think of a sync (blocking) method in a programming language.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Sync message
|
||||||
|
A.SyncMessage
|
||||||
|
A.SyncMessage(with, parameters) {
|
||||||
|
B.nestedSyncMessage()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Sync message
|
||||||
|
A.SyncMessage
|
||||||
|
A.SyncMessage(with, parameters) {
|
||||||
|
B.nestedSyncMessage()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Async message
|
||||||
|
|
||||||
|
You can think of an async (non-blocking) method in a programming language.
|
||||||
|
Fire an event and forget about it.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Async message
|
||||||
|
Alice->Bob: How are you?
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Async message
|
||||||
|
Alice->Bob: How are you?
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creation message
|
||||||
|
|
||||||
|
We use `new` keyword to create an object.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
new A1
|
||||||
|
new A2(with, parameters)
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
new A1
|
||||||
|
new A2(with, parameters)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reply message
|
||||||
|
|
||||||
|
There are three ways to express a reply message:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
// 1. assign a variable from a sync message.
|
||||||
|
a = A.SyncMessage()
|
||||||
|
|
||||||
|
// 1.1. optionally give the variable a type
|
||||||
|
SomeType a = A.SyncMessage()
|
||||||
|
|
||||||
|
// 2. use return keyword
|
||||||
|
A.SyncMessage() {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. use @return or @reply annotator on an async message
|
||||||
|
@return
|
||||||
|
A->B: result
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
// 1. assign a variable from a sync message.
|
||||||
|
a = A.SyncMessage()
|
||||||
|
|
||||||
|
// 1.1. optionally give the variable a type
|
||||||
|
SomeType a = A.SyncMessage()
|
||||||
|
|
||||||
|
// 2. use return keyword
|
||||||
|
A.SyncMessage() {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. use @return or @reply annotator on an async message
|
||||||
|
@return
|
||||||
|
A->B: result
|
||||||
|
```
|
||||||
|
|
||||||
|
The third way `@return` is rarely used, but it is useful when you want to return to one level up.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Reply message
|
||||||
|
Client->A.method() {
|
||||||
|
B.method() {
|
||||||
|
if(condition) {
|
||||||
|
return x1
|
||||||
|
// return early
|
||||||
|
@return
|
||||||
|
A->Client: x11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
title Reply message
|
||||||
|
Client->A.method() {
|
||||||
|
B.method() {
|
||||||
|
if(condition) {
|
||||||
|
return x1
|
||||||
|
// return early
|
||||||
|
@return
|
||||||
|
A->Client: x11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nesting
|
||||||
|
|
||||||
|
Sync messages and Creation messages are naturally nestable with `{}`.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
A.method() {
|
||||||
|
B.nested_sync_method()
|
||||||
|
B->C: nested async message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
A.method() {
|
||||||
|
B.nested_sync_method()
|
||||||
|
B->C: nested async message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
It is possible to add comments to a sequence diagram with `// comment` syntax.
|
||||||
|
Comments will be rendered above the messages or fragments. Comments on other places
|
||||||
|
are ignored. Markdown is supported.
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
// a comment on a participant will not be rendered
|
||||||
|
BookService
|
||||||
|
// a comment on a message.
|
||||||
|
// **Markdown** is supported.
|
||||||
|
BookService.getBook()
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
// a comment on a participant will not be rendered
|
||||||
|
BookService
|
||||||
|
// a comment on a message.
|
||||||
|
// **Markdown** is supported.
|
||||||
|
BookService.getBook()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loops
|
||||||
|
|
||||||
|
It is possible to express loops in a ZenUML diagram. This is done by any of the
|
||||||
|
following notations:
|
||||||
|
|
||||||
|
1. while
|
||||||
|
2. for
|
||||||
|
3. forEach, foreach
|
||||||
|
4. loop
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
while(condition) {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
Alice->John: Hello John, how are you?
|
||||||
|
while(true) {
|
||||||
|
John->Alice: Great!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
Alice->John: Hello John, how are you?
|
||||||
|
while(true) {
|
||||||
|
John->Alice: Great!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alt
|
||||||
|
|
||||||
|
It is possible to express alternative paths in a sequence diagram. This is done by the notation
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
if(condition1) {
|
||||||
|
...statements...
|
||||||
|
} else if(condition2) {
|
||||||
|
...statements...
|
||||||
|
} else {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
if(is_sick) {
|
||||||
|
Bob->Alice: Not so good :(
|
||||||
|
} else {
|
||||||
|
Bob->Alice: Feeling fresh like a daisy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
if(is_sick) {
|
||||||
|
Bob->Alice: Not so good :(
|
||||||
|
} else {
|
||||||
|
Bob->Alice: Feeling fresh like a daisy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Opt
|
||||||
|
|
||||||
|
It is possible to render an `opt` fragment. This is done by the notation
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
opt {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
Bob->Alice: Not so good :(
|
||||||
|
opt {
|
||||||
|
Bob->Alice: Thanks for asking
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
Bob->Alice: Not so good :(
|
||||||
|
opt {
|
||||||
|
Bob->Alice: Thanks for asking
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel
|
||||||
|
|
||||||
|
It is possible to show actions that are happening in parallel.
|
||||||
|
|
||||||
|
This is done by the notation
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
par {
|
||||||
|
statement1
|
||||||
|
statement2
|
||||||
|
statement3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
par {
|
||||||
|
Alice->Bob: Hello guys!
|
||||||
|
Alice->John: Hello guys!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
par {
|
||||||
|
Alice->Bob: Hello guys!
|
||||||
|
Alice->John: Hello guys!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Try/Catch/Finally (Break)
|
||||||
|
|
||||||
|
It is possible to indicate a stop of the sequence within the flow (usually used to model exceptions).
|
||||||
|
|
||||||
|
This is done by the notation
|
||||||
|
|
||||||
|
try {
|
||||||
|
...statements...
|
||||||
|
} catch {
|
||||||
|
...statements...
|
||||||
|
} finally {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
try {
|
||||||
|
Consumer->API: Book something
|
||||||
|
API->BookingService: Start booking process
|
||||||
|
} catch {
|
||||||
|
API->Consumer: show failure
|
||||||
|
} finally {
|
||||||
|
API->BookingService: rollback status
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
zenuml
|
||||||
|
try {
|
||||||
|
Consumer->API: Book something
|
||||||
|
API->BookingService: Start booking process
|
||||||
|
} catch {
|
||||||
|
API->Consumer: show failure
|
||||||
|
} finally {
|
||||||
|
API->BookingService: rollback status
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrating with your library/website.
|
||||||
|
|
||||||
|
Zenuml uses the experimental lazy loading & async rendering features which could change in the future.
|
||||||
|
|
||||||
|
You can use this method to add mermaid including the zenuml diagram to a web page:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||||
|
import zenuml from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@0.1.0/dist/mermaid-zenuml.esm.min.mjs';
|
||||||
|
await mermaid.registerExternalDiagrams([zenuml]);
|
||||||
|
</script>
|
||||||
|
```
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid-monorepo",
|
"name": "mermaid-monorepo",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "10.2.0",
|
"version": "10.2.3",
|
||||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@8.5.1",
|
"packageManager": "pnpm@8.5.1",
|
||||||
@ -18,7 +18,7 @@
|
|||||||
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
|
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
|
||||||
"build:mermaid": "pnpm build:vite --mermaid",
|
"build:mermaid": "pnpm build:vite --mermaid",
|
||||||
"build:viz": "pnpm build:mermaid --visualize",
|
"build:viz": "pnpm build:mermaid --visualize",
|
||||||
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
|
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
|
||||||
"build:watch": "pnpm build:vite --watch",
|
"build:watch": "pnpm build:vite --watch",
|
||||||
"build": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
|
"build": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
|
||||||
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
|
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
|
||||||
|
1
packages/mermaid-zenuml/README.md
Symbolic link
1
packages/mermaid-zenuml/README.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../mermaid/src/docs/syntax/zenuml.md
|
47
packages/mermaid-zenuml/package.json
Normal file
47
packages/mermaid-zenuml/package.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"name": "@mermaid-js/mermaid-zenuml",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "MermaidJS plugin for ZenUML integration",
|
||||||
|
"module": "dist/mermaid-zenuml.core.mjs",
|
||||||
|
"types": "dist/detector.d.ts",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/mermaid-zenuml.core.mjs",
|
||||||
|
"types": "./dist/detector.d.ts"
|
||||||
|
},
|
||||||
|
"./*": "./*"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"diagram",
|
||||||
|
"markdown",
|
||||||
|
"zenuml",
|
||||||
|
"mermaid"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"prepublishOnly": "pnpm -w run build"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mermaid-js/mermaid",
|
||||||
|
"directory": "packages/mermaid-zenuml"
|
||||||
|
},
|
||||||
|
"contributors": [
|
||||||
|
"Peng Xiao (https://github.com/MrCoder)",
|
||||||
|
"Sidharth Vinod (https://sidharth.dev)",
|
||||||
|
"Dong Cai (https://github.com/dontry)"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@zenuml/core": "^3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"mermaid": "workspace:^"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"mermaid": "workspace:>=10.0.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
21
packages/mermaid-zenuml/src/detector.ts
Normal file
21
packages/mermaid-zenuml/src/detector.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { ExternalDiagramDefinition } from 'mermaid';
|
||||||
|
|
||||||
|
const id = 'zenuml';
|
||||||
|
const regexp = /^\s*zenuml/;
|
||||||
|
|
||||||
|
const detector = (txt: string) => {
|
||||||
|
return txt.match(regexp) !== null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loader = async () => {
|
||||||
|
const { diagram } = await import('./zenuml-definition.js');
|
||||||
|
return { id, diagram };
|
||||||
|
};
|
||||||
|
|
||||||
|
const plugin: ExternalDiagramDefinition = {
|
||||||
|
id,
|
||||||
|
detector,
|
||||||
|
loader,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default plugin;
|
58
packages/mermaid-zenuml/src/mermaidUtils.ts
Normal file
58
packages/mermaid-zenuml/src/mermaidUtils.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import type { MermaidConfig } from 'mermaid';
|
||||||
|
|
||||||
|
const warning = (s: string) => {
|
||||||
|
// Todo remove debug code
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Log function was called before initialization', s);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||||
|
|
||||||
|
export const LEVELS: Record<LogLevel, number> = {
|
||||||
|
trace: 0,
|
||||||
|
debug: 1,
|
||||||
|
info: 2,
|
||||||
|
warn: 3,
|
||||||
|
error: 4,
|
||||||
|
fatal: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const log: Record<keyof typeof LEVELS, typeof console.log> = {
|
||||||
|
trace: warning,
|
||||||
|
debug: warning,
|
||||||
|
info: warning,
|
||||||
|
warn: warning,
|
||||||
|
error: warning,
|
||||||
|
fatal: warning,
|
||||||
|
};
|
||||||
|
|
||||||
|
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
|
||||||
|
export let getConfig: () => MermaidConfig;
|
||||||
|
export let sanitizeText: (str: string) => string;
|
||||||
|
// eslint-disable @typescript-eslint/no-explicit-any
|
||||||
|
export let setupGraphViewbox: (
|
||||||
|
graph: any,
|
||||||
|
svgElem: any,
|
||||||
|
padding: any,
|
||||||
|
useMaxWidth: boolean
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export const injectUtils = (
|
||||||
|
_log: Record<keyof typeof LEVELS, typeof console.log>,
|
||||||
|
_setLogLevel: any,
|
||||||
|
_getConfig: any,
|
||||||
|
_sanitizeText: any,
|
||||||
|
_setupGraphViewbox: any
|
||||||
|
) => {
|
||||||
|
_log.info('Mermaid utils injected');
|
||||||
|
log.trace = _log.trace;
|
||||||
|
log.debug = _log.debug;
|
||||||
|
log.info = _log.info;
|
||||||
|
log.warn = _log.warn;
|
||||||
|
log.error = _log.error;
|
||||||
|
log.fatal = _log.fatal;
|
||||||
|
setLogLevel = _setLogLevel;
|
||||||
|
getConfig = _getConfig;
|
||||||
|
sanitizeText = _sanitizeText;
|
||||||
|
setupGraphViewbox = _setupGraphViewbox;
|
||||||
|
};
|
12
packages/mermaid-zenuml/src/parser.ts
Normal file
12
packages/mermaid-zenuml/src/parser.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* ZenUML manage parsing internally. It uses Antlr4 to parse the DSL.
|
||||||
|
* The parser is defined in https://github.com/ZenUml/vue-sequence/blob/main/src/parser/index.js
|
||||||
|
*
|
||||||
|
* This is a dummy parser that satisfies the mermaid API logic.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
parser: { yy: {} },
|
||||||
|
parse: () => {
|
||||||
|
// no op
|
||||||
|
},
|
||||||
|
};
|
17
packages/mermaid-zenuml/src/zenuml-definition.ts
Normal file
17
packages/mermaid-zenuml/src/zenuml-definition.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { injectUtils } from './mermaidUtils.js';
|
||||||
|
import parser from './parser.js';
|
||||||
|
import renderer from './zenumlRenderer.js';
|
||||||
|
|
||||||
|
export const diagram = {
|
||||||
|
db: {
|
||||||
|
clear: () => {
|
||||||
|
// no-op
|
||||||
|
},
|
||||||
|
},
|
||||||
|
renderer,
|
||||||
|
parser,
|
||||||
|
styles: () => {
|
||||||
|
// no-op
|
||||||
|
},
|
||||||
|
injectUtils,
|
||||||
|
};
|
68
packages/mermaid-zenuml/src/zenumlRenderer.ts
Normal file
68
packages/mermaid-zenuml/src/zenumlRenderer.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { getConfig, log } from './mermaidUtils.js';
|
||||||
|
import ZenUml from '@zenuml/core';
|
||||||
|
|
||||||
|
const regexp = /^\s*zenuml/;
|
||||||
|
|
||||||
|
// Create a Zen UML container outside the svg first for rendering, otherwise the Zen UML diagram cannot be rendered properly
|
||||||
|
function createTemporaryZenumlContainer(id: string) {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.id = `container-${id}`;
|
||||||
|
container.style.display = 'flex';
|
||||||
|
container.innerHTML = `<div id="zenUMLApp-${id}"></div>`;
|
||||||
|
const app = container.querySelector(`#zenUMLApp-${id}`) as HTMLElement;
|
||||||
|
return { container, app };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a foreignObject to wrap the Zen UML container in the svg
|
||||||
|
function createForeignObject(id: string) {
|
||||||
|
const foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||||
|
foreignObject.setAttribute('x', '0');
|
||||||
|
foreignObject.setAttribute('y', '0');
|
||||||
|
foreignObject.setAttribute('width', '100%');
|
||||||
|
foreignObject.setAttribute('height', '100%');
|
||||||
|
const { container, app } = createTemporaryZenumlContainer(id);
|
||||||
|
foreignObject.appendChild(container);
|
||||||
|
return { foreignObject, container, app };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a Zen UML in the tag with id: id based on the graph definition in text.
|
||||||
|
*
|
||||||
|
* @param text - The text of the diagram
|
||||||
|
* @param id - The id of the diagram which will be used as a DOM element id¨
|
||||||
|
*/
|
||||||
|
export const draw = async function (text: string, id: string) {
|
||||||
|
log.info('draw with Zen UML renderer', ZenUml);
|
||||||
|
|
||||||
|
text = text.replace(regexp, '');
|
||||||
|
const { securityLevel } = getConfig();
|
||||||
|
// Handle root and Document for when rendering in sandbox mode
|
||||||
|
let sandboxElement: HTMLIFrameElement | null = null;
|
||||||
|
if (securityLevel === 'sandbox') {
|
||||||
|
sandboxElement = document.getElementById('i' + id) as HTMLIFrameElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = securityLevel === 'sandbox' ? sandboxElement?.contentWindow?.document : document;
|
||||||
|
|
||||||
|
const svgContainer = root?.querySelector(`svg#${id}`);
|
||||||
|
|
||||||
|
if (!root || !svgContainer) {
|
||||||
|
log.error('Cannot find root or svgContainer');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { foreignObject, container, app } = createForeignObject(id);
|
||||||
|
svgContainer.appendChild(foreignObject);
|
||||||
|
// @ts-expect-error @zenuml/core@3.0.0 exports the wrong type for ZenUml
|
||||||
|
const zenuml = new ZenUml(app);
|
||||||
|
// default is a theme name. More themes to be added and will be configurable in the future
|
||||||
|
await zenuml.render(text, 'theme-mermaid');
|
||||||
|
|
||||||
|
const { width, height } = window.getComputedStyle(container);
|
||||||
|
log.debug('zenuml diagram size', width, height);
|
||||||
|
svgContainer.setAttribute('style', `width: ${width}; height: ${height};`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
draw,
|
||||||
|
};
|
9
packages/mermaid-zenuml/tsconfig.json
Normal file
9
packages/mermaid-zenuml/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist"
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*.ts"],
|
||||||
|
"typeRoots": ["./src/types"]
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "10.2.0",
|
"version": "10.2.3",
|
||||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "./dist/mermaid.core.mjs",
|
"module": "./dist/mermaid.core.mjs",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts",
|
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts",
|
||||||
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify",
|
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify",
|
||||||
"docs:pre:vitepress": "rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress",
|
"docs:pre:vitepress": "rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress",
|
||||||
"docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm --filter ./ install --no-frozen-lockfile && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing",
|
"docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm --filter ./ install --no-frozen-lockfile --ignore-scripts && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing",
|
||||||
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./ src/vitepress dev\" \"ts-node-esm src/docs.mts --watch --vitepress\"",
|
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./ src/vitepress dev\" \"ts-node-esm src/docs.mts --watch --vitepress\"",
|
||||||
"docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress",
|
"docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress",
|
||||||
"docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"",
|
"docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"",
|
||||||
|
@ -717,7 +717,7 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
|
|||||||
const edgePath = edgesEl
|
const edgePath = edgesEl
|
||||||
.insert('path')
|
.insert('path')
|
||||||
.attr('d', curve(points))
|
.attr('d', curve(points))
|
||||||
.attr('class', 'path')
|
.attr('class', 'path ' + edgeData.classes)
|
||||||
.attr('fill', 'none');
|
.attr('fill', 'none');
|
||||||
const edgeG = edgesEl.insert('g').attr('class', 'edgeLabel');
|
const edgeG = edgesEl.insert('g').attr('class', 'edgeLabel');
|
||||||
const edgeWithLabel = select(edgeG.node().appendChild(edge.labelEl));
|
const edgeWithLabel = select(edgeG.node().appendChild(edge.labelEl));
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
import dayjs from 'dayjs/esm/index.js';
|
import dayjs from 'dayjs';
|
||||||
import dayjsIsoWeek from 'dayjs/esm/plugin/isoWeek/index.js';
|
import dayjsIsoWeek from 'dayjs/plugin/isoWeek.js';
|
||||||
import dayjsCustomParseFormat from 'dayjs/esm/plugin/customParseFormat/index.js';
|
import dayjsCustomParseFormat from 'dayjs/plugin/customParseFormat.js';
|
||||||
import dayjsAdvancedFormat from 'dayjs/esm/plugin/advancedFormat/index.js';
|
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import * as configApi from '../../config.js';
|
import * as configApi from '../../config.js';
|
||||||
import utils from '../../utils.js';
|
import utils from '../../utils.js';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck TODO: Fix TS
|
// @ts-nocheck TODO: Fix TS
|
||||||
import dayjs from 'dayjs/esm/index.js';
|
import dayjs from 'dayjs';
|
||||||
import ganttDb from './ganttDb.js';
|
import ganttDb from './ganttDb.js';
|
||||||
import { convert } from '../../tests/util.js';
|
import { convert } from '../../tests/util.js';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import dayjs from 'dayjs/esm/index.js';
|
import dayjs from 'dayjs';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import {
|
import {
|
||||||
select,
|
select,
|
||||||
|
@ -119,10 +119,10 @@ points
|
|||||||
|
|
||||||
axisDetails
|
axisDetails
|
||||||
: X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);}
|
: X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);}
|
||||||
| X-AXIS text AXIS-TEXT-DELIMITER {$2.text += $3; yy.setXAxisLeftText($2);}
|
| X-AXIS text AXIS-TEXT-DELIMITER {$2.text += " ⟶ "; yy.setXAxisLeftText($2);}
|
||||||
| X-AXIS text {yy.setXAxisLeftText($2);}
|
| X-AXIS text {yy.setXAxisLeftText($2);}
|
||||||
| Y-AXIS text AXIS-TEXT-DELIMITER text {yy.setYAxisBottomText($2); yy.setYAxisTopText($4);}
|
| Y-AXIS text AXIS-TEXT-DELIMITER text {yy.setYAxisBottomText($2); yy.setYAxisTopText($4);}
|
||||||
| Y-AXIS text AXIS-TEXT-DELIMITER {$2.text += $3; yy.setYAxisBottomText($2);}
|
| Y-AXIS text AXIS-TEXT-DELIMITER {$2.text += " ⟶ "; yy.setYAxisBottomText($2);}
|
||||||
| Y-AXIS text {yy.setYAxisBottomText($2);}
|
| Y-AXIS text {yy.setYAxisBottomText($2);}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ describe('Testing quadrantChart jison file', () => {
|
|||||||
str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> ';
|
str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> ';
|
||||||
expect(parserFnConstructor(str)).not.toThrow();
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
|
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
|
||||||
text: 'Urgent(* +=[❤ --> ',
|
text: 'Urgent(* +=[❤ ⟶ ',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
});
|
});
|
||||||
expect(mockDB.setXAxisRightText).not.toHaveBeenCalled();
|
expect(mockDB.setXAxisRightText).not.toHaveBeenCalled();
|
||||||
@ -131,7 +131,7 @@ describe('Testing quadrantChart jison file', () => {
|
|||||||
str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤" --> ';
|
str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤" --> ';
|
||||||
expect(parserFnConstructor(str)).not.toThrow();
|
expect(parserFnConstructor(str)).not.toThrow();
|
||||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
|
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
|
||||||
text: 'Urgent(* +=[❤ --> ',
|
text: 'Urgent(* +=[❤ ⟶ ',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
});
|
});
|
||||||
expect(mockDB.setYAxisTopText).not.toHaveBeenCalled();
|
expect(mockDB.setYAxisTopText).not.toHaveBeenCalled();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import common from '../common/common.js';
|
import common from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
import * as svgDrawCommon from '../common/svgDrawCommon';
|
||||||
import { addFunction } from '../../interactionDb.js';
|
import { addFunction } from '../../interactionDb.js';
|
||||||
import { parseFontSize } from '../../utils.js';
|
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
|
||||||
export const drawRect = function (elem, rectData) {
|
export const drawRect = function (elem, rectData) {
|
||||||
@ -224,15 +224,16 @@ export const drawText = function (elem, textData) {
|
|||||||
textElem.attr('dy', dy);
|
textElem.attr('dy', dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const text = line || ZERO_WIDTH_SPACE;
|
||||||
if (textData.tspan) {
|
if (textData.tspan) {
|
||||||
const span = textElem.append('tspan');
|
const span = textElem.append('tspan');
|
||||||
span.attr('x', textData.x);
|
span.attr('x', textData.x);
|
||||||
if (textData.fill !== undefined) {
|
if (textData.fill !== undefined) {
|
||||||
span.attr('fill', textData.fill);
|
span.attr('fill', textData.fill);
|
||||||
}
|
}
|
||||||
span.text(line);
|
span.text(text);
|
||||||
} else {
|
} else {
|
||||||
textElem.text(line);
|
textElem.text(text);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
textData.valign !== undefined &&
|
textData.valign !== undefined &&
|
||||||
|
@ -5,12 +5,12 @@ import { contributors } from '../contributors';
|
|||||||
<template>
|
<template>
|
||||||
<div flex="~ wrap gap2" justify-center>
|
<div flex="~ wrap gap2" justify-center>
|
||||||
<a
|
<a
|
||||||
v-for="{ name, avatar } of contributors"
|
v-for="{ username, avatar } of contributors"
|
||||||
:key="name"
|
:key="username"
|
||||||
:href="`https://github.com/${name}`"
|
:href="`https://github.com/${username}`"
|
||||||
m-0
|
m-0
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
:aria-label="`${name} on GitHub`"
|
:aria-label="`${username} on GitHub`"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@ -20,7 +20,7 @@ import { contributors } from '../contributors';
|
|||||||
rounded-full
|
rounded-full
|
||||||
h-12
|
h-12
|
||||||
w-12
|
w-12
|
||||||
:alt="`${name}'s avatar`"
|
:alt="`${username}'s avatar`"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,6 +121,7 @@ function sidebarSyntax() {
|
|||||||
{ text: 'C4C Diagram (Context) Diagram 🦺⚠️', link: '/syntax/c4c' },
|
{ text: 'C4C Diagram (Context) Diagram 🦺⚠️', link: '/syntax/c4c' },
|
||||||
{ text: 'Mindmaps 🔥', link: '/syntax/mindmap' },
|
{ text: 'Mindmaps 🔥', link: '/syntax/mindmap' },
|
||||||
{ text: 'Timeline 🔥', link: '/syntax/timeline' },
|
{ text: 'Timeline 🔥', link: '/syntax/timeline' },
|
||||||
|
{ text: 'Zenuml 🔥', link: '/syntax/zenuml' },
|
||||||
{ text: 'Other Examples', link: '/syntax/examples' },
|
{ text: 'Other Examples', link: '/syntax/examples' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
||||||
import { existsSync } from 'node:fs';
|
import { existsSync } from 'node:fs';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
@ -12,22 +13,23 @@ async function download(url: string, fileName: URL) {
|
|||||||
if (existsSync(fileName)) {
|
if (existsSync(fileName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-console
|
console.log('downloading', url);
|
||||||
console.log('downloading', fileName);
|
|
||||||
try {
|
try {
|
||||||
const image = await fetch(url);
|
const image = await fetch(url);
|
||||||
await writeFile(fileName, Buffer.from(await image.arrayBuffer()));
|
await writeFile(fileName, Buffer.from(await image.arrayBuffer()));
|
||||||
} catch {}
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAvatars() {
|
async function fetchAvatars() {
|
||||||
await mkdir(fileURLToPath(new URL('..', getAvatarPath('none'))), { recursive: true });
|
await mkdir(fileURLToPath(new URL(getAvatarPath('none'))).replace('none.png', ''), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
contributors = JSON.parse(await readFile(pathContributors, { encoding: 'utf-8' }));
|
contributors = JSON.parse(await readFile(pathContributors, { encoding: 'utf-8' }));
|
||||||
await Promise.allSettled(
|
for (const name of contributors) {
|
||||||
contributors.map((name) =>
|
await download(`https://github.com/${name}.png?size=100`, getAvatarPath(name));
|
||||||
download(`https://github.com/${name}.png?size=100`, getAvatarPath(name))
|
}
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAvatars();
|
fetchAvatars();
|
||||||
|
@ -23,9 +23,8 @@ async function fetchContributors() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
data = await response.json();
|
data = await response.json();
|
||||||
console.log(response.status, response.statusText);
|
|
||||||
console.log(data);
|
|
||||||
collaborators.push(...data.map((i) => i.login));
|
collaborators.push(...data.map((i) => i.login));
|
||||||
|
console.log(`Fetched page ${page}`);
|
||||||
page++;
|
page++;
|
||||||
} while (data.length === 100);
|
} while (data.length === 100);
|
||||||
return collaborators.filter((name) => !name.includes('[bot]'));
|
return collaborators.filter((name) => !name.includes('[bot]'));
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import mermaid, { type MermaidConfig } from 'mermaid';
|
import mermaid, { type MermaidConfig } from 'mermaid';
|
||||||
|
import zenuml from '../../../../../mermaid-zenuml/dist/mermaid-zenuml.core.mjs';
|
||||||
|
|
||||||
|
const init = mermaid.registerExternalDiagrams([zenuml]);
|
||||||
|
|
||||||
export const render = async (id: string, code: string, config: MermaidConfig): Promise<string> => {
|
export const render = async (id: string, code: string, config: MermaidConfig): Promise<string> => {
|
||||||
|
await init;
|
||||||
mermaid.initialize(config);
|
mermaid.initialize(config);
|
||||||
const { svg } = await mermaid.render(id, code);
|
const { svg } = await mermaid.render(id, code);
|
||||||
return svg;
|
return svg;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) (**Native support**)
|
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) (**Native support**)
|
||||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||||
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
||||||
|
- [GitHub Writer](https://github.com/ckeditor/github-writer)
|
||||||
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) (**Native support**)
|
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) (**Native support**)
|
||||||
- [Gitea](https://gitea.io) (**Native support**)
|
- [Gitea](https://gitea.io) (**Native support**)
|
||||||
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
||||||
@ -52,6 +53,8 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [hexo-filter-mermaid-diagrams](https://github.com/webappdevelp/hexo-filter-mermaid-diagrams)
|
- [hexo-filter-mermaid-diagrams](https://github.com/webappdevelp/hexo-filter-mermaid-diagrams)
|
||||||
- [hexo-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
- [hexo-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
||||||
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
||||||
|
- [Nextra](https://nextra.site/)
|
||||||
|
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||||
|
|
||||||
## CMS
|
## CMS
|
||||||
|
|
||||||
@ -136,6 +139,8 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Named block =Diagram](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
- [Named block =Diagram](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
||||||
- [GNU Nano](https://www.nano-editor.org/)
|
- [GNU Nano](https://www.nano-editor.org/)
|
||||||
- [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid)
|
- [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid)
|
||||||
|
- [CKEditor](https://github.com/ckeditor/ckeditor5)
|
||||||
|
- [CKEditor 5 Mermaid plugin](https://github.com/ckeditor/ckeditor5-mermaid)
|
||||||
|
|
||||||
## Document Generation
|
## Document Generation
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
"unplugin-vue-components": "^0.24.1",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
"vite": "^4.3.3",
|
"vite": "^4.3.3",
|
||||||
"vite-plugin-pwa": "^0.15.0",
|
"vite-plugin-pwa": "^0.15.0",
|
||||||
"vitepress": "1.0.0-alpha.76",
|
"vitepress": "1.0.0-beta.1",
|
||||||
"workbox-window": "^6.5.4"
|
"workbox-window": "^6.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 255 KiB |
@ -23,10 +23,6 @@ quadrantChart
|
|||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
```note
|
|
||||||
In place of `<text>` you can use text like `this is a sample text` or inside **double quotes** like `"This type of text may contain unicode like ❤"`.
|
|
||||||
```
|
|
||||||
|
|
||||||
```note
|
```note
|
||||||
If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant.
|
If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant.
|
||||||
If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant.
|
If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant.
|
||||||
@ -134,7 +130,7 @@ Points are used to plot a circle inside the quadrantChart. The syntax is `<text>
|
|||||||
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
||||||
quadrantChart
|
quadrantChart
|
||||||
x-axis Urgent --> Not Urgent
|
x-axis Urgent --> Not Urgent
|
||||||
y-axis Not Important --> important
|
y-axis Not Important --> "Important ❤"
|
||||||
quadrant-1 Plan
|
quadrant-1 Plan
|
||||||
quadrant-2 Do
|
quadrant-2 Do
|
||||||
quadrant-3 Deligate
|
quadrant-3 Deligate
|
||||||
|
314
packages/mermaid/src/docs/syntax/zenuml.md
Normal file
314
packages/mermaid/src/docs/syntax/zenuml.md
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
# ZenUML
|
||||||
|
|
||||||
|
> A Sequence diagram is an interaction diagram that shows how processes operate with one another and in what order.
|
||||||
|
|
||||||
|
Mermaid can render sequence diagrams with [ZenUML](https://zenuml.com). Note that ZenUML uses a different
|
||||||
|
syntax than the original Sequence Diagram in mermaid.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Demo
|
||||||
|
Alice->John: Hello John, how are you?
|
||||||
|
John->Alice: Great!
|
||||||
|
Alice->John: See you later!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
### Participants
|
||||||
|
|
||||||
|
The participants can be defined implicitly as in the first example on this page. The participants or actors are
|
||||||
|
rendered in order of appearance in the diagram source text. Sometimes you might want to show the participants in a
|
||||||
|
different order than how they appear in the first message. It is possible to specify the actor's order of
|
||||||
|
appearance by doing the following:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Declare participant (optional)
|
||||||
|
Bob
|
||||||
|
Alice
|
||||||
|
Alice->Bob: Hi Bob
|
||||||
|
Bob->Alice: Hi Alice
|
||||||
|
```
|
||||||
|
|
||||||
|
### Annotators
|
||||||
|
|
||||||
|
If you specifically want to use symbols instead of just rectangles with text you can do so by using the annotator syntax to declare participants as per below.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Annotators
|
||||||
|
@Actor Alice
|
||||||
|
@Database Bob
|
||||||
|
Alice->Bob: Hi Bob
|
||||||
|
Bob->Alice: Hi Alice
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are the available annotators:
|
||||||
|
![img.png](img/zenuml-participant-annotators.png)
|
||||||
|
|
||||||
|
### Aliases
|
||||||
|
|
||||||
|
The participants can have a convenient identifier and a descriptive label.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Aliases
|
||||||
|
A as Alice
|
||||||
|
J as John
|
||||||
|
A->J: Hello John, how are you?
|
||||||
|
J->A: Great!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Messages
|
||||||
|
|
||||||
|
Messages can be one of:
|
||||||
|
|
||||||
|
1. Sync message
|
||||||
|
2. Async message
|
||||||
|
3. Creation message
|
||||||
|
4. Reply message
|
||||||
|
|
||||||
|
### Sync message
|
||||||
|
|
||||||
|
You can think of a sync (blocking) method in a programming language.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Sync message
|
||||||
|
A.SyncMessage
|
||||||
|
A.SyncMessage(with, parameters) {
|
||||||
|
B.nestedSyncMessage()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Async message
|
||||||
|
|
||||||
|
You can think of an async (non-blocking) method in a programming language.
|
||||||
|
Fire an event and forget about it.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Async message
|
||||||
|
Alice->Bob: How are you?
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creation message
|
||||||
|
|
||||||
|
We use `new` keyword to create an object.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
new A1
|
||||||
|
new A2(with, parameters)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reply message
|
||||||
|
|
||||||
|
There are three ways to express a reply message:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
// 1. assign a variable from a sync message.
|
||||||
|
a = A.SyncMessage()
|
||||||
|
|
||||||
|
// 1.1. optionally give the variable a type
|
||||||
|
SomeType a = A.SyncMessage()
|
||||||
|
|
||||||
|
// 2. use return keyword
|
||||||
|
A.SyncMessage() {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. use @return or @reply annotator on an async message
|
||||||
|
@return
|
||||||
|
A->B: result
|
||||||
|
```
|
||||||
|
|
||||||
|
The third way `@return` is rarely used, but it is useful when you want to return to one level up.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
title Reply message
|
||||||
|
Client->A.method() {
|
||||||
|
B.method() {
|
||||||
|
if(condition) {
|
||||||
|
return x1
|
||||||
|
// return early
|
||||||
|
@return
|
||||||
|
A->Client: x11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nesting
|
||||||
|
|
||||||
|
Sync messages and Creation messages are naturally nestable with `{}`.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
A.method() {
|
||||||
|
B.nested_sync_method()
|
||||||
|
B->C: nested async message
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
It is possible to add comments to a sequence diagram with `// comment` syntax.
|
||||||
|
Comments will be rendered above the messages or fragments. Comments on other places
|
||||||
|
are ignored. Markdown is supported.
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
// a comment on a participant will not be rendered
|
||||||
|
BookService
|
||||||
|
// a comment on a message.
|
||||||
|
// **Markdown** is supported.
|
||||||
|
BookService.getBook()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loops
|
||||||
|
|
||||||
|
It is possible to express loops in a ZenUML diagram. This is done by any of the
|
||||||
|
following notations:
|
||||||
|
|
||||||
|
1. while
|
||||||
|
2. for
|
||||||
|
3. forEach, foreach
|
||||||
|
4. loop
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
while(condition) {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
Alice->John: Hello John, how are you?
|
||||||
|
while(true) {
|
||||||
|
John->Alice: Great!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alt
|
||||||
|
|
||||||
|
It is possible to express alternative paths in a sequence diagram. This is done by the notation
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
if(condition1) {
|
||||||
|
...statements...
|
||||||
|
} else if(condition2) {
|
||||||
|
...statements...
|
||||||
|
} else {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
if(is_sick) {
|
||||||
|
Bob->Alice: Not so good :(
|
||||||
|
} else {
|
||||||
|
Bob->Alice: Feeling fresh like a daisy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Opt
|
||||||
|
|
||||||
|
It is possible to render an `opt` fragment. This is done by the notation
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
opt {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
Alice->Bob: Hello Bob, how are you?
|
||||||
|
Bob->Alice: Not so good :(
|
||||||
|
opt {
|
||||||
|
Bob->Alice: Thanks for asking
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel
|
||||||
|
|
||||||
|
It is possible to show actions that are happening in parallel.
|
||||||
|
|
||||||
|
This is done by the notation
|
||||||
|
|
||||||
|
```zenuml
|
||||||
|
par {
|
||||||
|
statement1
|
||||||
|
statement2
|
||||||
|
statement3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
par {
|
||||||
|
Alice->Bob: Hello guys!
|
||||||
|
Alice->John: Hello guys!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Try/Catch/Finally (Break)
|
||||||
|
|
||||||
|
It is possible to indicate a stop of the sequence within the flow (usually used to model exceptions).
|
||||||
|
|
||||||
|
This is done by the notation
|
||||||
|
|
||||||
|
```
|
||||||
|
try {
|
||||||
|
...statements...
|
||||||
|
} catch {
|
||||||
|
...statements...
|
||||||
|
} finally {
|
||||||
|
...statements...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the example below:
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
zenuml
|
||||||
|
try {
|
||||||
|
Consumer->API: Book something
|
||||||
|
API->BookingService: Start booking process
|
||||||
|
} catch {
|
||||||
|
API->Consumer: show failure
|
||||||
|
} finally {
|
||||||
|
API->BookingService: rollback status
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrating with your library/website.
|
||||||
|
|
||||||
|
Zenuml uses the experimental lazy loading & async rendering features which could change in the future.
|
||||||
|
|
||||||
|
You can use this method to add mermaid including the zenuml diagram to a web page:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
||||||
|
import zenuml from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@0.1.0/dist/mermaid-zenuml.esm.min.mjs';
|
||||||
|
await mermaid.registerExternalDiagrams([zenuml]);
|
||||||
|
</script>
|
||||||
|
```
|
@ -2,7 +2,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import dayjs from 'dayjs/esm/index.js';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||||
|
|
||||||
|
@ -263,7 +263,10 @@ export const cleanUpSvgCode = (
|
|||||||
|
|
||||||
// Replace marker-end urls with just the # anchor (remove the preceding part of the URL)
|
// Replace marker-end urls with just the # anchor (remove the preceding part of the URL)
|
||||||
if (!useArrowMarkerUrls && !inSandboxMode) {
|
if (!useArrowMarkerUrls && !inSandboxMode) {
|
||||||
cleanedUpSvg = cleanedUpSvg.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#');
|
cleanedUpSvg = cleanedUpSvg.replace(
|
||||||
|
/marker-end="url\([\d+./:=?A-Za-z-]*?#/g,
|
||||||
|
'marker-end="url(#'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanedUpSvg = decodeEntities(cleanedUpSvg);
|
cleanedUpSvg = decodeEntities(cleanedUpSvg);
|
||||||
|
@ -78,6 +78,22 @@ function createTspan(textElement, lineIndex, lineHeight) {
|
|||||||
.attr('dy', lineHeight + 'em');
|
.attr('dy', lineHeight + 'em');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the width of rendered text
|
||||||
|
* @param {object} parentNode
|
||||||
|
* @param {number} lineHeight
|
||||||
|
* @param {string} text
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function computeWidthOfText(parentNode, lineHeight, text) {
|
||||||
|
const testElement = parentNode.append('text');
|
||||||
|
const testSpan = createTspan(testElement, 1, lineHeight);
|
||||||
|
updateTextContentAndStyles(testSpan, [{ content: text, type: 'normal' }]);
|
||||||
|
const textLength = testSpan.node().getComputedTextLength();
|
||||||
|
testElement.remove();
|
||||||
|
return textLength;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a formatted text element by breaking lines and applying styles based on
|
* Creates a formatted text element by breaking lines and applying styles based on
|
||||||
* the given structuredText.
|
* the given structuredText.
|
||||||
@ -95,31 +111,44 @@ function createFormattedText(width, g, structuredText, addBackground = false) {
|
|||||||
// .attr('dominant-baseline', 'middle')
|
// .attr('dominant-baseline', 'middle')
|
||||||
// .attr('text-anchor', 'middle');
|
// .attr('text-anchor', 'middle');
|
||||||
// .attr('text-anchor', 'middle');
|
// .attr('text-anchor', 'middle');
|
||||||
let lineIndex = -1;
|
let lineIndex = 0;
|
||||||
structuredText.forEach((line) => {
|
structuredText.forEach((line) => {
|
||||||
lineIndex++;
|
/**
|
||||||
let tspan = createTspan(textElement, lineIndex, lineHeight);
|
* Preprocess raw string content of line data
|
||||||
|
* Creating an array of strings pre-split to satisfy width limit
|
||||||
let words = [...line].reverse();
|
*/
|
||||||
let currentWord;
|
let fullStr = line.map((data) => data.content).join(' ');
|
||||||
let wrappedLine = [];
|
let tempStr = '';
|
||||||
|
let linesUnderWidth = [];
|
||||||
while (words.length) {
|
let prevIndex = 0;
|
||||||
currentWord = words.pop();
|
if (computeWidthOfText(labelGroup, lineHeight, fullStr) <= width) {
|
||||||
wrappedLine.push(currentWord);
|
linesUnderWidth.push(fullStr);
|
||||||
|
} else {
|
||||||
updateTextContentAndStyles(tspan, wrappedLine);
|
for (let i = 0; i <= fullStr.length; i++) {
|
||||||
|
tempStr = fullStr.slice(prevIndex, i);
|
||||||
if (tspan.node().getComputedTextLength() > width) {
|
log.info(tempStr, prevIndex, i);
|
||||||
wrappedLine.pop();
|
if (computeWidthOfText(labelGroup, lineHeight, tempStr) > width) {
|
||||||
words.push(currentWord);
|
const subStr = fullStr.slice(prevIndex, i);
|
||||||
|
// Break at space if any
|
||||||
updateTextContentAndStyles(tspan, wrappedLine);
|
const lastSpaceIndex = subStr.lastIndexOf(' ');
|
||||||
|
if (lastSpaceIndex > -1) {
|
||||||
wrappedLine = [];
|
i = prevIndex + lastSpaceIndex + 1;
|
||||||
lineIndex++;
|
|
||||||
tspan = createTspan(textElement, lineIndex, lineHeight);
|
|
||||||
}
|
}
|
||||||
|
linesUnderWidth.push(fullStr.slice(prevIndex, i).trim());
|
||||||
|
prevIndex = i;
|
||||||
|
tempStr = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tempStr != null) {
|
||||||
|
linesUnderWidth.push(tempStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Add each prepared line as a tspan to the parent node */
|
||||||
|
const preparedLines = linesUnderWidth.map((w) => ({ content: w, type: line.type }));
|
||||||
|
for (const preparedLine of preparedLines) {
|
||||||
|
let tspan = createTspan(textElement, lineIndex, lineHeight);
|
||||||
|
updateTextContentAndStyles(tspan, [preparedLine]);
|
||||||
|
lineIndex++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (addBackground) {
|
if (addBackground) {
|
||||||
|
@ -70,7 +70,6 @@ export const setupGraphViewbox = function (graph, svgElem, padding, useMaxWidth)
|
|||||||
height = sHeight + padding * 2;
|
height = sHeight + padding * 2;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// width =
|
|
||||||
log.info(`Calculated bounds: ${width}x${height}`);
|
log.info(`Calculated bounds: ${width}x${height}`);
|
||||||
configureSvgSize(svgElem, height, width, useMaxWidth);
|
configureSvgSize(svgElem, height, width, useMaxWidth);
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ import assignWithDepth from './assignWithDepth.js';
|
|||||||
import { MermaidConfig } from './config.type.js';
|
import { MermaidConfig } from './config.type.js';
|
||||||
import memoize from 'lodash-es/memoize.js';
|
import memoize from 'lodash-es/memoize.js';
|
||||||
|
|
||||||
|
export const ZERO_WIDTH_SPACE = '\u200b';
|
||||||
|
|
||||||
// Effectively an enum of the supported curve types, accessible by name
|
// Effectively an enum of the supported curve types, accessible by name
|
||||||
const d3CurveTypes = {
|
const d3CurveTypes = {
|
||||||
curveBasis: curveBasis,
|
curveBasis: curveBasis,
|
||||||
@ -765,7 +767,7 @@ export const calculateTextDimensions: (
|
|||||||
const dim = { width: 0, height: 0, lineHeight: 0 };
|
const dim = { width: 0, height: 0, lineHeight: 0 };
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const textObj = getTextObj();
|
const textObj = getTextObj();
|
||||||
textObj.text = line;
|
textObj.text = line || ZERO_WIDTH_SPACE;
|
||||||
const textElem = drawSimpleText(g, textObj)
|
const textElem = drawSimpleText(g, textObj)
|
||||||
.style('font-size', _fontSizePx)
|
.style('font-size', _fontSizePx)
|
||||||
.style('font-weight', fontWeight)
|
.style('font-weight', fontWeight)
|
||||||
|
552
pnpm-lock.yaml
generated
552
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user