mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-18 03:34:12 +01:00
Merge branch 'develop' into patch-1
This commit is contained in:
5
.changeset/angry-bags-brake.md
Normal file
5
.changeset/angry-bags-brake.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'mermaid': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: architecture diagrams no longer grow to extreme heights due to conflicting alignments
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: Jagged edge fix for icon shape
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Add missing TypeScript dependencies
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: Icon color fix for colored icons.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Adding Kanban board, a new diagram type
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'mermaid': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
fix: error `mermaid.parse` on an invalid shape, so that it matches the errors thrown by `mermaid.render`
|
|
||||||
@@ -171,6 +171,58 @@ describe.skip('architecture diagram', () => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render an architecture diagram with a resonable height', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`architecture-beta
|
||||||
|
group federated(cloud)[Federated Environment]
|
||||||
|
service server1(server)[System] in federated
|
||||||
|
service edge(server)[Edge Device] in federated
|
||||||
|
server1:R -- L:edge
|
||||||
|
|
||||||
|
group on_prem(cloud)[Hub]
|
||||||
|
service firewall(server)[Firewall Device] in on_prem
|
||||||
|
service server(server)[Server] in on_prem
|
||||||
|
firewall:R -- L:server
|
||||||
|
|
||||||
|
service db1(database)[db1] in on_prem
|
||||||
|
service db2(database)[db2] in on_prem
|
||||||
|
service db3(database)[db3] in on_prem
|
||||||
|
service db4(database)[db4] in on_prem
|
||||||
|
service db5(database)[db5] in on_prem
|
||||||
|
service db6(database)[db6] in on_prem
|
||||||
|
|
||||||
|
junction mid in on_prem
|
||||||
|
server:B -- T:mid
|
||||||
|
|
||||||
|
junction 1Leftofmid in on_prem
|
||||||
|
1Leftofmid:R -- L:mid
|
||||||
|
1Leftofmid:B -- T:db1
|
||||||
|
|
||||||
|
junction 2Leftofmid in on_prem
|
||||||
|
2Leftofmid:R -- L:1Leftofmid
|
||||||
|
2Leftofmid:B -- T:db2
|
||||||
|
|
||||||
|
junction 3Leftofmid in on_prem
|
||||||
|
3Leftofmid:R -- L:2Leftofmid
|
||||||
|
3Leftofmid:B -- T:db3
|
||||||
|
|
||||||
|
junction 1RightOfMid in on_prem
|
||||||
|
mid:R -- L:1RightOfMid
|
||||||
|
1RightOfMid:B -- T:db4
|
||||||
|
|
||||||
|
junction 2RightOfMid in on_prem
|
||||||
|
1RightOfMid:R -- L:2RightOfMid
|
||||||
|
2RightOfMid:B -- T:db5
|
||||||
|
|
||||||
|
junction 3RightOfMid in on_prem
|
||||||
|
2RightOfMid:R -- L:3RightOfMid
|
||||||
|
3RightOfMid:B -- T:db6
|
||||||
|
|
||||||
|
edge:R -- L:firewall
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Skipped as the layout is not deterministic, and causes issues in E2E tests.
|
// Skipped as the layout is not deterministic, and causes issues in E2E tests.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||||
|
|
||||||
describe.skip('Flowchart ELK', () => {
|
describe('Flowchart ELK', () => {
|
||||||
it('1-elk: should render a simple flowchart', () => {
|
it('1-elk: should render a simple flowchart', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`flowchart-elk TD
|
`flowchart-elk TD
|
||||||
@@ -857,6 +857,49 @@ flowchart LR
|
|||||||
D --> E
|
D --> E
|
||||||
A["A"]
|
A["A"]
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ flowchart: { titleTopMargin: 0 } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('6080: should handle diamond shape intersections', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
subgraph s1["Untitled subgraph"]
|
||||||
|
n1["Evaluate"]
|
||||||
|
n2["Option 1"]
|
||||||
|
n3["Option 2"]
|
||||||
|
n4["fa:fa-car Option 3"]
|
||||||
|
end
|
||||||
|
subgraph s2["Untitled subgraph"]
|
||||||
|
n5["Evaluate"]
|
||||||
|
n6["Option 1"]
|
||||||
|
n7["Option 2"]
|
||||||
|
n8["fa:fa-car Option 3"]
|
||||||
|
end
|
||||||
|
A["Start"] -- Some text --> B("Continue")
|
||||||
|
B --> C{"Evaluate"}
|
||||||
|
C -- One --> D["Option 1"]
|
||||||
|
C -- Two --> E["Option 2"]
|
||||||
|
C -- Three --> F["fa:fa-car Option 3"]
|
||||||
|
n1 -- One --> n2
|
||||||
|
n1 -- Two --> n3
|
||||||
|
n1 -- Three --> n4
|
||||||
|
n5 -- One --> n6
|
||||||
|
n5 -- Two --> n7
|
||||||
|
n5 -- Three --> n8
|
||||||
|
n1@{ shape: diam}
|
||||||
|
n2@{ shape: rect}
|
||||||
|
n3@{ shape: rect}
|
||||||
|
n4@{ shape: rect}
|
||||||
|
n5@{ shape: diam}
|
||||||
|
n6@{ shape: rect}
|
||||||
|
n7@{ shape: rect}
|
||||||
|
n8@{ shape: rect}
|
||||||
|
|
||||||
`,
|
`,
|
||||||
{ flowchart: { titleTopMargin: 0 } }
|
{ flowchart: { titleTopMargin: 0 } }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
|
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
|
<link
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
@@ -85,11 +89,209 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<pre id="diagram4" class="mermaid2">
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
subgraph s1["Untitled subgraph"]
|
||||||
|
n1["Evaluate"]
|
||||||
|
n2["Option 1"]
|
||||||
|
n3["Option 2"]
|
||||||
|
n4["fa:fa-car Option 3"]
|
||||||
|
end
|
||||||
|
n1 -- One --> n2
|
||||||
|
n1 -- Two --> n3
|
||||||
|
n1 -- Three --> n4
|
||||||
|
n5
|
||||||
|
n1@{ shape: diam}
|
||||||
|
n2@{ shape: rect}
|
||||||
|
n3@{ shape: rect}
|
||||||
|
n4@{ shape: rect}
|
||||||
|
A["Start"] -- Some text --> B("Continue")
|
||||||
|
B --> C{"Evaluate"}
|
||||||
|
C -- One --> D["Option 1"]
|
||||||
|
C -- Two --> E["Option 2"]
|
||||||
|
C -- Three --> F["fa:fa-car Option 3"]
|
||||||
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
subgraph s1["Untitled subgraph"]
|
||||||
|
n1["Evaluate"]
|
||||||
|
n2["Option 1"]
|
||||||
|
n3["Option 2"]
|
||||||
|
n4["fa:fa-car Option 3"]
|
||||||
|
end
|
||||||
|
subgraph s2["Untitled subgraph"]
|
||||||
|
n5["Evaluate"]
|
||||||
|
n6["Option 1"]
|
||||||
|
n7["Option 2"]
|
||||||
|
n8["fa:fa-car Option 3"]
|
||||||
|
end
|
||||||
|
A["Start"] -- Some text --> B("Continue")
|
||||||
|
B --> C{"Evaluate"}
|
||||||
|
C -- One --> D["Option 1"]
|
||||||
|
C -- Two --> E["Option 2"]
|
||||||
|
C -- Three --> F["fa:fa-car Option 3"]
|
||||||
|
n1 -- One --> n2
|
||||||
|
n1 -- Two --> n3
|
||||||
|
n1 -- Three --> n4
|
||||||
|
n5 -- One --> n6
|
||||||
|
n5 -- Two --> n7
|
||||||
|
n5 -- Three --> n8
|
||||||
|
n1@{ shape: diam}
|
||||||
|
n2@{ shape: rect}
|
||||||
|
n3@{ shape: rect}
|
||||||
|
n4@{ shape: rect}
|
||||||
|
n5@{ shape: diam}
|
||||||
|
n6@{ shape: rect}
|
||||||
|
n7@{ shape: rect}
|
||||||
|
n8@{ shape: rect}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
subgraph s1["Untitled subgraph"]
|
||||||
|
n1["Evaluate"]
|
||||||
|
n2["Option 1"]
|
||||||
|
end
|
||||||
|
n1 -- One --> n2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
A{A} --> B & C
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
flowchart LR
|
||||||
|
n2@{ shape: rect}
|
||||||
|
n3@{ shape: rect}
|
||||||
|
n4@{ shape: rect}
|
||||||
|
A["Start"] -- Some text --> B("Continue")
|
||||||
|
B --> C{"Evaluate"}
|
||||||
|
C -- One --> D["Option 1"]
|
||||||
|
C -- Two --> E["Option 2"]
|
||||||
|
C -- Three --> F["fa:fa-car Option 3"]
|
||||||
|
%% C@{ shape: hexagon}
|
||||||
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
kanban:
|
||||||
|
ticketBaseUrl: 'https://github.com/your-repo/issues/#TICKET#'
|
||||||
|
---
|
||||||
|
kanban
|
||||||
|
Backlog
|
||||||
|
task1[📝 Define project requirements]@{ ticket: a101 }
|
||||||
|
To Do
|
||||||
|
task2[🔍 Research technologies]@{ ticket: a102 }
|
||||||
|
Review
|
||||||
|
task4[🔍 Code review for login feature]@{ ticket: a104 }
|
||||||
|
Done
|
||||||
|
task5[✅ Deploy initial version]@{ ticket: a105 }
|
||||||
|
In Progress
|
||||||
|
task3[💻 Develop login feature]@{ ticket: 103 }
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||||
|
A:::AClass
|
||||||
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||||
|
A:::AClass
|
||||||
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||||
|
A:::AClass
|
||||||
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||||
|
A:::AClass
|
||||||
|
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||||
|
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
kanban
|
kanban
|
||||||
id2[In progress]
|
id2[In progress]
|
||||||
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
kanban:
|
kanban:
|
||||||
@@ -118,6 +320,30 @@ kanban
|
|||||||
<script type="module">
|
<script type="module">
|
||||||
import mermaid from './mermaid.esm.mjs';
|
import mermaid from './mermaid.esm.mjs';
|
||||||
import layouts from './mermaid-layout-elk.esm.mjs';
|
import layouts from './mermaid-layout-elk.esm.mjs';
|
||||||
|
|
||||||
|
const staticBellIconPack = {
|
||||||
|
prefix: 'fa6-regular',
|
||||||
|
icons: {
|
||||||
|
bell: {
|
||||||
|
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',
|
||||||
|
width: 448,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
};
|
||||||
|
|
||||||
|
mermaid.registerIconPacks([
|
||||||
|
{
|
||||||
|
name: 'logos',
|
||||||
|
loader: () =>
|
||||||
|
fetch('https://unpkg.com/@iconify-json/logos@1/icons.json').then((res) => res.json()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fa',
|
||||||
|
loader: () => staticBellIconPack,
|
||||||
|
},
|
||||||
|
]);
|
||||||
mermaid.registerLayoutLoaders(layouts);
|
mermaid.registerLayoutLoaders(layouts);
|
||||||
mermaid.parseError = function (err, hash) {
|
mermaid.parseError = function (err, hash) {
|
||||||
console.error('Mermaid error: ', err);
|
console.error('Mermaid error: ', err);
|
||||||
@@ -135,7 +361,7 @@ kanban
|
|||||||
// layout: 'dagre',
|
// layout: 'dagre',
|
||||||
// layout: 'elk',
|
// layout: 'elk',
|
||||||
// layout: 'fixed',
|
// layout: 'fixed',
|
||||||
htmlLabels: false,
|
// htmlLabels: false,
|
||||||
flowchart: { titleTopMargin: 10 },
|
flowchart: { titleTopMargin: 10 },
|
||||||
// fontFamily: 'Caveat',
|
// fontFamily: 'Caveat',
|
||||||
// fontFamily: 'Kalam',
|
// fontFamily: 'Kalam',
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ graph TB
|
|||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import mermaid from '/mermaid.esm.mjs';
|
import mermaid from '/mermaid.esm.mjs';
|
||||||
import flowchartELK from '/mermaid-flowchart-elk.esm.mjs';
|
import layouts from '/mermaid-layout-elk.esm.mjs';
|
||||||
await mermaid.registerExternalDiagrams([flowchartELK]);
|
mermaid.registerLayoutLoaders(layouts);
|
||||||
async function render(str) {
|
async function render(str) {
|
||||||
const { svg } = await mermaid.render('dynamic', str);
|
const { svg } = await mermaid.render('dynamic', str);
|
||||||
document.getElementById('dynamicDiagram').innerHTML = svg;
|
document.getElementById('dynamicDiagram').innerHTML = svg;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/rendering-util/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L128)
|
[packages/mermaid/src/rendering-util/types.ts:144](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L144)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/rendering-util/types.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L127)
|
[packages/mermaid/src/rendering-util/types.ts:143](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L143)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -40,4 +40,4 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/rendering-util/types.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L126)
|
[packages/mermaid/src/rendering-util/types.ts:142](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L142)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L95)
|
[packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:85](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L85)
|
[packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -63,4 +63,4 @@ The svg code for the rendered graph.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L81)
|
[packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
|
||||||
|
|||||||
@@ -185,8 +185,6 @@ Communication tools and platforms
|
|||||||
- [=Diagram block](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
- [=Diagram block](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
||||||
- [Standard Notes](https://standardnotes.com/)
|
- [Standard Notes](https://standardnotes.com/)
|
||||||
- [Mermaid Extension](https://github.com/nienow/sn-mermaid)
|
- [Mermaid Extension](https://github.com/nienow/sn-mermaid)
|
||||||
- [Sublime Text 3](https://sublimetext.com)
|
|
||||||
- [Mermaid Package](https://packagecontrol.io/packages/Mermaid)
|
|
||||||
- [VS Code](https://code.visualstudio.com/)
|
- [VS Code](https://code.visualstudio.com/)
|
||||||
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
|
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
|
||||||
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)
|
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid-whiteboard" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light" alt="Mermaid Whiteboard - Drag & Drop your Nodes with Mermaid's new Whiteboard! | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/products/mermaid-chart?utm_source=badge-follow&utm_medium=badge&utm_souce=badge-mermaid-chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/follow.svg?product_id=552855&theme=light" alt="Mermaid Chart - A smarter way to create diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,18 @@
|
|||||||
|
|
||||||
# Blog
|
# Blog
|
||||||
|
|
||||||
|
## [Mermaid 11.4 is out: New Features and Kanban Diagramming](https://www.mermaidchart.com/blog/posts/mermaid-11-4-is-out-new-features-and-kanban-diagramming)
|
||||||
|
|
||||||
|
Mermaid 11.4 brings enhanced functionality with the introduction of Kanban diagrams, allowing users to create visual workflows with status columns and task details.
|
||||||
|
|
||||||
|
October 31, 2024 · 2 mins
|
||||||
|
|
||||||
|
## [How To Build an ER Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-build-an-er-diagram-with-mermaid-chart)
|
||||||
|
|
||||||
|
An entity relationship (ER) diagram acts like a blueprint for your database. This makes ER diagrams effective tools for anyone dealing with complex databases, data modeling, and AI model training.
|
||||||
|
|
||||||
|
October 24, 2024 · 4 mins
|
||||||
|
|
||||||
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
|
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
|
||||||
|
|
||||||
24 September 2024 · 5 mins
|
24 September 2024 · 5 mins
|
||||||
|
|||||||
@@ -319,7 +319,6 @@ Below is a comprehensive list of the newly introduced shapes and their correspon
|
|||||||
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
||||||
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
|
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
|
||||||
| Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
|
| Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
|
||||||
| Class Box | Class Box | `classBox` | Class Box | `class-box` |
|
|
||||||
| Collate | Hourglass | `hourglass` | Represents a collate operation | `collate`, `hourglass` |
|
| Collate | Hourglass | `hourglass` | Represents a collate operation | `collate`, `hourglass` |
|
||||||
| Com Link | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
|
| Com Link | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
|
||||||
| Comment | Curly Brace | `brace` | Adds a comment | `brace-l`, `comment` |
|
| Comment | Curly Brace | `brace` | Adds a comment | `brace-l`, `comment` |
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ todo[Todo]
|
|||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams tacketBaseUrl. This can be set as in the the following example:
|
You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams `ticketBaseUrl`. This can be set as in the the following example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
# @mermaid-js/layout-elk
|
# @mermaid-js/layout-elk
|
||||||
|
|
||||||
|
## 0.1.6
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#6081](https://github.com/mermaid-js/mermaid/pull/6081) [`68f41f6`](https://github.com/mermaid-js/mermaid/commit/68f41f685d2afe7d12f63aabf3de0c3461898471) Thanks [@knsv](https://github.com/knsv)! - fix: Elk rendering of Diamond shape intersections
|
||||||
|
|
||||||
|
- Updated dependencies [[`01b5079`](https://github.com/mermaid-js/mermaid/commit/01b5079562ec8d34ce9964910f168873843c68f8), [`1388662`](https://github.com/mermaid-js/mermaid/commit/1388662132cc829f9820c2e9970ae04e2dd90588), [`fe3cffb`](https://github.com/mermaid-js/mermaid/commit/fe3cffbb673a25b81989aacb06e5d0eda35326db)]:
|
||||||
|
- mermaid@11.4.1
|
||||||
|
|
||||||
## 0.1.5
|
## 0.1.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mermaid-js/layout-elk",
|
"name": "@mermaid-js/layout-elk",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"description": "ELK layout engine for mermaid",
|
"description": "ELK layout engine for mermaid",
|
||||||
"module": "dist/mermaid-layout-elk.core.mjs",
|
"module": "dist/mermaid-layout-elk.core.mjs",
|
||||||
"types": "dist/layouts.d.ts",
|
"types": "dist/layouts.d.ts",
|
||||||
|
|||||||
@@ -484,6 +484,8 @@ export const render = async (
|
|||||||
const r3 = a1 * q1.x + b1 * q1.y + c1;
|
const r3 = a1 * q1.x + b1 * q1.y + c1;
|
||||||
const r4 = a1 * q2.x + b1 * q2.y + c1;
|
const r4 = a1 * q2.x + b1 * q2.y + c1;
|
||||||
|
|
||||||
|
const epsilon = 1e-6;
|
||||||
|
|
||||||
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
||||||
// same side of line 1, the line segments do not intersect.
|
// same side of line 1, the line segments do not intersect.
|
||||||
if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
|
if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
|
||||||
@@ -502,7 +504,7 @@ export const render = async (
|
|||||||
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
||||||
// on same side of second line segment, the line segments do
|
// on same side of second line segment, the line segments do
|
||||||
// not intersect.
|
// not intersect.
|
||||||
if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) {
|
if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) {
|
||||||
return /*DON'T_INTERSECT*/;
|
return /*DON'T_INTERSECT*/;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,11 +549,11 @@ export const render = async (
|
|||||||
{ x: x1 - w / 2, y: y1 },
|
{ x: x1 - w / 2, y: y1 },
|
||||||
];
|
];
|
||||||
log.debug(
|
log.debug(
|
||||||
`UIO diamondIntersection calc abc89:
|
`APA16 diamondIntersection calc abc89:
|
||||||
outsidePoint: ${JSON.stringify(outsidePoint)}
|
outsidePoint: ${JSON.stringify(outsidePoint)}
|
||||||
insidePoint : ${JSON.stringify(insidePoint)}
|
insidePoint : ${JSON.stringify(insidePoint)}
|
||||||
node : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`,
|
node-bounds : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`,
|
||||||
polyPoints
|
JSON.stringify(polyPoints)
|
||||||
);
|
);
|
||||||
|
|
||||||
const intersections = [];
|
const intersections = [];
|
||||||
@@ -564,8 +566,8 @@ export const render = async (
|
|||||||
minY = Math.min(minY, entry.y);
|
minY = Math.min(minY, entry.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
// const left = x1 - w / 2;
|
const left = x1 - w / 2 - minX;
|
||||||
// const top = y1 + h / 2;
|
const top = y1 - h / 2 - minY;
|
||||||
|
|
||||||
for (let i = 0; i < polyPoints.length; i++) {
|
for (let i = 0; i < polyPoints.length; i++) {
|
||||||
const p1 = polyPoints[i];
|
const p1 = polyPoints[i];
|
||||||
@@ -573,8 +575,8 @@ export const render = async (
|
|||||||
const intersect = intersectLine(
|
const intersect = intersectLine(
|
||||||
bounds,
|
bounds,
|
||||||
outsidePoint,
|
outsidePoint,
|
||||||
{ x: p1.x, y: p1.y },
|
{ x: left + p1.x, y: top + p1.y },
|
||||||
{ x: p2.x, y: p2.y }
|
{ x: left + p2.x, y: top + p2.y }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (intersect) {
|
if (intersect) {
|
||||||
@@ -753,7 +755,6 @@ export const render = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
log.debug('returning points', points);
|
|
||||||
return points;
|
return points;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -968,17 +969,17 @@ export const render = async (
|
|||||||
startNode.innerHTML
|
startNode.innerHTML
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (startNode.shape === 'diamond') {
|
if (startNode.shape === 'diamond' || startNode.shape === 'diam') {
|
||||||
edge.points.unshift({
|
edge.points.unshift({
|
||||||
x: startNode.x + startNode.width / 2 + offset.x,
|
x: startNode.x + startNode.width / 2 + offset.x,
|
||||||
y: startNode.y + startNode.height / 2 + offset.y,
|
y: startNode.y + startNode.height / 2 + offset.y,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (endNode.shape === 'diamond') {
|
if (endNode.shape === 'diamond' || endNode.shape === 'diam') {
|
||||||
const x = endNode.x + endNode.width / 2 + offset.x;
|
const x = endNode.x + endNode.width / 2 + offset.x;
|
||||||
// Add a point at the center of the diamond
|
// Add a point at the center of the diamond
|
||||||
if (
|
if (
|
||||||
Math.abs(edge.points[edge.points.length - 1].y - endNode.y - offset.y) > 0.001 ||
|
Math.abs(edge.points[edge.points.length - 1].y - endNode.y - offset.y) > 0.01 ||
|
||||||
Math.abs(edge.points[edge.points.length - 1].x - x) > 0.001
|
Math.abs(edge.points[edge.points.length - 1].x - x) > 0.001
|
||||||
) {
|
) {
|
||||||
edge.points.push({
|
edge.points.push({
|
||||||
@@ -997,7 +998,7 @@ export const render = async (
|
|||||||
height: startNode.height,
|
height: startNode.height,
|
||||||
padding: startNode.padding,
|
padding: startNode.padding,
|
||||||
},
|
},
|
||||||
startNode.shape === 'diamond'
|
startNode.shape === 'diamond' || startNode.shape === 'diam'
|
||||||
).reverse();
|
).reverse();
|
||||||
|
|
||||||
edge.points = cutPathAtIntersect(
|
edge.points = cutPathAtIntersect(
|
||||||
@@ -1009,7 +1010,7 @@ export const render = async (
|
|||||||
height: endNode.height,
|
height: endNode.height,
|
||||||
padding: endNode.padding,
|
padding: endNode.padding,
|
||||||
},
|
},
|
||||||
endNode.shape === 'diamond'
|
endNode.shape === 'diamond' || endNode.shape === 'diam'
|
||||||
);
|
);
|
||||||
|
|
||||||
const paths = insertEdge(
|
const paths = insertEdge(
|
||||||
|
|||||||
@@ -1,5 +1,43 @@
|
|||||||
# mermaid
|
# mermaid
|
||||||
|
|
||||||
|
## 11.4.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#6059](https://github.com/mermaid-js/mermaid/pull/6059) [`01b5079`](https://github.com/mermaid-js/mermaid/commit/01b5079562ec8d34ce9964910f168873843c68f8) Thanks [@knsv](https://github.com/knsv)! - fix: Kanban diagrams will not render when adding a number as ticket id or assigned for a task
|
||||||
|
|
||||||
|
- [#6038](https://github.com/mermaid-js/mermaid/pull/6038) [`1388662`](https://github.com/mermaid-js/mermaid/commit/1388662132cc829f9820c2e9970ae04e2dd90588) Thanks [@knsv](https://github.com/knsv)! - fix: Intersection calculations for tilted cylinder/DAS when using handdrawn look. Some random seeds could cause the calculations to break.
|
||||||
|
|
||||||
|
- [#6079](https://github.com/mermaid-js/mermaid/pull/6079) [`fe3cffb`](https://github.com/mermaid-js/mermaid/commit/fe3cffbb673a25b81989aacb06e5d0eda35326db) Thanks [@aloisklink](https://github.com/aloisklink)! - Bump dompurify to `^3.2.1`. This removes the need for `@types/dompurify`.
|
||||||
|
|
||||||
|
## 11.4.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#5999](https://github.com/mermaid-js/mermaid/pull/5999) [`742ad7c`](https://github.com/mermaid-js/mermaid/commit/742ad7c130964df1fb5544e909d9556081285f68) Thanks [@knsv](https://github.com/knsv)! - Adding Kanban board, a new diagram type
|
||||||
|
|
||||||
|
- [#5880](https://github.com/mermaid-js/mermaid/pull/5880) [`bdf145f`](https://github.com/mermaid-js/mermaid/commit/bdf145ffe362462176d9c1e68d5f3ff5c9d962b0) Thanks [@yari-dewalt](https://github.com/yari-dewalt)! - Class diagram changes:
|
||||||
|
|
||||||
|
- Updates the class diagram to the new unified way of rendering.
|
||||||
|
- Includes a new "classBox" shape to be used in diagrams
|
||||||
|
- Other updates such as:
|
||||||
|
- the option to hide the empty members box in class diagrams,
|
||||||
|
- support for handDrawn look,
|
||||||
|
- the introduction of the classDef statement into class diagrams,
|
||||||
|
- support for styling the default class,
|
||||||
|
- support lollipop interfaces.
|
||||||
|
- Includes fixes / additions for #5562 #3139 and #4037
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5937](https://github.com/mermaid-js/mermaid/pull/5937) [`17b7831`](https://github.com/mermaid-js/mermaid/commit/17b783135f9b2b7748b620dbf81d0f56ab4755f1) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Jagged edge fix for icon shape
|
||||||
|
|
||||||
|
- [#5933](https://github.com/mermaid-js/mermaid/pull/5933) [`72d60d2`](https://github.com/mermaid-js/mermaid/commit/72d60d2633584eb59bccdb6cf30b9522db645db2) Thanks [@remcohaszing](https://github.com/remcohaszing)! - Add missing TypeScript dependencies
|
||||||
|
|
||||||
|
- [#5937](https://github.com/mermaid-js/mermaid/pull/5937) [`17b7831`](https://github.com/mermaid-js/mermaid/commit/17b783135f9b2b7748b620dbf81d0f56ab4755f1) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Icon color fix for colored icons.
|
||||||
|
|
||||||
|
- [#6002](https://github.com/mermaid-js/mermaid/pull/6002) [`5fabd41`](https://github.com/mermaid-js/mermaid/commit/5fabd414fbee01e43bf6c900907ffc1511ca7440) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: error `mermaid.parse` on an invalid shape, so that it matches the errors thrown by `mermaid.render`
|
||||||
|
|
||||||
## 11.3.0
|
## 11.3.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "11.3.0",
|
"version": "11.4.1",
|
||||||
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
|
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "./dist/mermaid.core.mjs",
|
"module": "./dist/mermaid.core.mjs",
|
||||||
@@ -71,7 +71,6 @@
|
|||||||
"@iconify/utils": "^2.1.32",
|
"@iconify/utils": "^2.1.32",
|
||||||
"@mermaid-js/parser": "workspace:^",
|
"@mermaid-js/parser": "workspace:^",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/dompurify": "^3.0.5",
|
|
||||||
"cytoscape": "^3.29.2",
|
"cytoscape": "^3.29.2",
|
||||||
"cytoscape-cose-bilkent": "^4.1.0",
|
"cytoscape-cose-bilkent": "^4.1.0",
|
||||||
"cytoscape-fcose": "^2.2.0",
|
"cytoscape-fcose": "^2.2.0",
|
||||||
@@ -79,7 +78,7 @@
|
|||||||
"d3-sankey": "^0.12.3",
|
"d3-sankey": "^0.12.3",
|
||||||
"dagre-d3-es": "7.0.11",
|
"dagre-d3-es": "7.0.11",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"dompurify": "^3.0.11 <3.1.7",
|
"dompurify": "^3.2.1",
|
||||||
"katex": "^0.16.9",
|
"katex": "^0.16.9",
|
||||||
"khroma": "^2.1.0",
|
"khroma": "^2.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ This Markdown should be kept.
|
|||||||
"| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
"| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
|
||||||
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
|
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
|
||||||
| Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` |
|
| Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` |
|
||||||
| Class Box | Class Box | \`classBox\` | Class Box | \`class-box\` |
|
|
||||||
| Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` |
|
| Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` |
|
||||||
| Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` |
|
| Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` |
|
||||||
| Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` |
|
| Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` |
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
setDiagramTitle,
|
setDiagramTitle,
|
||||||
} from '../common/commonDb.js';
|
} from '../common/commonDb.js';
|
||||||
import type {
|
import type {
|
||||||
|
ArchitectureAlignment,
|
||||||
ArchitectureDB,
|
ArchitectureDB,
|
||||||
ArchitectureDirectionPair,
|
ArchitectureDirectionPair,
|
||||||
ArchitectureDirectionPairMap,
|
ArchitectureDirectionPairMap,
|
||||||
@@ -25,6 +26,7 @@ import type {
|
|||||||
ArchitectureState,
|
ArchitectureState,
|
||||||
} from './architectureTypes.js';
|
} from './architectureTypes.js';
|
||||||
import {
|
import {
|
||||||
|
getArchitectureDirectionAlignment,
|
||||||
getArchitectureDirectionPair,
|
getArchitectureDirectionPair,
|
||||||
isArchitectureDirection,
|
isArchitectureDirection,
|
||||||
isArchitectureJunction,
|
isArchitectureJunction,
|
||||||
@@ -211,12 +213,18 @@ const addEdge = function ({
|
|||||||
const getEdges = (): ArchitectureEdge[] => state.records.edges;
|
const getEdges = (): ArchitectureEdge[] => state.records.edges;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current diagram's adjacency list & spatial map.
|
* Returns the current diagram's adjacency list, spatial map, & group alignments.
|
||||||
* If they have not been created, run the algorithms to generate them.
|
* If they have not been created, run the algorithms to generate them.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getDataStructures = () => {
|
const getDataStructures = () => {
|
||||||
if (state.records.dataStructures === undefined) {
|
if (state.records.dataStructures === undefined) {
|
||||||
|
// Tracks how groups are aligned with one another. Generated while creating the adj list
|
||||||
|
const groupAlignments: Record<
|
||||||
|
string,
|
||||||
|
Record<string, Exclude<ArchitectureAlignment, 'bend'>>
|
||||||
|
> = {};
|
||||||
|
|
||||||
// Create an adjacency list of the diagram to perform BFS on
|
// Create an adjacency list of the diagram to perform BFS on
|
||||||
// Outer reduce applied on all services
|
// Outer reduce applied on all services
|
||||||
// Inner reduce applied on the edges for a service
|
// Inner reduce applied on the edges for a service
|
||||||
@@ -224,6 +232,19 @@ const getDataStructures = () => {
|
|||||||
Record<string, ArchitectureDirectionPairMap>
|
Record<string, ArchitectureDirectionPairMap>
|
||||||
>((prevOuter, [id, service]) => {
|
>((prevOuter, [id, service]) => {
|
||||||
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
|
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
|
||||||
|
// track the direction groups connect to one another
|
||||||
|
const lhsGroupId = getNode(edge.lhsId)?.in;
|
||||||
|
const rhsGroupId = getNode(edge.rhsId)?.in;
|
||||||
|
if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) {
|
||||||
|
const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir);
|
||||||
|
if (alignment !== 'bend') {
|
||||||
|
groupAlignments[lhsGroupId] ??= {};
|
||||||
|
groupAlignments[lhsGroupId][rhsGroupId] = alignment;
|
||||||
|
groupAlignments[rhsGroupId] ??= {};
|
||||||
|
groupAlignments[rhsGroupId][lhsGroupId] = alignment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (edge.lhsId === id) {
|
if (edge.lhsId === id) {
|
||||||
// source is LHS
|
// source is LHS
|
||||||
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
|
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
|
||||||
@@ -245,6 +266,7 @@ const getDataStructures = () => {
|
|||||||
// Configuration for the initial pass of BFS
|
// Configuration for the initial pass of BFS
|
||||||
const firstId = Object.keys(adjList)[0];
|
const firstId = Object.keys(adjList)[0];
|
||||||
const visited = { [firstId]: 1 };
|
const visited = { [firstId]: 1 };
|
||||||
|
// If a key is present in this object, it has not been visited
|
||||||
const notVisited = Object.keys(adjList).reduce(
|
const notVisited = Object.keys(adjList).reduce(
|
||||||
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
|
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
|
||||||
{} as Record<string, number>
|
{} as Record<string, number>
|
||||||
@@ -283,6 +305,7 @@ const getDataStructures = () => {
|
|||||||
state.records.dataStructures = {
|
state.records.dataStructures = {
|
||||||
adjList,
|
adjList,
|
||||||
spatialMaps,
|
spatialMaps,
|
||||||
|
groupAlignments,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return state.records.dataStructures;
|
return state.records.dataStructures;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
|||||||
import { getConfigField } from './architectureDb.js';
|
import { getConfigField } from './architectureDb.js';
|
||||||
import { architectureIcons } from './architectureIcons.js';
|
import { architectureIcons } from './architectureIcons.js';
|
||||||
import type {
|
import type {
|
||||||
|
ArchitectureAlignment,
|
||||||
ArchitectureDataStructures,
|
ArchitectureDataStructures,
|
||||||
|
ArchitectureGroupAlignments,
|
||||||
ArchitectureJunction,
|
ArchitectureJunction,
|
||||||
ArchitectureSpatialMap,
|
ArchitectureSpatialMap,
|
||||||
EdgeSingular,
|
EdgeSingular,
|
||||||
@@ -149,25 +151,91 @@ function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlignments(spatialMaps: ArchitectureSpatialMap[]): fcose.FcoseAlignmentConstraint {
|
function getAlignments(
|
||||||
|
db: ArchitectureDB,
|
||||||
|
spatialMaps: ArchitectureSpatialMap[],
|
||||||
|
groupAlignments: ArchitectureGroupAlignments
|
||||||
|
): fcose.FcoseAlignmentConstraint {
|
||||||
|
/**
|
||||||
|
* Flattens the alignment object so nodes in different groups will be in the same alignment array IFF their groups don't connect in a conflicting alignment
|
||||||
|
*
|
||||||
|
* i.e., two groups which connect horizontally should not have nodes with vertical alignments to one another
|
||||||
|
*
|
||||||
|
* See: #5952
|
||||||
|
*
|
||||||
|
* @param alignmentObj - alignment object with the outer key being the row/col # and the inner key being the group name mapped to the nodes on that axis in the group
|
||||||
|
* @param alignmentDir - alignment direction
|
||||||
|
* @returns flattened alignment object with an arbitrary key mapping to nodes in the same row/col
|
||||||
|
*/
|
||||||
|
const flattenAlignments = (
|
||||||
|
alignmentObj: Record<number, Record<string, string[]>>,
|
||||||
|
alignmentDir: ArchitectureAlignment
|
||||||
|
): Record<string, string[]> => {
|
||||||
|
return Object.entries(alignmentObj).reduce(
|
||||||
|
(prev, [dir, alignments]) => {
|
||||||
|
// prev is the mapping of x/y coordinate to an array of the nodes in that row/column
|
||||||
|
let cnt = 0;
|
||||||
|
const arr = Object.entries(alignments); // [group name, array of nodes within the group on axis dir]
|
||||||
|
if (arr.length === 1) {
|
||||||
|
// If only one group exists in the row/column, we don't need to do anything else
|
||||||
|
prev[dir] = arr[0][1];
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < arr.length - 1; i++) {
|
||||||
|
for (let j = i + 1; j < arr.length; j++) {
|
||||||
|
const [aGroupId, aNodeIds] = arr[i];
|
||||||
|
const [bGroupId, bNodeIds] = arr[j];
|
||||||
|
const alignment = groupAlignments[aGroupId]?.[bGroupId]; // Get how the two groups are intended to align (undefined if they aren't)
|
||||||
|
|
||||||
|
if (alignment === alignmentDir) {
|
||||||
|
// If the intended alignment between the two groups is the same as the alignment we are parsing
|
||||||
|
prev[dir] ??= [];
|
||||||
|
prev[dir] = [...prev[dir], ...aNodeIds, ...bNodeIds]; // add the node ids of both groups to the axis array in prev
|
||||||
|
} else if (aGroupId === 'default' || bGroupId === 'default') {
|
||||||
|
// If either of the groups are in the default space (not in a group), use the same behavior as above
|
||||||
|
prev[dir] ??= [];
|
||||||
|
prev[dir] = [...prev[dir], ...aNodeIds, ...bNodeIds];
|
||||||
|
} else {
|
||||||
|
// Otherwise, the nodes in the two groups are not intended to align
|
||||||
|
const keyA = `${dir}-${cnt++}`;
|
||||||
|
prev[keyA] = aNodeIds;
|
||||||
|
const keyB = `${dir}-${cnt++}`;
|
||||||
|
prev[keyB] = bNodeIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
},
|
||||||
|
{} as Record<string, string[]>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const alignments = spatialMaps.map((spatialMap) => {
|
const alignments = spatialMaps.map((spatialMap) => {
|
||||||
const horizontalAlignments: Record<number, string[]> = {};
|
const horizontalAlignments: Record<number, Record<string, string[]>> = {};
|
||||||
const verticalAlignments: Record<number, string[]> = {};
|
const verticalAlignments: Record<number, Record<string, string[]>> = {};
|
||||||
|
|
||||||
// Group service ids in an object with their x and y coordinate as the key
|
// Group service ids in an object with their x and y coordinate as the key
|
||||||
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
|
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
|
||||||
if (!horizontalAlignments[y]) {
|
const nodeGroup = db.getNode(id)?.in ?? 'default';
|
||||||
horizontalAlignments[y] = [];
|
|
||||||
}
|
horizontalAlignments[y] ??= {};
|
||||||
if (!verticalAlignments[x]) {
|
horizontalAlignments[y][nodeGroup] ??= [];
|
||||||
verticalAlignments[x] = [];
|
horizontalAlignments[y][nodeGroup].push(id);
|
||||||
}
|
|
||||||
horizontalAlignments[y].push(id);
|
verticalAlignments[x] ??= {};
|
||||||
verticalAlignments[x].push(id);
|
verticalAlignments[x][nodeGroup] ??= [];
|
||||||
|
verticalAlignments[x][nodeGroup].push(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Merge the values of each object into a list if the inner list has at least 2 elements
|
// Merge the values of each object into a list if the inner list has at least 2 elements
|
||||||
return {
|
return {
|
||||||
horiz: Object.values(horizontalAlignments).filter((arr) => arr.length > 1),
|
horiz: Object.values(flattenAlignments(horizontalAlignments, 'horizontal')).filter(
|
||||||
vert: Object.values(verticalAlignments).filter((arr) => arr.length > 1),
|
(arr) => arr.length > 1
|
||||||
|
),
|
||||||
|
vert: Object.values(flattenAlignments(verticalAlignments, 'vertical')).filter(
|
||||||
|
(arr) => arr.length > 1
|
||||||
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -244,7 +312,8 @@ function layoutArchitecture(
|
|||||||
junctions: ArchitectureJunction[],
|
junctions: ArchitectureJunction[],
|
||||||
groups: ArchitectureGroup[],
|
groups: ArchitectureGroup[],
|
||||||
edges: ArchitectureEdge[],
|
edges: ArchitectureEdge[],
|
||||||
{ spatialMaps }: ArchitectureDataStructures
|
db: ArchitectureDB,
|
||||||
|
{ spatialMaps, groupAlignments }: ArchitectureDataStructures
|
||||||
): Promise<cytoscape.Core> {
|
): Promise<cytoscape.Core> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||||
@@ -318,9 +387,8 @@ function layoutArchitecture(
|
|||||||
addServices(services, cy);
|
addServices(services, cy);
|
||||||
addJunctions(junctions, cy);
|
addJunctions(junctions, cy);
|
||||||
addEdges(edges, cy);
|
addEdges(edges, cy);
|
||||||
|
|
||||||
// Use the spatial map to create alignment arrays for fcose
|
// Use the spatial map to create alignment arrays for fcose
|
||||||
const alignmentConstraint = getAlignments(spatialMaps);
|
const alignmentConstraint = getAlignments(db, spatialMaps, groupAlignments);
|
||||||
|
|
||||||
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
|
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
|
||||||
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
|
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
|
||||||
@@ -454,7 +522,7 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
|
|||||||
await drawServices(db, servicesElem, services);
|
await drawServices(db, servicesElem, services);
|
||||||
drawJunctions(db, servicesElem, junctions);
|
drawJunctions(db, servicesElem, junctions);
|
||||||
|
|
||||||
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
|
const cy = await layoutArchitecture(services, junctions, groups, edges, db, ds);
|
||||||
|
|
||||||
await drawEdges(edgesElem, cy);
|
await drawEdges(edgesElem, cy);
|
||||||
await drawGroups(groupElem, cy);
|
await drawGroups(groupElem, cy);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import type cytoscape from 'cytoscape';
|
|||||||
| Architecture Diagram Types |
|
| Architecture Diagram Types |
|
||||||
\*=======================================*/
|
\*=======================================*/
|
||||||
|
|
||||||
|
export type ArchitectureAlignment = 'vertical' | 'horizontal' | 'bend';
|
||||||
|
|
||||||
export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B';
|
export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B';
|
||||||
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>;
|
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>;
|
||||||
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>;
|
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>;
|
||||||
@@ -170,6 +172,18 @@ export const getArchitectureDirectionXYFactors = function (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getArchitectureDirectionAlignment = function (
|
||||||
|
a: ArchitectureDirection,
|
||||||
|
b: ArchitectureDirection
|
||||||
|
): ArchitectureAlignment {
|
||||||
|
if (isArchitectureDirectionXY(a, b)) {
|
||||||
|
return 'bend';
|
||||||
|
} else if (isArchitectureDirectionX(a)) {
|
||||||
|
return 'horizontal';
|
||||||
|
}
|
||||||
|
return 'vertical';
|
||||||
|
};
|
||||||
|
|
||||||
export interface ArchitectureStyleOptions {
|
export interface ArchitectureStyleOptions {
|
||||||
archEdgeColor: string;
|
archEdgeColor: string;
|
||||||
archEdgeArrowColor: string;
|
archEdgeArrowColor: string;
|
||||||
@@ -249,9 +263,27 @@ export interface ArchitectureDB extends DiagramDB {
|
|||||||
|
|
||||||
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
|
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
|
||||||
export type ArchitectureSpatialMap = Record<string, number[]>;
|
export type ArchitectureSpatialMap = Record<string, number[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the direction that groups connect from.
|
||||||
|
*
|
||||||
|
* **Outer key**: ID of group A
|
||||||
|
*
|
||||||
|
* **Inner key**: ID of group B
|
||||||
|
*
|
||||||
|
* **Value**: 'vertical' or 'horizontal'
|
||||||
|
*
|
||||||
|
* Note: tmp[groupA][groupB] == tmp[groupB][groupA]
|
||||||
|
*/
|
||||||
|
export type ArchitectureGroupAlignments = Record<
|
||||||
|
string,
|
||||||
|
Record<string, Exclude<ArchitectureAlignment, 'bend'>>
|
||||||
|
>;
|
||||||
|
|
||||||
export interface ArchitectureDataStructures {
|
export interface ArchitectureDataStructures {
|
||||||
adjList: ArchitectureAdjacencyList;
|
adjList: ArchitectureAdjacencyList;
|
||||||
spatialMaps: ArchitectureSpatialMap[];
|
spatialMaps: ArchitectureSpatialMap[];
|
||||||
|
groupAlignments: ArchitectureGroupAlignments;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ArchitectureState extends Record<string, unknown> {
|
export interface ArchitectureState extends Record<string, unknown> {
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ const setupDompurifyHooksIfNotSetup = (() => {
|
|||||||
function setupDompurifyHooks() {
|
function setupDompurifyHooks() {
|
||||||
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target';
|
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target';
|
||||||
|
|
||||||
DOMPurify.addHook('beforeSanitizeAttributes', (node: Element) => {
|
DOMPurify.addHook('beforeSanitizeAttributes', (node) => {
|
||||||
if (node.tagName === 'A' && node.hasAttribute('target')) {
|
if (node instanceof Element && node.tagName === 'A' && node.hasAttribute('target')) {
|
||||||
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? '');
|
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => {
|
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
|
||||||
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
|
if (node instanceof Element && node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
|
||||||
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? '');
|
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? '');
|
||||||
node.removeAttribute(TEMPORARY_ATTRIBUTE);
|
node.removeAttribute(TEMPORARY_ATTRIBUTE);
|
||||||
if (node.getAttribute('target') === '_blank') {
|
if (node.getAttribute('target') === '_blank') {
|
||||||
@@ -83,7 +83,6 @@ export const sanitizeText = (text: string, config: MermaidConfig): string => {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
if (config.dompurifyConfig) {
|
if (config.dompurifyConfig) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
||||||
text = DOMPurify.sanitize(sanitizeMore(text, config), config.dompurifyConfig).toString();
|
text = DOMPurify.sanitize(sanitizeMore(text, config), config.dompurifyConfig).toString();
|
||||||
} else {
|
} else {
|
||||||
text = DOMPurify.sanitize(sanitizeMore(text, config), {
|
text = DOMPurify.sanitize(sanitizeMore(text, config), {
|
||||||
|
|||||||
@@ -893,7 +893,7 @@ const addNodeFromVertex = (
|
|||||||
node.cssCompiledStyles = getCompiledStyles(vertex.classes);
|
node.cssCompiledStyles = getCompiledStyles(vertex.classes);
|
||||||
node.cssClasses = vertex.classes.join(' ');
|
node.cssClasses = vertex.classes.join(' ');
|
||||||
} else {
|
} else {
|
||||||
nodes.push({
|
const baseNode = {
|
||||||
id: vertex.id,
|
id: vertex.id,
|
||||||
label: vertex.text,
|
label: vertex.text,
|
||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
@@ -902,10 +902,8 @@ const addNodeFromVertex = (
|
|||||||
cssStyles: vertex.styles,
|
cssStyles: vertex.styles,
|
||||||
cssCompiledStyles: getCompiledStyles(['default', 'node', ...vertex.classes]),
|
cssCompiledStyles: getCompiledStyles(['default', 'node', ...vertex.classes]),
|
||||||
cssClasses: 'default ' + vertex.classes.join(' '),
|
cssClasses: 'default ' + vertex.classes.join(' '),
|
||||||
shape: getTypeFromVertex(vertex),
|
|
||||||
dir: vertex.dir,
|
dir: vertex.dir,
|
||||||
domId: vertex.domId,
|
domId: vertex.domId,
|
||||||
isGroup,
|
|
||||||
look,
|
look,
|
||||||
link: vertex.link,
|
link: vertex.link,
|
||||||
linkTarget: vertex.linkTarget,
|
linkTarget: vertex.linkTarget,
|
||||||
@@ -916,7 +914,20 @@ const addNodeFromVertex = (
|
|||||||
assetWidth: vertex.assetWidth,
|
assetWidth: vertex.assetWidth,
|
||||||
assetHeight: vertex.assetHeight,
|
assetHeight: vertex.assetHeight,
|
||||||
constraint: vertex.constraint,
|
constraint: vertex.constraint,
|
||||||
});
|
};
|
||||||
|
if (isGroup) {
|
||||||
|
nodes.push({
|
||||||
|
...baseNode,
|
||||||
|
isGroup: true,
|
||||||
|
shape: 'rect',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nodes.push({
|
||||||
|
...baseNode,
|
||||||
|
isGroup: false,
|
||||||
|
shape: getTypeFromVertex(vertex),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ const getData = function () {
|
|||||||
shape: 'kanbanItem',
|
shape: 'kanbanItem',
|
||||||
level: item.level,
|
level: item.level,
|
||||||
rx: 5,
|
rx: 5,
|
||||||
|
ry: 5,
|
||||||
cssStyles: ['text-align: left'],
|
cssStyles: ['text-align: left'],
|
||||||
} satisfies KanbanNode;
|
} satisfies KanbanNode;
|
||||||
_nodes.push(childNode);
|
_nodes.push(childNode);
|
||||||
@@ -129,20 +130,21 @@ const addNode = (level: number, id: string, descr: string, type: number, shapeDa
|
|||||||
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
|
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc?.shape) {
|
// if shape is defined in the yaml data, use it if it is a valid shape kanbanItem
|
||||||
|
if (doc?.shape && doc.shape === 'kanbanItem') {
|
||||||
node.shape = doc?.shape;
|
node.shape = doc?.shape;
|
||||||
}
|
}
|
||||||
if (doc?.label) {
|
if (doc?.label) {
|
||||||
node.label = doc?.label;
|
node.label = doc?.label;
|
||||||
}
|
}
|
||||||
if (doc?.icon) {
|
if (doc?.icon) {
|
||||||
node.icon = doc?.icon;
|
node.icon = doc?.icon.toString();
|
||||||
}
|
}
|
||||||
if (doc?.assigned) {
|
if (doc?.assigned) {
|
||||||
node.assigned = doc?.assigned;
|
node.assigned = doc?.assigned.toString();
|
||||||
}
|
}
|
||||||
if (doc?.ticket) {
|
if (doc?.ticket) {
|
||||||
node.ticket = doc?.ticket;
|
node.ticket = doc?.ticket.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doc?.priority) {
|
if (doc?.priority) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type { KanbanDB } from './kanbanTypes.js';
|
|||||||
import defaultConfig from '../../defaultConfig.js';
|
import defaultConfig from '../../defaultConfig.js';
|
||||||
import { insertCluster } from '../../rendering-util/rendering-elements/clusters.js';
|
import { insertCluster } from '../../rendering-util/rendering-elements/clusters.js';
|
||||||
import { insertNode, positionNode } from '../../rendering-util/rendering-elements/nodes.js';
|
import { insertNode, positionNode } from '../../rendering-util/rendering-elements/nodes.js';
|
||||||
|
import type { ClusterNode } from '../../rendering-util/types.js';
|
||||||
|
|
||||||
export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
||||||
log.debug('Rendering kanban diagram\n' + text);
|
log.debug('Rendering kanban diagram\n' + text);
|
||||||
@@ -26,7 +27,10 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
|||||||
sectionsElem.attr('class', 'sections');
|
sectionsElem.attr('class', 'sections');
|
||||||
const nodesElem = svg.append('g');
|
const nodesElem = svg.append('g');
|
||||||
nodesElem.attr('class', 'items');
|
nodesElem.attr('class', 'items');
|
||||||
const sections = data4Layout.nodes.filter((node) => node.isGroup);
|
const sections = data4Layout.nodes.filter(
|
||||||
|
// TODO: TypeScript 5.5 will infer this predicate automatically
|
||||||
|
(node): node is typeof node & ClusterNode => node.isGroup
|
||||||
|
);
|
||||||
let cnt = 0;
|
let cnt = 0;
|
||||||
// TODO set padding
|
// TODO set padding
|
||||||
const padding = 10;
|
const padding = 10;
|
||||||
@@ -60,10 +64,15 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
|||||||
let y = top;
|
let y = top;
|
||||||
const sectionItems = data4Layout.nodes.filter((node) => node.parentId === section.id);
|
const sectionItems = data4Layout.nodes.filter((node) => node.parentId === section.id);
|
||||||
for (const item of sectionItems) {
|
for (const item of sectionItems) {
|
||||||
|
if (item.isGroup) {
|
||||||
|
// Kanban diagrams should not have groups within groups
|
||||||
|
// this should never happen
|
||||||
|
throw new Error('Groups within groups are not allowed in Kanban diagrams');
|
||||||
|
}
|
||||||
item.x = section.x;
|
item.x = section.x;
|
||||||
item.width = WIDTH - 1.5 * padding;
|
item.width = WIDTH - 1.5 * padding;
|
||||||
const nodeEl = await insertNode(nodesElem, item, { config: conf });
|
const nodeEl = await insertNode(nodesElem, item, { config: conf });
|
||||||
const bbox = nodeEl.node().getBBox();
|
const bbox = nodeEl.node()!.getBBox();
|
||||||
item.y = y + bbox.height / 2;
|
item.y = y + bbox.height / 2;
|
||||||
await positionNode(item);
|
await positionNode(item);
|
||||||
y = item.y + bbox.height / 2 + padding / 2;
|
y = item.y + bbox.height / 2 + padding / 2;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="-mt-6 mb-8">
|
|
||||||
<a
|
|
||||||
href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid-whiteboard"
|
|
||||||
target="_blank"
|
|
||||||
><img
|
|
||||||
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light"
|
|
||||||
alt="Mermaid Whiteboard - Drag & Drop your Nodes with Mermaid's new Whiteboard! | Product Hunt"
|
|
||||||
style="width: 250px; height: 54px"
|
|
||||||
width="250"
|
|
||||||
height="54"
|
|
||||||
/></a>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -7,17 +7,21 @@ interface Taglines {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const taglines: Taglines[] = [
|
const taglines: Taglines[] = [
|
||||||
|
{
|
||||||
|
label: 'Explore the Mermaid Whiteboard from the creators of Mermaid',
|
||||||
|
url: 'https://www.mermaidchart.com/whiteboard?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=whiteboard',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Use the Visual Editor in Mermaid Chart to design and build diagrams',
|
label: 'Use the Visual Editor in Mermaid Chart to design and build diagrams',
|
||||||
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=visual_editor',
|
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=visual_editor',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Diagram live with teammates in Mermaid Chart',
|
label: 'Diagram live with teammates in Mermaid Chart',
|
||||||
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=teams',
|
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=teams',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Skip the rough draft with Mermaid AI in Mermaid Chart',
|
label: 'Replace ChatGPT Pro, Mermaid.live, and LucidChart with Mermaid Pro',
|
||||||
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=mermaid_ai',
|
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=AIbundle',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -25,7 +29,7 @@ let index = ref(Math.floor(Math.random() * taglines.length));
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
index.value = (index.value + 1) % taglines.length;
|
index.value = (index.value + 1) % taglines.length;
|
||||||
}, 60_000);
|
}, 40_000);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -41,7 +45,7 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<span class="font-semibold">{{ taglines[index].label }}</span>
|
<span class="font-semibold">{{ taglines[index].label }}</span>
|
||||||
<button class="rounded bg-[#111113] p-1 px-2 text-sm font-semibold tracking-wide">
|
<button class="rounded bg-[#111113] p-1 px-2 text-sm font-semibold tracking-wide">
|
||||||
Try it now
|
Try now
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</transition>
|
</transition>
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import Contributors from '../components/Contributors.vue';
|
|||||||
import HomePage from '../components/HomePage.vue';
|
import HomePage from '../components/HomePage.vue';
|
||||||
// @ts-ignore Type not available
|
// @ts-ignore Type not available
|
||||||
import TopBar from '../components/TopBar.vue';
|
import TopBar from '../components/TopBar.vue';
|
||||||
// @ts-ignore Type not available
|
|
||||||
import ProductHuntBadge from '../components/ProductHuntBadge.vue';
|
|
||||||
import { getRedirect } from './redirect.js';
|
import { getRedirect } from './redirect.js';
|
||||||
// @ts-ignore Type not available
|
// @ts-ignore Type not available
|
||||||
import 'uno.css';
|
import 'uno.css';
|
||||||
@@ -25,7 +23,6 @@ export default {
|
|||||||
return h(Theme.Layout, null, {
|
return h(Theme.Layout, null, {
|
||||||
// Keeping this as comment as it took a lot of time to figure out how to add a component to the top bar.
|
// Keeping this as comment as it took a lot of time to figure out how to add a component to the top bar.
|
||||||
'home-hero-before': () => h(TopBar),
|
'home-hero-before': () => h(TopBar),
|
||||||
'home-hero-info-before': () => h(ProductHuntBadge),
|
|
||||||
'home-features-after': () => h(HomePage),
|
'home-features-after': () => h(HomePage),
|
||||||
'doc-before': () => h(TopBar),
|
'doc-before': () => h(TopBar),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -180,8 +180,6 @@ Communication tools and platforms
|
|||||||
- [=Diagram block](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
- [=Diagram block](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
||||||
- [Standard Notes](https://standardnotes.com/)
|
- [Standard Notes](https://standardnotes.com/)
|
||||||
- [Mermaid Extension](https://github.com/nienow/sn-mermaid)
|
- [Mermaid Extension](https://github.com/nienow/sn-mermaid)
|
||||||
- [Sublime Text 3](https://sublimetext.com)
|
|
||||||
- [Mermaid Package](https://packagecontrol.io/packages/Mermaid)
|
|
||||||
- [VS Code](https://code.visualstudio.com/)
|
- [VS Code](https://code.visualstudio.com/)
|
||||||
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
|
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
|
||||||
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)
|
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid-whiteboard" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light" alt="Mermaid Whiteboard - Drag & Drop your Nodes with Mermaid's new Whiteboard! | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/products/mermaid-chart?utm_source=badge-follow&utm_medium=badge&utm_souce=badge-mermaid-chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/follow.svg?product_id=552855&theme=light" alt="Mermaid Chart - A smarter way to create diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
# Blog
|
# Blog
|
||||||
|
|
||||||
|
## [Mermaid 11.4 is out: New Features and Kanban Diagramming](https://www.mermaidchart.com/blog/posts/mermaid-11-4-is-out-new-features-and-kanban-diagramming)
|
||||||
|
|
||||||
|
Mermaid 11.4 brings enhanced functionality with the introduction of Kanban diagrams, allowing users to create visual workflows with status columns and task details.
|
||||||
|
|
||||||
|
October 31, 2024 · 2 mins
|
||||||
|
|
||||||
|
## [How To Build an ER Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-build-an-er-diagram-with-mermaid-chart)
|
||||||
|
|
||||||
|
An entity relationship (ER) diagram acts like a blueprint for your database. This makes ER diagrams effective tools for anyone dealing with complex databases, data modeling, and AI model training.
|
||||||
|
|
||||||
|
October 24, 2024 · 4 mins
|
||||||
|
|
||||||
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
|
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
|
||||||
|
|
||||||
24 September 2024 · 5 mins
|
24 September 2024 · 5 mins
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ todo[Todo]
|
|||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams tacketBaseUrl. This can be set as in the the following example:
|
You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams `ticketBaseUrl`. This can be set as in the the following example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -455,6 +455,7 @@ const render = async function (
|
|||||||
svgCode = DOMPurify.sanitize(svgCode, {
|
svgCode = DOMPurify.sanitize(svgCode, {
|
||||||
ADD_TAGS: DOMPURIFY_TAGS,
|
ADD_TAGS: DOMPURIFY_TAGS,
|
||||||
ADD_ATTR: DOMPURIFY_ATTR,
|
ADD_ATTR: DOMPURIFY_ATTR,
|
||||||
|
HTML_INTEGRATION_POINTS: { foreignobject: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
|
|||||||
lines.push([]);
|
lines.push([]);
|
||||||
}
|
}
|
||||||
textLine.split(' ').forEach((word) => {
|
textLine.split(' ').forEach((word) => {
|
||||||
|
word = word.replace(/'/g, `'`);
|
||||||
if (word) {
|
if (word) {
|
||||||
lines[currentLine].push({ content: word, type: parentType });
|
lines[currentLine].push({ content: word, type: parentType });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -471,6 +471,13 @@ const shapes = {
|
|||||||
|
|
||||||
let clusterElems = new Map();
|
let clusterElems = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {keyof typeof shapes} ClusterShapeID
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('../types.js').ClusterNode} node - Shape defaults to 'rect'
|
||||||
|
*/
|
||||||
export const insertCluster = async (elem, node) => {
|
export const insertCluster = async (elem, node) => {
|
||||||
const shape = node.shape || 'rect';
|
const shape = node.shape || 'rect';
|
||||||
const cluster = await shapes[shape](elem, node);
|
const cluster = await shapes[shape](elem, node);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import { shapes } from './shapes.js';
|
import { shapes } from './shapes.js';
|
||||||
import type { Node, ShapeRenderOptions } from '../types.js';
|
import type { Node, NonClusterNode, ShapeRenderOptions } from '../types.js';
|
||||||
import type { SVGGroup } from '../../mermaid.js';
|
import type { SVGGroup } from '../../mermaid.js';
|
||||||
import type { D3Selection } from '../../types.js';
|
import type { D3Selection } from '../../types.js';
|
||||||
import type { graphlib } from 'dagre-d3-es';
|
import type { graphlib } from 'dagre-d3-es';
|
||||||
@@ -10,7 +10,11 @@ type NodeElement = D3Selection<SVGAElement> | Awaited<ReturnType<ShapeHandler>>;
|
|||||||
|
|
||||||
const nodeElems = new Map<string, NodeElement>();
|
const nodeElems = new Map<string, NodeElement>();
|
||||||
|
|
||||||
export async function insertNode(elem: SVGGroup, node: Node, renderOptions: ShapeRenderOptions) {
|
export async function insertNode(
|
||||||
|
elem: SVGGroup,
|
||||||
|
node: NonClusterNode,
|
||||||
|
renderOptions: ShapeRenderOptions
|
||||||
|
) {
|
||||||
let newEl: NodeElement | undefined;
|
let newEl: NodeElement | undefined;
|
||||||
let el;
|
let el;
|
||||||
|
|
||||||
|
|||||||
@@ -449,14 +449,6 @@ export const shapesDefs = [
|
|||||||
aliases: ['lined-document'],
|
aliases: ['lined-document'],
|
||||||
handler: linedWaveEdgedRect,
|
handler: linedWaveEdgedRect,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
semanticName: 'Class Box',
|
|
||||||
name: 'Class Box',
|
|
||||||
shortName: 'classBox',
|
|
||||||
description: 'Class Box',
|
|
||||||
aliases: ['class-box'],
|
|
||||||
handler: classBox,
|
|
||||||
},
|
|
||||||
] as const satisfies ShapeDefinition[];
|
] as const satisfies ShapeDefinition[];
|
||||||
|
|
||||||
const generateShapeMap = () => {
|
const generateShapeMap = () => {
|
||||||
@@ -477,8 +469,13 @@ const generateShapeMap = () => {
|
|||||||
icon,
|
icon,
|
||||||
iconRounded,
|
iconRounded,
|
||||||
imageSquare,
|
imageSquare,
|
||||||
kanbanItem,
|
|
||||||
anchor,
|
anchor,
|
||||||
|
|
||||||
|
// Kanban diagram
|
||||||
|
kanbanItem,
|
||||||
|
|
||||||
|
// class diagram
|
||||||
|
classBox,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const entries = [
|
const entries = [
|
||||||
|
|||||||
@@ -26,16 +26,18 @@ export async function iconCircle<T extends SVGGraphicsElement>(
|
|||||||
|
|
||||||
const topLabel = node.pos === 't';
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
const { nodeBorder } = themeVariables;
|
const { nodeBorder, mainBkg } = themeVariables;
|
||||||
const { stylesMap } = compileStyles(node);
|
const { stylesMap } = compileStyles(node);
|
||||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: 'transparent' });
|
const options = userNodeOverrides(node, {});
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
const fill = stylesMap.get('fill');
|
||||||
|
options.stroke = fill ?? mainBkg;
|
||||||
|
|
||||||
const iconElem = shapeSvg.append('g');
|
const iconElem = shapeSvg.append('g');
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export async function iconRounded<T extends SVGGraphicsElement>(
|
|||||||
|
|
||||||
const height = iconSize + halfPadding * 2;
|
const height = iconSize + halfPadding * 2;
|
||||||
const width = iconSize + halfPadding * 2;
|
const width = iconSize + halfPadding * 2;
|
||||||
const { nodeBorder } = themeVariables;
|
const { nodeBorder, mainBkg } = themeVariables;
|
||||||
const { stylesMap } = compileStyles(node);
|
const { stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
const x = -width / 2;
|
const x = -width / 2;
|
||||||
@@ -40,12 +40,14 @@ export async function iconRounded<T extends SVGGraphicsElement>(
|
|||||||
|
|
||||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: 'transparent' });
|
const options = userNodeOverrides(node, {});
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
const fill = stylesMap.get('fill');
|
||||||
|
options.stroke = fill ?? mainBkg;
|
||||||
|
|
||||||
const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options);
|
const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options);
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ export async function iconRounded<T extends SVGGraphicsElement>(
|
|||||||
stroke: 'none',
|
stroke: 'none',
|
||||||
});
|
});
|
||||||
|
|
||||||
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child').attr('class', 'icon-shape2');
|
||||||
const outerShape = shapeSvg.insert(() => outerNode);
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
|
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { log } from '../../../logger.js';
|
|||||||
import { getIconSVG } from '../../icons.js';
|
import { getIconSVG } from '../../icons.js';
|
||||||
import type { Node, ShapeRenderOptions } from '../../types.js';
|
import type { Node, ShapeRenderOptions } from '../../types.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
|
import { createRoundedRectPathD } from './roundedRectPath.js';
|
||||||
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import { labelHelper, updateNodeBounds } from './util.js';
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
import type { D3Selection } from '../../../types.js';
|
import type { D3Selection } from '../../../types.js';
|
||||||
@@ -29,7 +30,7 @@ export async function iconSquare<T extends SVGGraphicsElement>(
|
|||||||
|
|
||||||
const height = iconSize + halfPadding * 2;
|
const height = iconSize + halfPadding * 2;
|
||||||
const width = iconSize + halfPadding * 2;
|
const width = iconSize + halfPadding * 2;
|
||||||
const { nodeBorder } = themeVariables;
|
const { nodeBorder, mainBkg } = themeVariables;
|
||||||
const { stylesMap } = compileStyles(node);
|
const { stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
const x = -width / 2;
|
const x = -width / 2;
|
||||||
@@ -39,14 +40,16 @@ export async function iconSquare<T extends SVGGraphicsElement>(
|
|||||||
|
|
||||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: 'transparent' });
|
const options = userNodeOverrides(node, {});
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
const fill = stylesMap.get('fill');
|
||||||
|
options.stroke = fill ?? mainBkg;
|
||||||
|
|
||||||
const iconNode = rc.rectangle(x, y, width, height, options);
|
const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 0.1), options);
|
||||||
|
|
||||||
const outerWidth = Math.max(width, bbox.width);
|
const outerWidth = Math.max(width, bbox.width);
|
||||||
const outerHeight = height + bbox.height + labelPadding;
|
const outerHeight = height + bbox.height + labelPadding;
|
||||||
|
|||||||
@@ -1,28 +1,33 @@
|
|||||||
import { labelHelper, insertLabel, updateNodeBounds, getNodeClasses } from './util.js';
|
import { labelHelper, insertLabel, updateNodeBounds, getNodeClasses } from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { SVG } from '../../../diagram-api/types.js';
|
|
||||||
import type { Node, KanbanNode, ShapeRenderOptions } from '../../types.js';
|
import type { Node, KanbanNode, ShapeRenderOptions } from '../../types.js';
|
||||||
import { createRoundedRectPathD } from './roundedRectPath.js';
|
import { createRoundedRectPathD } from './roundedRectPath.js';
|
||||||
import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js';
|
import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
import type { D3Selection } from '../../../types.js';
|
||||||
|
|
||||||
const colorFromPriority = (priority: KanbanNode['priority']) => {
|
const colorFromPriority = (priority: NonNullable<KanbanNode['priority']>) => {
|
||||||
switch (priority) {
|
switch (priority) {
|
||||||
case 'Very High':
|
case 'Very High':
|
||||||
return 'red';
|
return 'red';
|
||||||
case 'High':
|
case 'High':
|
||||||
return 'orange';
|
return 'orange';
|
||||||
|
case 'Medium':
|
||||||
|
return null; // no stroke
|
||||||
case 'Low':
|
case 'Low':
|
||||||
return 'blue';
|
return 'blue';
|
||||||
case 'Very Low':
|
case 'Very Low':
|
||||||
return 'lightblue';
|
return 'lightblue';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRenderOptions) => {
|
export async function kanbanItem<T extends SVGGraphicsElement>(
|
||||||
const unknownNode = node as unknown;
|
parent: D3Selection<T>,
|
||||||
const kanbanNode = unknownNode as KanbanNode;
|
// Omit the 'shape' prop since otherwise, it causes a TypeScript circular dependency error
|
||||||
|
kanbanNode: Omit<Node, 'shape'> | Omit<KanbanNode, 'level' | 'shape'>,
|
||||||
|
{ config }: ShapeRenderOptions
|
||||||
|
) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(kanbanNode);
|
const { labelStyles, nodeStyles } = styles2String(kanbanNode);
|
||||||
kanbanNode.labelStyle = labelStyles;
|
kanbanNode.labelStyle = labelStyles || '';
|
||||||
|
|
||||||
const labelPaddingX = 10;
|
const labelPaddingX = 10;
|
||||||
const orgWidth = kanbanNode.width;
|
const orgWidth = kanbanNode.width;
|
||||||
@@ -38,10 +43,10 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende
|
|||||||
let ticketUrl = '';
|
let ticketUrl = '';
|
||||||
let link;
|
let link;
|
||||||
|
|
||||||
if (kanbanNode.ticket && config?.kanban?.ticketBaseUrl) {
|
if ('ticket' in kanbanNode && kanbanNode.ticket && config?.kanban?.ticketBaseUrl) {
|
||||||
ticketUrl = config?.kanban?.ticketBaseUrl.replace('#TICKET#', kanbanNode.ticket);
|
ticketUrl = config?.kanban?.ticketBaseUrl.replace('#TICKET#', kanbanNode.ticket);
|
||||||
link = shapeSvg
|
link = shapeSvg
|
||||||
.insert('svg:a', ':first-child')
|
.insert<SVGAElement>('svg:a', ':first-child')
|
||||||
.attr('class', 'kanban-ticket-link')
|
.attr('class', 'kanban-ticket-link')
|
||||||
.attr('xlink:href', ticketUrl)
|
.attr('xlink:href', ticketUrl)
|
||||||
.attr('target', '_blank');
|
.attr('target', '_blank');
|
||||||
@@ -49,21 +54,29 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende
|
|||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
useHtmlLabels: kanbanNode.useHtmlLabels,
|
useHtmlLabels: kanbanNode.useHtmlLabels,
|
||||||
labelStyle: kanbanNode.labelStyle,
|
labelStyle: kanbanNode.labelStyle || '',
|
||||||
width: kanbanNode.width,
|
width: kanbanNode.width,
|
||||||
icon: kanbanNode.icon,
|
|
||||||
img: kanbanNode.img,
|
img: kanbanNode.img,
|
||||||
padding: kanbanNode.padding,
|
padding: kanbanNode.padding || 8,
|
||||||
centerLabel: false,
|
centerLabel: false,
|
||||||
};
|
};
|
||||||
const { label: labelEl, bbox: bbox2 } = await insertLabel(
|
let labelEl, bbox2;
|
||||||
link ? link : shapeSvg,
|
if (link) {
|
||||||
kanbanNode.ticket || '',
|
({ label: labelEl, bbox: bbox2 } = await insertLabel(
|
||||||
options
|
link,
|
||||||
);
|
('ticket' in kanbanNode && kanbanNode.ticket) || '',
|
||||||
|
options
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
({ label: labelEl, bbox: bbox2 } = await insertLabel(
|
||||||
|
shapeSvg,
|
||||||
|
('ticket' in kanbanNode && kanbanNode.ticket) || '',
|
||||||
|
options
|
||||||
|
));
|
||||||
|
}
|
||||||
const { label: labelElAssigned, bbox: bboxAssigned } = await insertLabel(
|
const { label: labelElAssigned, bbox: bboxAssigned } = await insertLabel(
|
||||||
shapeSvg,
|
shapeSvg,
|
||||||
kanbanNode.assigned || '',
|
('assigned' in kanbanNode && kanbanNode.assigned) || '',
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
kanbanNode.width = orgWidth;
|
kanbanNode.width = orgWidth;
|
||||||
@@ -107,21 +120,23 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende
|
|||||||
: rc.rectangle(x, y, totalWidth, totalHeight, options);
|
: rc.rectangle(x, y, totalWidth, totalHeight, options);
|
||||||
|
|
||||||
rect = shapeSvg.insert(() => roughNode, ':first-child');
|
rect = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
rect.attr('class', 'basic label-container').attr('style', cssStyles);
|
rect.attr('class', 'basic label-container').attr('style', cssStyles ? cssStyles : null);
|
||||||
} else {
|
} else {
|
||||||
rect = shapeSvg.insert('rect', ':first-child');
|
rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
|
||||||
rect
|
rect
|
||||||
.attr('class', 'basic label-container __APA__')
|
.attr('class', 'basic label-container __APA__')
|
||||||
.attr('style', nodeStyles)
|
.attr('style', nodeStyles)
|
||||||
.attr('rx', rx)
|
.attr('rx', rx ?? 5)
|
||||||
.attr('ry', ry)
|
.attr('ry', ry ?? 5)
|
||||||
.attr('x', x)
|
.attr('x', x)
|
||||||
.attr('y', y)
|
.attr('y', y)
|
||||||
.attr('width', totalWidth)
|
.attr('width', totalWidth)
|
||||||
.attr('height', totalHeight);
|
.attr('height', totalHeight);
|
||||||
if (kanbanNode.priority) {
|
|
||||||
const line = shapeSvg.append('line', ':first-child');
|
const priority = 'priority' in kanbanNode && kanbanNode.priority;
|
||||||
|
if (priority) {
|
||||||
|
const line = shapeSvg.append('line');
|
||||||
const lineX = x + 2;
|
const lineX = x + 2;
|
||||||
|
|
||||||
const y1 = y + Math.floor((rx ?? 0) / 2);
|
const y1 = y + Math.floor((rx ?? 0) / 2);
|
||||||
@@ -133,7 +148,7 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende
|
|||||||
.attr('y2', y2)
|
.attr('y2', y2)
|
||||||
|
|
||||||
.attr('stroke-width', '4')
|
.attr('stroke-width', '4')
|
||||||
.attr('stroke', colorFromPriority(kanbanNode.priority));
|
.attr('stroke', colorFromPriority(priority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,4 +160,4 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende
|
|||||||
};
|
};
|
||||||
|
|
||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import intersect from '../intersect/index.js';
|
|||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
import type { D3Selection } from '../../../types.js';
|
import type { D3Selection } from '../../../types.js';
|
||||||
|
import { getConfig } from '../../../config.js';
|
||||||
|
|
||||||
export async function note<T extends SVGGraphicsElement>(
|
export async function note<T extends SVGGraphicsElement>(
|
||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
@@ -12,16 +13,16 @@ export async function note<T extends SVGGraphicsElement>(
|
|||||||
) {
|
) {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
|
const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart?.htmlLabels !== false;
|
||||||
|
if (!useHtmlLabels) {
|
||||||
|
node.centerLabel = true;
|
||||||
|
}
|
||||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
const totalWidth = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
|
const totalWidth = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
|
||||||
const totalHeight = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
|
const totalHeight = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
|
||||||
const x = -totalWidth / 2;
|
const x = -totalWidth / 2;
|
||||||
const y = -totalHeight / 2;
|
const y = -totalHeight / 2;
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
const useHtmlLabels = node.useHtmlLabels;
|
|
||||||
if (!useHtmlLabels) {
|
|
||||||
node.centerLabel = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the rect
|
// add the rect
|
||||||
// @ts-ignore TODO: Fix rough typings
|
// @ts-ignore TODO: Fix rough typings
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export async function tiltedCylinder<T extends SVGGraphicsElement>(
|
|||||||
) {
|
) {
|
||||||
let x = rx * rx * (1 - (y * y) / (ry * ry));
|
let x = rx * rx * (1 - (y * y) / (ry * ry));
|
||||||
if (x != 0) {
|
if (x != 0) {
|
||||||
x = Math.sqrt(x);
|
x = Math.sqrt(Math.abs(x));
|
||||||
}
|
}
|
||||||
x = rx - x;
|
x = rx - x;
|
||||||
if (point.x - (node.x ?? 0) > 0) {
|
if (point.x - (node.x ?? 0) > 0) {
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ import type { D3Selection, Point } from '../../../types.js';
|
|||||||
export const labelHelper = async <T extends SVGGraphicsElement>(
|
export const labelHelper = async <T extends SVGGraphicsElement>(
|
||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
node: Node,
|
node: Node,
|
||||||
_classes?: string,
|
_classes?: string
|
||||||
_shapeSvg?: D3Selection<T>
|
|
||||||
) => {
|
) => {
|
||||||
let cssClasses;
|
let cssClasses;
|
||||||
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
|
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.htmlLabels);
|
||||||
if (!_classes) {
|
if (!_classes) {
|
||||||
cssClasses = 'node default';
|
cssClasses = 'node default';
|
||||||
} else {
|
} else {
|
||||||
@@ -22,12 +21,10 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = _shapeSvg
|
const shapeSvg = parent
|
||||||
? _shapeSvg
|
.insert('g')
|
||||||
: parent
|
.attr('class', cssClasses)
|
||||||
.insert('g')
|
.attr('id', node.domId || node.id);
|
||||||
.attr('class', cssClasses)
|
|
||||||
.attr('id', node.domId || node.id);
|
|
||||||
|
|
||||||
// Create the label and insert it after the rect
|
// Create the label and insert it after the rect
|
||||||
const labelEl = shapeSvg
|
const labelEl = shapeSvg
|
||||||
@@ -55,7 +52,7 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
|||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
const halfPadding = (node?.padding ?? 0) / 2;
|
const halfPadding = (node?.padding ?? 0) / 2;
|
||||||
|
|
||||||
if (evaluate(getConfig().flowchart?.htmlLabels)) {
|
if (useHtmlLabels) {
|
||||||
const div = text.children[0];
|
const div = text.children[0];
|
||||||
const dv = select(text);
|
const dv = select(text);
|
||||||
|
|
||||||
@@ -119,7 +116,7 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
|
|||||||
labelEl.insert('rect', ':first-child');
|
labelEl.insert('rect', ':first-child');
|
||||||
return { shapeSvg, bbox, halfPadding, label: labelEl };
|
return { shapeSvg, bbox, halfPadding, label: labelEl };
|
||||||
};
|
};
|
||||||
export const insertLabel = async (
|
export const insertLabel = async <T extends SVGGraphicsElement>(
|
||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
label: string,
|
label: string,
|
||||||
options: {
|
options: {
|
||||||
@@ -136,7 +133,10 @@ export const insertLabel = async (
|
|||||||
const useHtmlLabels = options.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
|
const useHtmlLabels = options.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
|
||||||
|
|
||||||
// Create the label and insert it after the rect
|
// Create the label and insert it after the rect
|
||||||
const labelEl = parent.insert('g').attr('class', 'label').attr('style', options.labelStyle);
|
const labelEl = parent
|
||||||
|
.insert('g')
|
||||||
|
.attr('class', 'label')
|
||||||
|
.attr('style', options.labelStyle || '');
|
||||||
|
|
||||||
const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export type MarkdownWordType = 'normal' | 'strong' | 'em';
|
export type MarkdownWordType = 'normal' | 'strong' | 'em';
|
||||||
import type { MermaidConfig } from '../config.type.js';
|
import type { MermaidConfig } from '../config.type.js';
|
||||||
|
import type { ClusterShapeID } from './rendering-elements/clusters.js';
|
||||||
import type { ShapeID } from './rendering-elements/shapes.js';
|
import type { ShapeID } from './rendering-elements/shapes.js';
|
||||||
export interface MarkdownWord {
|
export interface MarkdownWord {
|
||||||
content: string;
|
content: string;
|
||||||
@@ -9,8 +10,7 @@ export type MarkdownLine = MarkdownWord[];
|
|||||||
/** Returns `true` if the line fits a constraint (e.g. it's under 𝑛 chars) */
|
/** Returns `true` if the line fits a constraint (e.g. it's under 𝑛 chars) */
|
||||||
export type CheckFitFunction = (text: MarkdownLine) => boolean;
|
export type CheckFitFunction = (text: MarkdownLine) => boolean;
|
||||||
|
|
||||||
// Common properties for any node in the system
|
interface BaseNode {
|
||||||
export interface Node {
|
|
||||||
id: string;
|
id: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
description?: string[];
|
description?: string[];
|
||||||
@@ -38,7 +38,6 @@ export interface Node {
|
|||||||
linkTarget?: string;
|
linkTarget?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
padding?: number; //REMOVE?, use from LayoutData.config - Keep, this could be shape specific
|
padding?: number; //REMOVE?, use from LayoutData.config - Keep, this could be shape specific
|
||||||
shape?: ShapeID;
|
|
||||||
isGroup: boolean;
|
isGroup: boolean;
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
@@ -75,6 +74,22 @@ export interface Node {
|
|||||||
constraint?: 'on' | 'off';
|
constraint?: 'on' | 'off';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group/cluster nodes, e.g. nodes that contain other nodes.
|
||||||
|
*/
|
||||||
|
export interface ClusterNode extends BaseNode {
|
||||||
|
shape?: ClusterShapeID;
|
||||||
|
isGroup: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NonClusterNode extends BaseNode {
|
||||||
|
shape?: ShapeID;
|
||||||
|
isGroup: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common properties for any node in the system
|
||||||
|
export type Node = ClusterNode | NonClusterNode;
|
||||||
|
|
||||||
// Common properties for any edge in the system
|
// Common properties for any edge in the system
|
||||||
export interface Edge {
|
export interface Edge {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -118,9 +133,9 @@ export interface RectOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extending the Node interface for specific types if needed
|
// Extending the Node interface for specific types if needed
|
||||||
export interface ClassDiagramNode extends Node {
|
export type ClassDiagramNode = Node & {
|
||||||
memberData: any; // Specific property for class diagram nodes
|
memberData: any; // Specific property for class diagram nodes
|
||||||
}
|
};
|
||||||
|
|
||||||
// Specific interfaces for layout and render data
|
// Specific interfaces for layout and render data
|
||||||
export interface LayoutData {
|
export interface LayoutData {
|
||||||
@@ -154,11 +169,11 @@ export interface ShapeRenderOptions {
|
|||||||
dir?: Node['dir'];
|
dir?: Node['dir'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KanbanNode extends Node {
|
export type KanbanNode = Node & {
|
||||||
// Kanban specif data
|
// Kanban specif data
|
||||||
priority?: 'Very High' | 'High' | 'Medium' | 'Low' | 'Very Low';
|
priority?: 'Very High' | 'High' | 'Medium' | 'Low' | 'Very Low';
|
||||||
ticket?: string;
|
ticket?: string;
|
||||||
assigned?: string;
|
assigned?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
level: number;
|
level: number;
|
||||||
}
|
};
|
||||||
|
|||||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
@@ -226,9 +226,6 @@ importers:
|
|||||||
'@types/d3':
|
'@types/d3':
|
||||||
specifier: ^7.4.3
|
specifier: ^7.4.3
|
||||||
version: 7.4.3
|
version: 7.4.3
|
||||||
'@types/dompurify':
|
|
||||||
specifier: ^3.0.5
|
|
||||||
version: 3.0.5
|
|
||||||
cytoscape:
|
cytoscape:
|
||||||
specifier: ^3.29.2
|
specifier: ^3.29.2
|
||||||
version: 3.30.2
|
version: 3.30.2
|
||||||
@@ -251,8 +248,8 @@ importers:
|
|||||||
specifier: ^1.11.10
|
specifier: ^1.11.10
|
||||||
version: 1.11.13
|
version: 1.11.13
|
||||||
dompurify:
|
dompurify:
|
||||||
specifier: ^3.0.11 <3.1.7
|
specifier: ^3.2.1
|
||||||
version: 3.1.6
|
version: 3.2.1
|
||||||
katex:
|
katex:
|
||||||
specifier: ^0.16.9
|
specifier: ^0.16.9
|
||||||
version: 0.16.11
|
version: 0.16.11
|
||||||
@@ -2768,9 +2765,6 @@ packages:
|
|||||||
'@types/debug@4.1.12':
|
'@types/debug@4.1.12':
|
||||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||||
|
|
||||||
'@types/dompurify@3.0.5':
|
|
||||||
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
|
|
||||||
|
|
||||||
'@types/estree@0.0.39':
|
'@types/estree@0.0.39':
|
||||||
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
|
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
|
||||||
|
|
||||||
@@ -4720,8 +4714,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
dompurify@3.1.6:
|
dompurify@3.2.1:
|
||||||
resolution: {integrity: sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==}
|
resolution: {integrity: sha512-NBHEsc0/kzRYQd+AY6HR6B/IgsqzBABrqJbpCDQII/OK6h7B7LXzweZTDsqSW2LkTRpoxf18YUP+YjGySk6B3w==}
|
||||||
|
|
||||||
domutils@3.1.0:
|
domutils@3.1.0:
|
||||||
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
|
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
|
||||||
@@ -12125,10 +12119,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/ms': 0.7.34
|
'@types/ms': 0.7.34
|
||||||
|
|
||||||
'@types/dompurify@3.0.5':
|
|
||||||
dependencies:
|
|
||||||
'@types/trusted-types': 2.0.7
|
|
||||||
|
|
||||||
'@types/estree@0.0.39': {}
|
'@types/estree@0.0.39': {}
|
||||||
|
|
||||||
'@types/estree@1.0.6': {}
|
'@types/estree@1.0.6': {}
|
||||||
@@ -12970,7 +12960,7 @@ snapshots:
|
|||||||
antlr4: 4.11.0
|
antlr4: 4.11.0
|
||||||
color-string: 1.9.1
|
color-string: 1.9.1
|
||||||
dom-to-image-more: 2.16.0
|
dom-to-image-more: 2.16.0
|
||||||
dompurify: 3.1.6
|
dompurify: 3.2.1
|
||||||
file-saver: 2.0.5
|
file-saver: 2.0.5
|
||||||
highlight.js: 10.7.3
|
highlight.js: 10.7.3
|
||||||
html-to-image: 1.11.11
|
html-to-image: 1.11.11
|
||||||
@@ -14509,7 +14499,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
|
|
||||||
dompurify@3.1.6: {}
|
dompurify@3.2.1:
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
domutils@3.1.0:
|
domutils@3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ const SRC = {
|
|||||||
// to match the real `package.json` values
|
// to match the real `package.json` values
|
||||||
'type-fest': '*',
|
'type-fest': '*',
|
||||||
'@types/d3': '^7.4.3',
|
'@types/d3': '^7.4.3',
|
||||||
'@types/dompurify': '^3.0.5',
|
|
||||||
typescript: '*',
|
typescript: '*',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user