mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
Merge branch 'develop' into pr/MikeJeffers/4416
* develop: (45 commits) Add nextra to cSpell Update docs: Added Nextra to Blogs category on integrations page Render empty lines correctly Restore classes on edges for elk Update diagram proposal Update docs Added CKEditor and GitHub Writer to available integrations. Fix exceptions for empty lines chore(deps): update all patch dependencies build(deps): fix broken pnpm-lock.yaml file Mermaid version 10.2.0 Mermaid Version 10.2.0-rc.4 Label background fix Test commit Fix for regression error in sequenceDiagrams Update all minor dependencies Update all patch dependencies Update docs Add contributors profile url ignore ZenUML types ...
This commit is contained in:
commit
88b94dea66
@ -5,3 +5,4 @@ cypress.config.js
|
||||
cypress/plugins/index.js
|
||||
coverage
|
||||
*.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:
|
||||
- 'Status: Triage'
|
||||
- 'Type: Enhancement'
|
||||
- 'Type: New Diagram'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
@ -17,6 +18,14 @@ body:
|
||||
- Use a clear and concise title
|
||||
- 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!
|
||||
|
||||
## 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
|
||||
attributes:
|
||||
label: Proposal
|
||||
@ -35,8 +44,17 @@ body:
|
||||
description: If applicable, add screenshots to show possible examples of how the diagram may look like.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Code Sample
|
||||
label: Syntax
|
||||
description: |-
|
||||
If applicable, add a code sample for how to implement this new diagram.
|
||||
The text will automatically be rendered as JavaScript code.
|
||||
render: javascript
|
||||
If possible, include a syntax which could be used to write the diagram.
|
||||
Try to add one or two examples of valid use-cases here.
|
||||
- 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.
|
||||
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
||||
if ! pnpm run lint; then
|
||||
# print a nice error message on lint failure
|
||||
ERROR_MESSAGE='Running `pnpm run lint` failed.'
|
||||
ERROR_MESSAGE+=' Running `pnpm run lint:fix` may fix this issue. '
|
||||
ERROR_MESSAGE+=' Running `pnpm -w run lint:fix` may fix this issue. '
|
||||
ERROR_MESSAGE+=" If this error doesn't occur on your local machine,"
|
||||
ERROR_MESSAGE+=' make sure your packages are up-to-date by running `pnpm install`.'
|
||||
ERROR_MESSAGE+=' You may also need to delete your prettier cache by running'
|
||||
|
@ -44,6 +44,11 @@ const packageOptions = {
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-zenuml': {
|
||||
name: 'mermaid-zenuml',
|
||||
packageName: 'mermaid-zenuml',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
};
|
||||
|
||||
interface BuildOptions {
|
||||
@ -146,6 +151,7 @@ if (watch) {
|
||||
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
||||
if (!mermaidOnly) {
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
||||
}
|
||||
} else if (visualize) {
|
||||
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
|
||||
|
@ -15,6 +15,7 @@ async function createServer() {
|
||||
|
||||
app.use(cors());
|
||||
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(vite.middlewares);
|
||||
app.use(express.static('demos'));
|
||||
|
@ -7,6 +7,7 @@
|
||||
"alois",
|
||||
"aloisklink",
|
||||
"antiscript",
|
||||
"antlr",
|
||||
"appli",
|
||||
"applitools",
|
||||
"asciidoctor",
|
||||
@ -83,6 +84,7 @@
|
||||
"mkdocs",
|
||||
"mmorel",
|
||||
"mult",
|
||||
"nextra",
|
||||
"orlandoni",
|
||||
"pathe",
|
||||
"pbrolin",
|
||||
@ -134,7 +136,8 @@
|
||||
"vitepress",
|
||||
"vueuse",
|
||||
"xlink",
|
||||
"yash"
|
||||
"yash",
|
||||
"zenuml"
|
||||
],
|
||||
"patterns": [
|
||||
{ "name": "Markdown links", "pattern": "\\((.*)\\)", "description": "" },
|
||||
|
@ -684,6 +684,20 @@ A --> B
|
||||
{ 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('html labels', () => {
|
||||
it('With styling and classes', () => {
|
||||
|
@ -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', () => {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
@ -42,368 +42,86 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart LR
|
||||
subgraph one
|
||||
direction LR
|
||||
A[myClass1] --> B[default]
|
||||
subgraph two
|
||||
direction BT
|
||||
C[myClass2] --> D[default]
|
||||
end
|
||||
end
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||
flowchart LR
|
||||
b("`The dog in **the** hog.(1).. a a a a *very long text* about it
|
||||
Word!
|
||||
|
||||
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words.`") --apa--> c
|
||||
|
||||
</pre
|
||||
>
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
classDiagram-v2
|
||||
|
||||
classA -- classB : Inheritance
|
||||
classA -- classC : link
|
||||
classC -- classD : link
|
||||
classB -- classD
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart LR
|
||||
classDef aPID stroke:#4e4403,fill:#fdde29,color:#4e4403,rx:5px,ry:5px;
|
||||
classDef crm stroke:#333333,fill:#DCDCDC,color:#333333,rx:5px,ry:5px;
|
||||
classDef type stroke:#502604,fill:#FAB565,color:#502604,rx:20px,ry:20px;;
|
||||
|
||||
O0("Joe")
|
||||
class O0 aPID;
|
||||
|
||||
O1("Person")
|
||||
class O1 crm;
|
||||
O0 -- has type -->O1["Person"]
|
||||
|
||||
O2("aat:300411314")
|
||||
class O2 type;
|
||||
click O2 function "Sorry the newline html tags are not being processed correctly<br/> So all of this appears on the <br/> same line."
|
||||
O0 -- has type -->O2["Bug"]
|
||||
click O0 function "Lots of great info about Joe<br>Lots of great info about Joe<br>burt<br>fred";
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart TD
|
||||
subgraph test
|
||||
direction TB
|
||||
subgraph test2
|
||||
direction LR
|
||||
F --> D
|
||||
end
|
||||
subgraph test3
|
||||
direction TB
|
||||
G --> H
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart TD
|
||||
|
||||
release-branch[Create Release Branch]:::relClass
|
||||
develop-branch[Update Develop Branch]:::relClass
|
||||
github-release-draft[GitHub Release Draft]:::relClass
|
||||
trigger-pipeline[Trigger Jenkins pipeline]:::fixClass
|
||||
github-release[GitHub Release]:::postClass
|
||||
|
||||
build-ready --> release-branch
|
||||
build-ready --> develop-branch
|
||||
release-branch --> jenkins-release-build
|
||||
jenkins-release-build --> github-release-draft
|
||||
jenkins-release-build --> install-release
|
||||
install-release --> verify-release
|
||||
jenkins-release-build --> announce
|
||||
github-release-draft --> github-release
|
||||
verify-release --> verify-check
|
||||
verify-check -- Yes --> github-release
|
||||
verify-check -- No --> release-fix
|
||||
release-fix --> release-branch-pr
|
||||
verify-check -- No --> delete-artifacts
|
||||
release-branch-pr --> trigger-pipeline
|
||||
delete-artifacts --> trigger-pipeline
|
||||
trigger-pipeline --> jenkins-release-build
|
||||
|
||||
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart LR
|
||||
a["<strong>Haiya</strong>"]===>b
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart TD
|
||||
A --> B
|
||||
A --> C
|
||||
B --> C
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
flowchart TD
|
||||
A([stadium shape test])
|
||||
A -->|Get money| B([Go shopping])
|
||||
B --> C([Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?])
|
||||
C -->|One| D([Laptop])
|
||||
C -->|Two| E([iPhone])
|
||||
C -->|Three| F([Car<br/>wroom wroom])
|
||||
click A "index.html#link-clicked" "link test"
|
||||
click B testClick "click test"
|
||||
classDef someclass fill:#f96;
|
||||
class A someclass;
|
||||
class C someclass;
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
sequenceDiagram
|
||||
title: My Sequence Diagram Title
|
||||
accTitle: My Acc Sequence Diagram
|
||||
accDescr: My Sequence Diagram Description
|
||||
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
Alice-)John: See you later!
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 50%">
|
||||
graph TD
|
||||
A -->|000| B
|
||||
B -->|111| C
|
||||
|
||||
linkStyle 1 stroke:#ff3,stroke-width:4px,color:red;
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
journey
|
||||
accTitle: My User Journey Diagram
|
||||
accDescr: My User Journey Diagram Description
|
||||
|
||||
title My working day
|
||||
section Go to work
|
||||
Make tea: 5: Me
|
||||
Go upstairs: 3: Me
|
||||
Do work: 1: Me, Cat
|
||||
section Go home
|
||||
Go downstairs: 5: Me
|
||||
Sit down: 5: Me
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
info
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
requirementDiagram
|
||||
accTitle: My req Diagram
|
||||
accDescr: My req Diagram Description
|
||||
|
||||
requirement test_req {
|
||||
id: 1
|
||||
text: the test text.
|
||||
risk: high
|
||||
verifymethod: test
|
||||
}
|
||||
|
||||
functionalRequirement test_req2 {
|
||||
id: 1.1
|
||||
text: the second test text.
|
||||
risk: low
|
||||
verifymethod: inspection
|
||||
}
|
||||
|
||||
performanceRequirement test_req3 {
|
||||
id: 1.2
|
||||
text: the third test text.
|
||||
risk: medium
|
||||
verifymethod: demonstration
|
||||
}
|
||||
|
||||
element test_entity {
|
||||
type: simulation
|
||||
}
|
||||
|
||||
element test_entity2 {
|
||||
type: word doc
|
||||
docRef: reqs/test_entity
|
||||
}
|
||||
|
||||
|
||||
test_entity - satisfies -> test_req2
|
||||
test_req - traces -> test_req2
|
||||
test_req - contains -> test_req3
|
||||
test_req <- copies - test_entity2
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram functionality to mermaid
|
||||
excludes weekends
|
||||
%% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".)
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
Functionality added :milestone, 2014-01-25, 0d
|
||||
|
||||
section Documentation
|
||||
Describe gantt syntax :active, a1, after des1, 3d
|
||||
Add gantt diagram to demo page :after a1 , 20h
|
||||
Add another diagram to demo page :doc1, after a1 , 48h
|
||||
|
||||
section Last section
|
||||
Describe gantt syntax :after doc1, 3d
|
||||
Add gantt diagram to demo page :20h
|
||||
Add another diagram to demo page :48h
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
stateDiagram
|
||||
state Active {
|
||||
Idle
|
||||
}
|
||||
Inactive --> Idle: ACT
|
||||
Active --> Active: LOG
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
flowchart TB
|
||||
accTitle: My flowchart
|
||||
accDescr: My flowchart Description
|
||||
subgraph One
|
||||
a1-->a2-->a3
|
||||
Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
loop Loopy
|
||||
Bob->>Alice: Pasten
|
||||
end
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
sequenceDiagram
|
||||
A ->> B: 1
|
||||
rect rgb(204, 0, 102)
|
||||
break yes
|
||||
rect rgb(0, 204, 204)
|
||||
C ->> C: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
B ->> A: Return
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||
flowchart LR
|
||||
b("`The dog in **the** hog.(1)
|
||||
NL`") --"`1o **bold**`"--> c[new strings svg labels]
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
classDiagram
|
||||
accTitle: My class diagram
|
||||
accDescr: My class diagram Description
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
class Class10 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||
flowchart LR
|
||||
b("`The dog in **the** hog.(1)
|
||||
NL`") --"`1o **bold**`"--> c[new strings html labels]
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
%%{init: {'config': {'wrap': true }}}%%
|
||||
sequenceDiagram
|
||||
participant A as Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
A->>Bob: Hola
|
||||
Bob-->A: Pasten !
|
||||
<pre class="mermaid" style="width: 50%">
|
||||
%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||
flowchart LR
|
||||
b("The dog in the hog.(1)\nNL") --"1o bold"--> c[old strings svg labels]
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
gitGraph
|
||||
commit id: "ZERO"
|
||||
branch develop
|
||||
commit id:"A"
|
||||
checkout main
|
||||
commit id:"ONE"
|
||||
checkout develop
|
||||
commit id:"B"
|
||||
branch featureA
|
||||
commit id:"FIX"
|
||||
commit id: "FIX-2"
|
||||
checkout main
|
||||
commit id:"TWO"
|
||||
cherry-pick id:"A"
|
||||
commit id:"THREE"
|
||||
cherry-pick id:"FIX"
|
||||
checkout develop
|
||||
commit id:"C"
|
||||
merge featureA
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
classDiagram
|
||||
Animal "1" <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <--o Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
</pre>
|
||||
<pre class="mermaid2" style="width: 100%">
|
||||
erDiagram
|
||||
CAR ||--o{ NAMED-DRIVER : allows
|
||||
CAR {
|
||||
string registrationNumber
|
||||
string make
|
||||
string model
|
||||
}
|
||||
PERSON ||--o{ NAMED-DRIVER : is
|
||||
PERSON {
|
||||
string firstName
|
||||
string lastName
|
||||
int age
|
||||
}
|
||||
</pre>
|
||||
|
||||
<!-- <script src="./mermaid.js"></script> -->
|
||||
<script src="./mermaid.js"></script>
|
||||
<script>
|
||||
<script type="module">
|
||||
// import mindmap from '../../packages/mermaid-mindmap/src/detector';
|
||||
// import example from '../../packages/mermaid-example-diagram/src/mermaid-example-diagram.core.mjs';
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
// await mermaid.registerExternalDiagrams([example]);
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
maxTextSize: 900000,
|
||||
// theme: 'forest',
|
||||
startOnLoad: true,
|
||||
securityLevel: 'loose',
|
||||
logLevel: 0,
|
||||
fontFamily: 'courier',
|
||||
flowchart: {
|
||||
// curve: 'curveLinear',
|
||||
useMaxWidth: true,
|
||||
htmlLabels: false,
|
||||
fontFamily: 'courier',
|
||||
// defaultRenderer: 'elk',
|
||||
useMaxWidth: false,
|
||||
// htmlLabels: false,
|
||||
htmlLabels: true,
|
||||
},
|
||||
lazyLoadedDiagrams: ['./mermaid-mindmap-detector.js'],
|
||||
// htmlLabels: false,
|
||||
gantt: {
|
||||
useMaxWidth: false,
|
||||
},
|
||||
sequence: {
|
||||
wrap: true,
|
||||
},
|
||||
useMaxWidth: false,
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
|
||||
function clickByFlow(elemName) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'created-by-click';
|
||||
div.style = 'padding: 20px; background: green; color: white;';
|
||||
div.innerText = 'Clicked By Flow';
|
||||
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
}
|
||||
|
||||
mermaid.parseError = function (err, hash) {
|
||||
console.error('In parse error:');
|
||||
console.error(err);
|
||||
};
|
||||
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
|
||||
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
|
||||
// mermaid.test1('third_fast', 200).then((r) => console.info(r));
|
||||
// mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import mermaid2 from './mermaid.esm.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) {
|
||||
return decodeURIComponent(escape(window.atob(str)));
|
||||
@ -44,7 +45,7 @@ const contentLoaded = async function () {
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
}
|
||||
|
||||
await mermaid2.registerExternalDiagrams([externalExample]);
|
||||
await mermaid2.registerExternalDiagrams([externalExample, zenUml]);
|
||||
mermaid2.initialize(graphObj.mermaid);
|
||||
await mermaid2.run();
|
||||
}
|
||||
|
@ -66,6 +66,9 @@
|
||||
<li>
|
||||
<h2><a href="./state.html">State</a></h2>
|
||||
</li>
|
||||
<li>
|
||||
<h2><a href="./zenuml.html">ZenUML</a></h2>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</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>
|
@ -517,7 +517,7 @@ mermaidAPI.initialize({
|
||||
- About Markpad integration [#323](https://github.com/knsv/mermaid/issues/323)
|
||||
- How to link backwards in flowchart? [#321](https://github.com/knsv/mermaid/issues/321)
|
||||
- Help with editor [#310](https://github.com/knsv/mermaid/issues/310)
|
||||
- \+1 [#293](https://github.com/knsv/mermaid/issues/293)
|
||||
- +1 [#293](https://github.com/knsv/mermaid/issues/293)
|
||||
- Basic chart does not render on Chome, but does in Firefox [#290](https://github.com/knsv/mermaid/issues/290)
|
||||
- Live editor is broken [#285](https://github.com/knsv/mermaid/issues/285)
|
||||
- "No such file or directory" trying to run mermaid 0.5.7 on OS X [#284](https://github.com/knsv/mermaid/issues/284)
|
||||
|
@ -96,7 +96,7 @@ mermaid.initialize(config);
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
|
||||
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667)
|
||||
|
||||
## Functions
|
||||
|
||||
@ -127,7 +127,7 @@ Return the last node appended
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:312](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L312)
|
||||
[mermaidAPI.ts:306](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L306)
|
||||
|
||||
---
|
||||
|
||||
@ -153,7 +153,7 @@ the cleaned up svgCode
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:263](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L263)
|
||||
[mermaidAPI.ts:257](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L257)
|
||||
|
||||
---
|
||||
|
||||
@ -179,7 +179,7 @@ the string with all the user styles
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L192)
|
||||
[mermaidAPI.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L186)
|
||||
|
||||
---
|
||||
|
||||
@ -202,7 +202,7 @@ the string with all the user styles
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:240](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L240)
|
||||
[mermaidAPI.ts:234](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L234)
|
||||
|
||||
---
|
||||
|
||||
@ -229,7 +229,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L176)
|
||||
[mermaidAPI.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L170)
|
||||
|
||||
---
|
||||
|
||||
@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:291](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L291)
|
||||
[mermaidAPI.ts:285](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L285)
|
||||
|
||||
---
|
||||
|
||||
@ -320,4 +320,4 @@ Remove any existing elements from the given document
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:362](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L362)
|
||||
[mermaidAPI.ts:356](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L356)
|
||||
|
@ -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**)
|
||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||
- [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**)
|
||||
- [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**)
|
||||
@ -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-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
||||
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
||||
- [Nextra](https://nextra.site/)
|
||||
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||
|
||||
## 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)
|
||||
- [GNU Nano](https://www.nano-editor.org/)
|
||||
- [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
|
||||
|
||||
|
@ -54,7 +54,45 @@ flowchart LR
|
||||
id1[This is the text in the box]
|
||||
```
|
||||
|
||||
## Graph
|
||||
#### Unicode text
|
||||
|
||||
Use `"` to enclose the unicode text.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
id["This ❤ Unicode"]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
id["This ❤ Unicode"]
|
||||
```
|
||||
|
||||
#### Markdown formatting
|
||||
|
||||
Use double quotes and backticks "\` text \`" to enclose the markdown text.
|
||||
|
||||
```mermaid-example
|
||||
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||
flowchart LR
|
||||
markdown["`This **is** _Markdown_`"]
|
||||
newLines["`Line1
|
||||
Line 2
|
||||
Line 3`"]
|
||||
markdown --> newLines
|
||||
```
|
||||
|
||||
```mermaid
|
||||
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||
flowchart LR
|
||||
markdown["`This **is** _Markdown_`"]
|
||||
newLines["`Line1
|
||||
Line 2
|
||||
Line 3`"]
|
||||
markdown --> newLines
|
||||
```
|
||||
|
||||
### Direction
|
||||
|
||||
This statement declares the direction of the Flowchart.
|
||||
|
||||
@ -82,15 +120,13 @@ flowchart LR
|
||||
Start --> Stop
|
||||
```
|
||||
|
||||
## Flowchart Orientation
|
||||
|
||||
Possible FlowChart orientations are:
|
||||
|
||||
- TB - top to bottom
|
||||
- TD - top-down/ same as top to bottom
|
||||
- BT - bottom to top
|
||||
- RL - right to left
|
||||
- LR - left to right
|
||||
- TB - Top to bottom
|
||||
- TD - Top-down/ same as top to bottom
|
||||
- BT - Bottom to top
|
||||
- RL - Right to left
|
||||
- LR - Left to right
|
||||
|
||||
## Node shapes
|
||||
|
||||
|
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 |
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",
|
||||
"private": true,
|
||||
"version": "10.1.0",
|
||||
"version": "10.2.0",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@8.5.1",
|
||||
@ -18,7 +18,7 @@
|
||||
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
|
||||
"build:mermaid": "pnpm build:vite --mermaid",
|
||||
"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": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
|
||||
"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",
|
||||
"version": "10.2.0-rc.2",
|
||||
"version": "10.2.0",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@ -53,7 +53,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@khanacademy/simple-markdown": "^0.9.0",
|
||||
"cytoscape": "^3.23.0",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.1.0",
|
||||
@ -64,6 +63,7 @@
|
||||
"elkjs": "^0.8.2",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2",
|
||||
"stylis": "^4.1.3",
|
||||
"ts-dedent": "^2.2.0",
|
||||
|
@ -361,72 +361,6 @@ export const drawNote = function (elem, note, conf, diagObj) {
|
||||
};
|
||||
|
||||
export const parseMember = function (text) {
|
||||
// Note: these two regular expressions don't parse the official UML syntax for attributes
|
||||
// and methods. They parse a Java-style syntax of the form
|
||||
// "String name" (for attributes) and "String name(int x)" for methods
|
||||
const fieldRegEx = /^([#+~-])?(\w+)(~\w+~|\[])?\s+(\w+) *([$*])?$/;
|
||||
const methodRegEx = /^([#+|~-])?(\w+) *\( *(.*)\) *([$*])? *(\w*[[\]|~]*\s*\w*~?)$/;
|
||||
|
||||
let fieldMatch = text.match(fieldRegEx);
|
||||
let methodMatch = text.match(methodRegEx);
|
||||
|
||||
if (fieldMatch && !methodMatch) {
|
||||
return buildFieldDisplay(fieldMatch);
|
||||
} else if (methodMatch) {
|
||||
return buildMethodDisplay(methodMatch);
|
||||
} else {
|
||||
return buildLegacyDisplay(text);
|
||||
}
|
||||
};
|
||||
|
||||
const buildFieldDisplay = function (parsedText) {
|
||||
let cssStyle = '';
|
||||
let displayText = '';
|
||||
|
||||
try {
|
||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
|
||||
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3].trim()) : '';
|
||||
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
|
||||
let classifier = parsedText[5] ? parsedText[5].trim() : '';
|
||||
|
||||
displayText = visibility + fieldType + genericType + ' ' + fieldName;
|
||||
cssStyle = parseClassifier(classifier);
|
||||
} catch (err) {
|
||||
displayText = parsedText;
|
||||
}
|
||||
|
||||
return {
|
||||
displayText: displayText,
|
||||
cssStyle: cssStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const buildMethodDisplay = function (parsedText) {
|
||||
let cssStyle = '';
|
||||
let displayText = '';
|
||||
|
||||
try {
|
||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||
let methodName = parsedText[2] ? parsedText[2].trim() : '';
|
||||
let parameters = parsedText[3] ? parseGenericTypes(parsedText[3].trim()) : '';
|
||||
let classifier = parsedText[4] ? parsedText[4].trim() : '';
|
||||
let returnType = parsedText[5] ? ' : ' + parseGenericTypes(parsedText[5]).trim() : '';
|
||||
|
||||
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
||||
cssStyle = parseClassifier(classifier);
|
||||
} catch (err) {
|
||||
displayText = parsedText;
|
||||
}
|
||||
|
||||
return {
|
||||
displayText: displayText,
|
||||
cssStyle: cssStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const buildLegacyDisplay = function (text) {
|
||||
// if for some reason we don't have any match, use old format to parse text
|
||||
let displayText = '';
|
||||
let cssStyle = '';
|
||||
let returnType = '';
|
||||
@ -444,14 +378,15 @@ const buildLegacyDisplay = function (text) {
|
||||
cssStyle = parseClassifier(lastChar);
|
||||
}
|
||||
|
||||
let startIndex = visibility === '' ? 0 : 1;
|
||||
const startIndex = visibility === '' ? 0 : 1;
|
||||
let endIndex = cssStyle === '' ? text.length : text.length - 1;
|
||||
text = text.substring(startIndex, endIndex);
|
||||
|
||||
let methodStart = text.indexOf('(');
|
||||
let methodEnd = text.indexOf(')');
|
||||
const methodStart = text.indexOf('(');
|
||||
const methodEnd = text.indexOf(')');
|
||||
const isMethod = methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length;
|
||||
|
||||
if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) {
|
||||
if (isMethod) {
|
||||
let methodName = text.substring(0, methodStart).trim();
|
||||
|
||||
const parameters = text.substring(methodStart + 1, methodEnd);
|
||||
@ -478,7 +413,7 @@ const buildLegacyDisplay = function (text) {
|
||||
}
|
||||
} else {
|
||||
// finally - if all else fails, just send the text back as written (other than parsing for generic types)
|
||||
displayText = parseGenericTypes(text);
|
||||
displayText = visibility + parseGenericTypes(text);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -717,7 +717,7 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
|
||||
const edgePath = edgesEl
|
||||
.insert('path')
|
||||
.attr('d', curve(points))
|
||||
.attr('class', 'path')
|
||||
.attr('class', 'path ' + edgeData.classes)
|
||||
.attr('fill', 'none');
|
||||
const edgeG = edgesEl.insert('g').attr('class', 'edgeLabel');
|
||||
const edgeWithLabel = select(edgeG.node().appendChild(edge.labelEl));
|
||||
|
@ -1,3 +1,6 @@
|
||||
// import khroma from 'khroma';
|
||||
import * as khroma from 'khroma';
|
||||
|
||||
/** Returns the styles given options */
|
||||
export interface FlowChartStyleOptions {
|
||||
arrowheadColor: string;
|
||||
@ -15,6 +18,18 @@ export interface FlowChartStyleOptions {
|
||||
titleColor: string;
|
||||
}
|
||||
|
||||
const fade = (color: string, opacity: number) => {
|
||||
// @ts-ignore TODO: incorrect types from khroma
|
||||
const channel = khroma.channel;
|
||||
|
||||
const r = channel(color, 'r');
|
||||
const g = channel(color, 'g');
|
||||
const b = channel(color, 'b');
|
||||
|
||||
// @ts-ignore incorrect types from khroma
|
||||
return khroma.rgba(r, g, b, opacity);
|
||||
};
|
||||
|
||||
const getStyles = (options: FlowChartStyleOptions) =>
|
||||
`.label {
|
||||
font-family: ${options.fontFamily};
|
||||
@ -82,6 +97,12 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* For html labels only */
|
||||
.labelBkg {
|
||||
background-color: ${fade(options.edgeLabelBackground, 0.5)};
|
||||
// background-color:
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: ${options.clusterBkg};
|
||||
stroke: ${options.clusterBorder};
|
||||
|
@ -119,10 +119,10 @@ points
|
||||
|
||||
axisDetails
|
||||
: 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);}
|
||||
| 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);}
|
||||
;
|
||||
|
||||
|
@ -93,7 +93,7 @@ describe('Testing quadrantChart jison file', () => {
|
||||
str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
|
||||
text: 'Urgent(* +=[❤ --> ',
|
||||
text: 'Urgent(* +=[❤ ⟶ ',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setXAxisRightText).not.toHaveBeenCalled();
|
||||
@ -131,7 +131,7 @@ describe('Testing quadrantChart jison file', () => {
|
||||
str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤" --> ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
|
||||
text: 'Urgent(* +=[❤ --> ',
|
||||
text: 'Urgent(* +=[❤ ⟶ ',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setYAxisTopText).not.toHaveBeenCalled();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import common from '../common/common.js';
|
||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
||||
import { addFunction } from '../../interactionDb.js';
|
||||
import { parseFontSize } from '../../utils.js';
|
||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
|
||||
export const drawRect = function (elem, rectData) {
|
||||
@ -224,15 +224,16 @@ export const drawText = function (elem, textData) {
|
||||
textElem.attr('dy', dy);
|
||||
}
|
||||
|
||||
const text = line || ZERO_WIDTH_SPACE;
|
||||
if (textData.tspan) {
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x);
|
||||
if (textData.fill !== undefined) {
|
||||
span.attr('fill', textData.fill);
|
||||
}
|
||||
span.text(line);
|
||||
span.text(text);
|
||||
} else {
|
||||
textElem.text(line);
|
||||
textElem.text(text);
|
||||
}
|
||||
if (
|
||||
textData.valign !== undefined &&
|
||||
@ -578,7 +579,7 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
||||
txt.class = 'labelText';
|
||||
|
||||
drawLabel(g, txt);
|
||||
txt = svgDrawCommon.getTextObj();
|
||||
txt = getTextObj();
|
||||
txt.text = loopModel.title;
|
||||
txt.x = loopModel.startx + labelBoxWidth / 2 + (loopModel.stopx - loopModel.startx) / 2;
|
||||
txt.y = loopModel.starty + boxMargin + boxTextMargin;
|
||||
@ -764,6 +765,37 @@ export const insertArrowCrossHead = function (elem) {
|
||||
// this is actual shape for arrowhead
|
||||
};
|
||||
|
||||
export const getTextObj = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: undefined,
|
||||
anchor: undefined,
|
||||
style: '#666',
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
tspan: true,
|
||||
valign: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const getNoteRect = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: '#EDF2AE',
|
||||
stroke: '#666',
|
||||
width: 100,
|
||||
anchor: 'start',
|
||||
height: 100,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
};
|
||||
};
|
||||
|
||||
const _drawTextCandidateFunc = (function () {
|
||||
/**
|
||||
* @param {any} content
|
||||
@ -1004,6 +1036,8 @@ export default {
|
||||
insertDatabaseIcon,
|
||||
insertComputerIcon,
|
||||
insertClockIcon,
|
||||
getTextObj,
|
||||
getNoteRect,
|
||||
popupMenu,
|
||||
popdownMenu,
|
||||
fixLifeLineHeights,
|
||||
|
@ -121,6 +121,7 @@ function sidebarSyntax() {
|
||||
{ text: 'C4C Diagram (Context) Diagram 🦺⚠️', link: '/syntax/c4c' },
|
||||
{ text: 'Mindmaps 🔥', link: '/syntax/mindmap' },
|
||||
{ text: 'Timeline 🔥', link: '/syntax/timeline' },
|
||||
{ text: 'Zenuml 🔥', link: '/syntax/zenuml' },
|
||||
{ text: 'Other Examples', link: '/syntax/examples' },
|
||||
],
|
||||
},
|
||||
|
@ -1,6 +1,10 @@
|
||||
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> => {
|
||||
await init;
|
||||
mermaid.initialize(config);
|
||||
const { svg } = await mermaid.render(id, code);
|
||||
return svg;
|
||||
|
@ -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**)
|
||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||
- [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**)
|
||||
- [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**)
|
||||
@ -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-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
||||
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
||||
- [Nextra](https://nextra.site/)
|
||||
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||
|
||||
## 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)
|
||||
- [GNU Nano](https://www.nano-editor.org/)
|
||||
- [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
|
||||
|
||||
|
@ -20,17 +20,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/carbon": "^1.1.16",
|
||||
"@unocss/reset": "^0.51.8",
|
||||
"@unocss/reset": "^0.52.0",
|
||||
"@vite-pwa/vitepress": "^0.0.5",
|
||||
"@vitejs/plugin-vue": "^4.2.1",
|
||||
"fast-glob": "^3.2.12",
|
||||
"https-localhost": "^4.7.1",
|
||||
"pathe": "^1.1.0",
|
||||
"unocss": "^0.51.8",
|
||||
"unocss": "^0.52.0",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.3.3",
|
||||
"vite-plugin-pwa": "^0.14.7",
|
||||
"vitepress": "1.0.0-alpha.75",
|
||||
"vite-plugin-pwa": "^0.15.0",
|
||||
"vitepress": "1.0.0-beta.1",
|
||||
"workbox-window": "^6.5.4"
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,30 @@ flowchart LR
|
||||
id1[This is the text in the box]
|
||||
```
|
||||
|
||||
## Graph
|
||||
#### Unicode text
|
||||
|
||||
Use `"` to enclose the unicode text.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
id["This ❤ Unicode"]
|
||||
```
|
||||
|
||||
#### Markdown formatting
|
||||
|
||||
Use double quotes and backticks "\` text \`" to enclose the markdown text.
|
||||
|
||||
```mermaid-example
|
||||
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||
flowchart LR
|
||||
markdown["`This **is** _Markdown_`"]
|
||||
newLines["`Line1
|
||||
Line 2
|
||||
Line 3`"]
|
||||
markdown --> newLines
|
||||
```
|
||||
|
||||
### Direction
|
||||
|
||||
This statement declares the direction of the Flowchart.
|
||||
|
||||
@ -57,15 +80,13 @@ flowchart LR
|
||||
Start --> Stop
|
||||
```
|
||||
|
||||
## Flowchart Orientation
|
||||
|
||||
Possible FlowChart orientations are:
|
||||
|
||||
- TB - top to bottom
|
||||
- TD - top-down/ same as top to bottom
|
||||
- BT - bottom to top
|
||||
- RL - right to left
|
||||
- LR - left to right
|
||||
- TB - Top to bottom
|
||||
- TD - Top-down/ same as top to bottom
|
||||
- BT - Bottom to top
|
||||
- RL - Right to left
|
||||
- LR - Left to right
|
||||
|
||||
## Node shapes
|
||||
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 255 KiB |
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>
|
||||
```
|
@ -154,13 +154,7 @@ export const encodeEntities = function (text: string): string {
|
||||
* @returns
|
||||
*/
|
||||
export const decodeEntities = function (text: string): string {
|
||||
let txt = text;
|
||||
|
||||
txt = txt.replace(/fl°°/g, '&#');
|
||||
txt = txt.replace(/fl°/g, '&');
|
||||
txt = txt.replace(/¶ß/g, ';');
|
||||
|
||||
return txt;
|
||||
return text.replace(/fl°°/g, '&#').replace(/fl°/g, '&').replace(/¶ß/g, ';');
|
||||
};
|
||||
|
||||
// append !important; to each cssClass followed by a final !important, all enclosed in { }
|
||||
|
@ -1,7 +1,4 @@
|
||||
import { select } from 'd3';
|
||||
import { log } from '../logger.js';
|
||||
import { getConfig } from '../config.js';
|
||||
import { evaluate } from '../diagrams/common/common.js';
|
||||
import { decodeEntities } from '../mermaidAPI.js';
|
||||
import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text.js';
|
||||
/**
|
||||
@ -19,9 +16,10 @@ function applyStyle(dom, styleFn) {
|
||||
* @param {any} node
|
||||
* @param width
|
||||
* @param classes
|
||||
* @param addBackground
|
||||
* @returns {SVGForeignObjectElement} Node
|
||||
*/
|
||||
function addHtmlSpan(element, node, width, classes) {
|
||||
function addHtmlSpan(element, node, width, classes, addBackground = false) {
|
||||
const fo = element.append('foreignObject');
|
||||
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||
@ -32,7 +30,8 @@ function addHtmlSpan(element, node, width, classes) {
|
||||
const label = node.label;
|
||||
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||
div.html(
|
||||
`<span class="${labelClass} ${classes}" ` +
|
||||
`
|
||||
<span class="${labelClass} ${classes}" ` +
|
||||
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
|
||||
'>' +
|
||||
label +
|
||||
@ -44,6 +43,9 @@ function addHtmlSpan(element, node, width, classes) {
|
||||
div.style('white-space', 'nowrap');
|
||||
div.style('max-width', width + 'px');
|
||||
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||
if (addBackground) {
|
||||
div.attr('class', 'labelBkg');
|
||||
}
|
||||
|
||||
let bbox = div.node().getBoundingClientRect();
|
||||
if (bbox.width === width) {
|
||||
@ -232,21 +234,10 @@ export const createText = (
|
||||
),
|
||||
labelStyle: style.replace('fill:', 'color:'),
|
||||
};
|
||||
let vertexNode = addHtmlSpan(el, node, width, classes);
|
||||
let vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
|
||||
return vertexNode;
|
||||
} else {
|
||||
const structuredText = markdownToLines(text);
|
||||
const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
|
||||
let lastWord;
|
||||
structuredText.forEach((line) => {
|
||||
line.forEach((word) => {
|
||||
if (special.includes(word.content) && lastWord) {
|
||||
lastWord.content += word.content;
|
||||
word.content = '';
|
||||
}
|
||||
lastWord = word;
|
||||
});
|
||||
});
|
||||
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
|
||||
return svgLabel;
|
||||
}
|
||||
|
@ -1,61 +1,55 @@
|
||||
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { dedent } from 'ts-dedent';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markdown
|
||||
* @param {string} markdown markdown to process
|
||||
* @returns {string} processed markdown
|
||||
*/
|
||||
function preprocessMarkdown(markdown) {
|
||||
// Replace multiple newlines with a single newline
|
||||
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
|
||||
// Remove extra spaces at the beginning of each line
|
||||
const withoutExtraSpaces = withoutMultipleNewlines.replace(/^\s+/gm, '');
|
||||
const withoutExtraSpaces = dedent(withoutMultipleNewlines);
|
||||
return withoutExtraSpaces;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markdown
|
||||
* @param {string} markdown markdown to split into lines
|
||||
*/
|
||||
export function markdownToLines(markdown) {
|
||||
const preprocessedMarkdown = preprocessMarkdown(markdown);
|
||||
const mdParse = SimpleMarkdown.defaultBlockParse;
|
||||
const syntaxTree = mdParse(preprocessedMarkdown);
|
||||
|
||||
let lines = [[]];
|
||||
const { children } = fromMarkdown(preprocessedMarkdown);
|
||||
const lines = [[]];
|
||||
let currentLine = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param parentType
|
||||
* @param {import('mdast').Content} node
|
||||
* @param {string} [parentType]
|
||||
*/
|
||||
function processNode(node, parentType) {
|
||||
function processNode(node, parentType = 'normal') {
|
||||
if (node.type === 'text') {
|
||||
const textLines = node.content.split('\n');
|
||||
|
||||
const textLines = node.value.split('\n');
|
||||
textLines.forEach((textLine, index) => {
|
||||
if (index !== 0) {
|
||||
currentLine++;
|
||||
lines.push([]);
|
||||
}
|
||||
|
||||
// textLine.split(/ (?=[^!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]+)/).forEach((word) => {
|
||||
textLine.split(' ').forEach((word) => {
|
||||
if (word) {
|
||||
lines[currentLine].push({ content: word, type: parentType || 'normal' });
|
||||
lines[currentLine].push({ content: word, type: parentType });
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (node.type === 'strong' || node.type === 'em') {
|
||||
node.content.forEach((contentNode) => {
|
||||
} else if (node.type === 'strong' || node.type === 'emphasis') {
|
||||
node.children.forEach((contentNode) => {
|
||||
processNode(contentNode, node.type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
syntaxTree.forEach((treeNode) => {
|
||||
children.forEach((treeNode) => {
|
||||
if (treeNode.type === 'paragraph') {
|
||||
treeNode.content.forEach((contentNode) => {
|
||||
treeNode.children.forEach((contentNode) => {
|
||||
processNode(contentNode);
|
||||
});
|
||||
}
|
||||
@ -65,30 +59,27 @@ export function markdownToLines(markdown) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markdown
|
||||
* @param {string} markdown markdown to convert to HTML
|
||||
* @returns {string} HTML
|
||||
*/
|
||||
export function markdownToHTML(markdown) {
|
||||
const mdParse = SimpleMarkdown.defaultBlockParse;
|
||||
const syntaxTree = mdParse(markdown);
|
||||
const { children } = fromMarkdown(markdown);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param {import('mdast').Content} node
|
||||
*/
|
||||
function output(node) {
|
||||
if (node.type === 'text') {
|
||||
return node.content.replace(/\n/g, '<br/>');
|
||||
return node.value.replace(/\n/g, '<br/>');
|
||||
} else if (node.type === 'strong') {
|
||||
return `<strong>${node.content.map(output).join('')}</strong>`;
|
||||
} else if (node.type === 'em') {
|
||||
return `<em>${node.content.map(output).join('')}</em>`;
|
||||
return `<strong>${node.children.map(output).join('')}</strong>`;
|
||||
} else if (node.type === 'emphasis') {
|
||||
return `<em>${node.children.map(output).join('')}</em>`;
|
||||
} else if (node.type === 'paragraph') {
|
||||
return `<p>${node.content.map(output).join('')}</p>`;
|
||||
} else {
|
||||
return '';
|
||||
return `<p>${node.children.map(output).join('')}</p>`;
|
||||
}
|
||||
return `Unsupported markdown: ${node.type}`;
|
||||
}
|
||||
|
||||
return syntaxTree.map(output).join('');
|
||||
return children.map(output).join('');
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// import { test } from 'vitest';
|
||||
import { markdownToLines, markdownToHTML } from './handle-markdown-text';
|
||||
import { test } from 'vitest';
|
||||
import { markdownToLines, markdownToHTML } from './handle-markdown-text.js';
|
||||
import { test, expect } from 'vitest';
|
||||
|
||||
test('markdownToLines - Basic test', () => {
|
||||
const input = `This is regular text
|
||||
@ -37,9 +36,9 @@ Here is a line *with an italic* section`;
|
||||
{ content: 'is', type: 'normal' },
|
||||
{ content: 'a', type: 'normal' },
|
||||
{ content: 'line', type: 'normal' },
|
||||
{ content: 'with', type: 'em' },
|
||||
{ content: 'an', type: 'em' },
|
||||
{ content: 'italic', type: 'em' },
|
||||
{ content: 'with', type: 'emphasis' },
|
||||
{ content: 'an', type: 'emphasis' },
|
||||
{ content: 'italic', type: 'emphasis' },
|
||||
{ content: 'section', type: 'normal' },
|
||||
],
|
||||
];
|
||||
@ -117,7 +116,6 @@ test('markdownToLines - paragraph 1', () => {
|
||||
|
||||
test('markdownToLines - paragraph', () => {
|
||||
const input = `**Start** with
|
||||
|
||||
a second line`;
|
||||
|
||||
const expectedOutput = [
|
||||
@ -144,7 +142,7 @@ test('markdownToLines - Only italic formatting', () => {
|
||||
{ content: 'This', type: 'normal' },
|
||||
{ content: 'is', type: 'normal' },
|
||||
{ content: 'an', type: 'normal' },
|
||||
{ content: 'italic', type: 'em' },
|
||||
{ content: 'italic', type: 'emphasis' },
|
||||
{ content: 'test', type: 'normal' },
|
||||
],
|
||||
];
|
||||
@ -158,7 +156,7 @@ it('markdownToLines - Mixed formatting', () => {
|
||||
|
||||
const expectedOutput = [
|
||||
[
|
||||
{ content: 'Italic', type: 'em' },
|
||||
{ content: 'Italic', type: 'emphasis' },
|
||||
{ content: 'and', type: 'normal' },
|
||||
{ content: 'bold', type: 'strong' },
|
||||
{ content: 'formatting', type: 'normal' },
|
||||
@ -179,21 +177,15 @@ Word!`;
|
||||
{ content: 'dog', type: 'normal' },
|
||||
{ content: 'in', type: 'normal' },
|
||||
{ content: 'the', type: 'strong' },
|
||||
{ content: 'hog', type: 'normal' },
|
||||
{ content: '.', type: 'normal' },
|
||||
{ content: '.', type: 'normal' },
|
||||
{ content: '.', type: 'normal' },
|
||||
{ content: 'hog...', type: 'normal' },
|
||||
{ content: 'a', type: 'normal' },
|
||||
{ content: 'very', type: 'em' },
|
||||
{ content: 'long', type: 'em' },
|
||||
{ content: 'text', type: 'em' },
|
||||
{ content: 'very', type: 'emphasis' },
|
||||
{ content: 'long', type: 'emphasis' },
|
||||
{ content: 'text', type: 'emphasis' },
|
||||
{ content: 'about', type: 'normal' },
|
||||
{ content: 'it', type: 'normal' },
|
||||
],
|
||||
[
|
||||
{ content: 'Word', type: 'normal' },
|
||||
{ content: '!', type: 'normal' },
|
||||
],
|
||||
[{ content: 'Word!', type: 'normal' }],
|
||||
];
|
||||
|
||||
const output = markdownToLines(input);
|
||||
@ -246,8 +238,16 @@ test('markdownToHTML - Only italic formatting', () => {
|
||||
|
||||
test('markdownToHTML - Mixed formatting', () => {
|
||||
const input = `*Italic* and **bold** formatting`;
|
||||
|
||||
const expectedOutput = `<p><em>Italic</em> and <strong>bold</strong> formatting</p>`;
|
||||
const output = markdownToHTML(input);
|
||||
expect(output).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
test('markdownToHTML - Unsupported formatting', () => {
|
||||
expect(
|
||||
markdownToHTML(`Hello
|
||||
- l1
|
||||
- l2
|
||||
- l3`)
|
||||
).toMatchInlineSnapshot('"<p>Hello</p>Unsupported markdown: list"');
|
||||
});
|
@ -70,7 +70,6 @@ export const setupGraphViewbox = function (graph, svgElem, padding, useMaxWidth)
|
||||
height = sHeight + padding * 2;
|
||||
// }
|
||||
|
||||
// width =
|
||||
log.info(`Calculated bounds: ${width}x${height}`);
|
||||
configureSvgSize(svgElem, height, width, useMaxWidth);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { darken, lighten, adjust, invert, isDark } from 'khroma';
|
||||
import { darken, lighten, adjust, invert, isDark, toRgba } from 'khroma';
|
||||
import { mkBorder } from './theme-helpers.js';
|
||||
import {
|
||||
oldAttributeBackgroundColorEven,
|
||||
|
@ -32,6 +32,8 @@ import assignWithDepth from './assignWithDepth.js';
|
||||
import { MermaidConfig } from './config.type.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
|
||||
const d3CurveTypes = {
|
||||
curveBasis: curveBasis,
|
||||
@ -765,7 +767,7 @@ export const calculateTextDimensions: (
|
||||
const dim = { width: 0, height: 0, lineHeight: 0 };
|
||||
for (const line of lines) {
|
||||
const textObj = getTextObj();
|
||||
textObj.text = line;
|
||||
textObj.text = line || ZERO_WIDTH_SPACE;
|
||||
const textElem = drawSimpleText(g, textObj)
|
||||
.style('font-size', _fontSizePx)
|
||||
.style('font-weight', fontWeight)
|
||||
|
1386
pnpm-lock.yaml
generated
1386
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user