mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-29 20:29:40 +02:00
Merge from develop
This commit is contained in:
@@ -25,6 +25,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
|
||||
'sankey',
|
||||
'block',
|
||||
'packet',
|
||||
'architecture',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
|
6
.changeset/famous-bananas-join.md
Normal file
6
.changeset/famous-bananas-join.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
Fix for self loops in cluster
|
||||
Supporting legacy defaultRenderer directive
|
9
.changeset/nice-flowers-yawn.md
Normal file
9
.changeset/nice-flowers-yawn.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
'@mermaid-js/docs': patch
|
||||
'@mermaid-js/parser': minor
|
||||
---
|
||||
|
||||
New Diagram: Architecture
|
||||
|
||||
Adds architecture diagrams which allows users to show relations between services.
|
6
.changeset/pants-things-go.md
Normal file
6
.changeset/pants-things-go.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@mermaid-js/layout-elk': patch
|
||||
---
|
||||
|
||||
fix: Updates to the default elk configuration
|
||||
feat: exposing cycleBreakingStrategy to the configuration so that it can be modified suing the configuration.
|
@@ -55,6 +55,7 @@ GENERICTYPE
|
||||
getBoundarys
|
||||
grammr
|
||||
graphtype
|
||||
halign
|
||||
iife
|
||||
interp
|
||||
introdcued
|
||||
@@ -66,6 +67,7 @@ Kaufmann
|
||||
keyify
|
||||
LABELPOS
|
||||
LABELTYPE
|
||||
layoutstop
|
||||
lcov
|
||||
LEFTOF
|
||||
Lexa
|
||||
|
@@ -24,6 +24,7 @@ Doctave
|
||||
DokuWiki
|
||||
dompurify
|
||||
elkjs
|
||||
fcose
|
||||
fontawesome
|
||||
Foswiki
|
||||
Gitea
|
||||
|
@@ -35,7 +35,7 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
|
||||
[](https://www.npmjs.com/package/mermaid)
|
||||
[](https://discord.gg/AgrbSrBer3)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
[](https://argos-ci.com)
|
||||
[](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/mermaid-js/mermaid)
|
||||
|
||||
<img src="./img/header.png" alt="" />
|
||||
@@ -83,6 +83,10 @@ You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-
|
||||
|
||||
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](https://mermaid.js.org/intro/getting-started.html), [Usage](https://mermaid.js.org/config/usage.html) and [Tutorials](https://mermaid.js.org/ecosystem/tutorials.html).
|
||||
|
||||
Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze.
|
||||
|
||||
[](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
|
||||
|
||||
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
|
||||
|
||||
<a href="https://applitools.com/">
|
||||
|
174
cypress/integration/rendering/architecture.spec.ts
Normal file
174
cypress/integration/rendering/architecture.spec.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
|
||||
describe('architecture diagram', () => {
|
||||
it('should render a simple architecture diagram with groups', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
group api(cloud)[API]
|
||||
|
||||
service db(database)[Database] in api
|
||||
service disk1(disk)[Storage] in api
|
||||
service disk2(disk)[Storage] in api
|
||||
service server(server)[Server] in api
|
||||
service gateway(internet)[Gateway]
|
||||
|
||||
db L--R server
|
||||
disk1 T--B server
|
||||
disk2 T--B db
|
||||
server T--B gateway
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with groups within groups', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
group api[API]
|
||||
group public[Public API] in api
|
||||
group private[Private API] in api
|
||||
|
||||
service serv1(server)[Server] in public
|
||||
|
||||
service serv2(server)[Server] in private
|
||||
service db(database)[Database] in private
|
||||
|
||||
service gateway(internet)[Gateway] in api
|
||||
|
||||
serv1 B--T serv2
|
||||
serv2 L--R db
|
||||
serv1 L--R gateway
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with the fallback icon', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
service unknown(iconnamedoesntexist)[Unknown Icon]
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with split directioning', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
service db(database)[Database]
|
||||
service s3(disk)[Storage]
|
||||
service serv1(server)[Server 1]
|
||||
service serv2(server)[Server 2]
|
||||
service disk(disk)[Disk]
|
||||
|
||||
db L--R s3
|
||||
serv1 L--T s3
|
||||
serv2 L--B s3
|
||||
serv1 T--B disk
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with directional arrows', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
service servC(server)[Server 1]
|
||||
service servL(server)[Server 2]
|
||||
service servR(server)[Server 3]
|
||||
service servT(server)[Server 4]
|
||||
service servB(server)[Server 5]
|
||||
|
||||
servC (L--R) servL
|
||||
servC (R--L) servR
|
||||
servC (T--B) servT
|
||||
servC (B--T) servB
|
||||
|
||||
servL (T--L) servT
|
||||
servL (B--L) servB
|
||||
servR (T--R) servT
|
||||
servR (B--R) servB
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with group edges', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
group left_group(cloud)[Left]
|
||||
group right_group(cloud)[Right]
|
||||
group top_group(cloud)[Top]
|
||||
group bottom_group(cloud)[Bottom]
|
||||
group center_group(cloud)[Center]
|
||||
|
||||
service left_disk(disk)[Disk] in left_group
|
||||
service right_disk(disk)[Disk] in right_group
|
||||
service top_disk(disk)[Disk] in top_group
|
||||
service bottom_disk(disk)[Disk] in bottom_group
|
||||
service center_disk(disk)[Disk] in center_group
|
||||
|
||||
left_disk{group} (R--L) center_disk{group}
|
||||
right_disk{group} (L--R) center_disk{group}
|
||||
top_disk{group} (B--T) center_disk{group}
|
||||
bottom_disk{group} (T--B) center_disk{group}
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with edge labels', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
service servC(server)[Server 1]
|
||||
service servL(server)[Server 2]
|
||||
service servR(server)[Server 3]
|
||||
service servT(server)[Server 4]
|
||||
service servB(server)[Server 5]
|
||||
|
||||
servC L-[Label]-R servL
|
||||
servC R-[Label]-L servR
|
||||
servC T-[Label]-B servT
|
||||
servC B-[Label]-T servB
|
||||
|
||||
servL T-[Label]-L servT
|
||||
servL B-[Label]-L servB
|
||||
servR T-[Label]-R servT
|
||||
servR B-[Label]-R servB
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with simple junction edges', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
service left_disk(disk)[Disk]
|
||||
service top_disk(disk)[Disk]
|
||||
service bottom_disk(disk)[Disk]
|
||||
service top_gateway(internet)[Gateway]
|
||||
service bottom_gateway(internet)[Gateway]
|
||||
junction juncC
|
||||
junction juncR
|
||||
|
||||
left_disk R--L juncC
|
||||
top_disk B--T juncC
|
||||
bottom_disk T--B juncC
|
||||
juncC R--L juncR
|
||||
top_gateway B--T juncR
|
||||
bottom_gateway T--B juncR
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render an architecture diagram with complex junction edges', () => {
|
||||
imgSnapshotTest(
|
||||
`architecture
|
||||
group left
|
||||
group right
|
||||
service left_disk(disk)[Disk] in left
|
||||
service top_disk(disk)[Disk] in left
|
||||
service bottom_disk(disk)[Disk] in left
|
||||
service top_gateway(internet)[Gateway] in right
|
||||
service bottom_gateway(internet)[Gateway] in right
|
||||
junction juncC in left
|
||||
junction juncR in right
|
||||
|
||||
left_disk R--L juncC
|
||||
top_disk B--T juncC
|
||||
bottom_disk T--B juncC
|
||||
|
||||
|
||||
top_gateway (B--T juncR
|
||||
bottom_gateway (T--B juncR
|
||||
|
||||
juncC{group} R--L) juncR{group}
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
309
demos/architecture.html
Normal file
309
demos/architecture.html
Normal file
@@ -0,0 +1,309 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>Architecture Mermaid Quick Test Page</title>
|
||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
|
||||
<style>
|
||||
div.mermaid {
|
||||
/* font-family: 'trebuchet ms', verdana, arial; */
|
||||
font-family: 'Courier New', Courier, monospace !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Architecture diagram demo</h1>
|
||||
<h2>Simple diagram with groups</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
group api(cloud)[API]
|
||||
|
||||
service db(database)[Database] in api
|
||||
service disk1(disk)[Storage] in api
|
||||
service disk2(disk)[Storage] in api
|
||||
service server(server)[Server] in api
|
||||
service gateway(internet)[Gateway]
|
||||
|
||||
db:L -- R:server
|
||||
disk1:T -- B:server
|
||||
disk2:T -- B:db
|
||||
server:T -- B:gateway
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Groups within groups</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
group api[API]
|
||||
group public[Public API] in api
|
||||
group private[Private API] in api
|
||||
|
||||
|
||||
service serv1(server)[Server] in public
|
||||
|
||||
|
||||
service serv2(server)[Server] in private
|
||||
service db(database)[Database] in private
|
||||
|
||||
service gateway(internet)[Gateway] in api
|
||||
|
||||
serv1:B -- T:serv2
|
||||
|
||||
serv2:L -- R:db
|
||||
|
||||
serv1:L -- R:gateway
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Default icon (?) from unknown icon name</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service unknown(iconnamedoesntexist)[Unknown Icon]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Split Direction</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service db(database)[Database]
|
||||
service s3(disk)[Storage]
|
||||
service serv1(server)[Server 1]
|
||||
service serv2(server)[Server 2]
|
||||
service disk(disk)[Disk]
|
||||
|
||||
db:L -- R:s3
|
||||
serv1:L -- T:s3
|
||||
serv2:L -- B:s3
|
||||
serv1:T -- B:disk
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Arrow Tests</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service servC(server)[Server 1]
|
||||
service servL(server)[Server 2]
|
||||
service servR(server)[Server 3]
|
||||
service servT(server)[Server 4]
|
||||
service servB(server)[Server 5]
|
||||
|
||||
servC:L <--> R:servL
|
||||
servC:R <--> L:servR
|
||||
servC:T <--> B:servT
|
||||
servC:B <--> T:servB
|
||||
|
||||
servL:T <--> L:servT
|
||||
servL:B <--> L:servB
|
||||
servR:T <--> R:servT
|
||||
servR:B <--> R:servB
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service servC(server)[Server 1]
|
||||
service servL(server)[Server 2]
|
||||
service servR(server)[Server 3]
|
||||
service servT(server)[Server 4]
|
||||
service servB(server)[Server 5]
|
||||
|
||||
servC:L <--> R:servL
|
||||
servC:R <--> L:servR
|
||||
servC:T <--> B:servT
|
||||
servC:B <--> T:servB
|
||||
|
||||
servT:L <--> T:servL
|
||||
servB:L <--> B:servL
|
||||
servT:R <--> T:servR
|
||||
servB:R <--> B:servR
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Group Edges</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
group left_group(cloud)[Left]
|
||||
group right_group(cloud)[Right]
|
||||
group top_group(cloud)[Top]
|
||||
group bottom_group(cloud)[Bottom]
|
||||
group center_group(cloud)[Center]
|
||||
|
||||
service left_disk(disk)[Disk] in left_group
|
||||
service right_disk(disk)[Disk] in right_group
|
||||
service top_disk(disk)[Disk] in top_group
|
||||
service bottom_disk(disk)[Disk] in bottom_group
|
||||
service center_disk(disk)[Disk] in center_group
|
||||
|
||||
left_disk{group}:R <--> L:center_disk{group}
|
||||
right_disk{group}:L <--> R:center_disk{group}
|
||||
top_disk{group}:B <--> T:center_disk{group}
|
||||
bottom_disk{group}:T <--> B:center_disk{group}
|
||||
</pre
|
||||
>
|
||||
<hr />
|
||||
|
||||
<h2>Edge Label Test</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service servC(server)[Server 1]
|
||||
service servL(server)[Server 2]
|
||||
service servR(server)[Server 3]
|
||||
service servT(server)[Server 4]
|
||||
service servB(server)[Server 5]
|
||||
|
||||
servC:L -[Label]- R:servL
|
||||
servC:R -[Label]- L:servR
|
||||
servC:T -[Label]- B:servT
|
||||
servC:B -[Label]- T:servB
|
||||
|
||||
servL:T -[Label]- L:servT
|
||||
servL:B -[Label]- L:servB
|
||||
servR:T -[Label]- R:servT
|
||||
servR:B -[Label]- R:servB
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service servC(server)[Server 1]
|
||||
service servL(server)[Server 2]
|
||||
service servR(server)[Server 3]
|
||||
service servT(server)[Server 4]
|
||||
service servB(server)[Server 5]
|
||||
|
||||
servC:L -[Label that is Long]- R:servL
|
||||
servC:R -[Label that is Long]- L:servR
|
||||
servC:T -[Label that is Long]- B:servT
|
||||
servC:B -[Label that is Long]- T:servB
|
||||
|
||||
servL:T -[Label that is Long]- L:servT
|
||||
servL:B -[Label that is Long]- L:servB
|
||||
servR:T -[Label that is Long]- R:servT
|
||||
servR:B -[Label that is Long]- R:servB
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
<h2>Junction Demo</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service left_disk(disk)[Disk]
|
||||
service top_disk(disk)[Disk]
|
||||
service bottom_disk(disk)[Disk]
|
||||
service top_gateway(internet)[Gateway]
|
||||
service bottom_gateway(internet)[Gateway]
|
||||
junction juncC
|
||||
junction juncR
|
||||
|
||||
left_disk:R -- L:juncC
|
||||
top_disk:B -- T:juncC
|
||||
bottom_disk:T -- B:juncC
|
||||
juncC:R -- L:juncR
|
||||
top_gateway:B -- T:juncR
|
||||
bottom_gateway:T -- B:juncR
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Junction Demo Groups</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
group left
|
||||
group right
|
||||
service left_disk(disk)[Disk] in left
|
||||
service top_disk(disk)[Disk] in left
|
||||
service bottom_disk(disk)[Disk] in left
|
||||
service top_gateway(internet)[Gateway] in right
|
||||
service bottom_gateway(internet)[Gateway] in right
|
||||
junction juncC in left
|
||||
junction juncR in right
|
||||
|
||||
left_disk:R -- L:juncC
|
||||
top_disk:B -- T:juncC
|
||||
bottom_disk:T -- B:juncC
|
||||
|
||||
|
||||
top_gateway:B <-- T:juncR
|
||||
bottom_gateway:T <-- B:juncR
|
||||
|
||||
juncC{group}:R --> L:juncR{group}
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>AWS Icon Demo</h2>
|
||||
<pre class="mermaid">
|
||||
architecture-beta
|
||||
service s3(aws:s3)[Cloud Store]
|
||||
service ec2(aws:ec2)[Server]
|
||||
service wave(aws:wavelength)[Wave]
|
||||
service droplet(do:droplet)[Droplet]
|
||||
service repo(gh:github)[Repository]
|
||||
</pre
|
||||
>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
|
||||
const ALLOWED_TAGS = [
|
||||
'a',
|
||||
'b',
|
||||
'blockquote',
|
||||
'br',
|
||||
'dd',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'em',
|
||||
'foreignObject',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'h7',
|
||||
'h8',
|
||||
'hr',
|
||||
'i',
|
||||
'li',
|
||||
'ul',
|
||||
'ol',
|
||||
'p',
|
||||
'pre',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'tr',
|
||||
];
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
theme: 'base',
|
||||
startOnLoad: true,
|
||||
logLevel: 0,
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
htmlLabels: true,
|
||||
},
|
||||
gantt: {
|
||||
useMaxWidth: false,
|
||||
},
|
||||
architecture: {
|
||||
iconSize: 80,
|
||||
},
|
||||
useMaxWidth: false,
|
||||
iconLibraries: ['aws:common', 'aws:full', 'github', 'digital-ocean'],
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
mermaid.parseError = function (err, hash) {
|
||||
console.error('In parse error:');
|
||||
console.error(err);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -88,6 +88,9 @@
|
||||
<li>
|
||||
<h2><a href="./block.html">Layered Blocks</a></h2>
|
||||
</li>
|
||||
<li>
|
||||
<h2><a href="./architecture.html">Architecture</a></h2>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
67
docs/config/icons.md
Normal file
67
docs/config/icons.md
Normal file
@@ -0,0 +1,67 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/icons.md](../../packages/mermaid/src/docs/config/icons.md).
|
||||
|
||||
# SVG Icons (v???+)
|
||||
|
||||
SVG Icons can be used with supported diagrams. Alongside the icon packs included with Mermaid, 3rd party libraries can be included in the configuration to cover additional use-cases.
|
||||
|
||||
## Supported Diagrams
|
||||
|
||||
| Diagram | Usage |
|
||||
| ------------ | --------------------------------- |
|
||||
| Architecture | Icon names are surrounded by `()` |
|
||||
|
||||
## Included Icon Packs
|
||||
|
||||
| Icon Pack | Prefix |
|
||||
| ------------- | ------ |
|
||||
| default | N/A |
|
||||
| Amazon AWS | `aws:` |
|
||||
| Digital Ocean | `do:` |
|
||||
| GitHub | `gh:` |
|
||||
|
||||
Note that in order to use non-generic icons that are provided with Mermaid, the packs must be explicitly loaded when on initialization initialized.
|
||||
|
||||
```js
|
||||
import sampleIconPack from 'sample-icon-pack';
|
||||
|
||||
mermaid.initialize({
|
||||
iconLibraries: ['aws:common', 'aws:full', 'github', 'digital-ocean'],
|
||||
});
|
||||
```
|
||||
|
||||
## Using Custom Icon Packs
|
||||
|
||||
Custom icon packs can be used by including them in the `iconLibraries` array on mermaid initialization.
|
||||
|
||||
```js
|
||||
import sampleIconPack from 'sample-icon-pack';
|
||||
|
||||
mermaid.initialize({
|
||||
iconLibraries: [sampleIconPack, 'aws:full', ...],
|
||||
});
|
||||
```
|
||||
|
||||
## Creating Custom Icon Packs
|
||||
|
||||
```js
|
||||
import { createIcon } from 'mermaid';
|
||||
import type { IconLibrary, IconResolver } from 'mermaid';
|
||||
|
||||
// type IconLibrary = Record<string, IconResolver>;
|
||||
// createIcon: (icon: string, originalSize: number) => IconResolver
|
||||
const myIconLibrary: IconLibrary = {
|
||||
defaultCloudExample: createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<path d="..." style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`,
|
||||
80
|
||||
)
|
||||
};
|
||||
|
||||
export default myIconLibrary
|
||||
```
|
@@ -28,7 +28,7 @@ page.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435)
|
||||
[packages/mermaid/src/mermaid.ts:441](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L441)
|
||||
|
||||
---
|
||||
|
||||
@@ -59,7 +59,7 @@ A graph definition key
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
|
||||
[packages/mermaid/src/mermaid.ts:443](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L443)
|
||||
|
||||
---
|
||||
|
||||
@@ -89,7 +89,7 @@ Use [initialize](mermaid.Mermaid.md#initialize) and [run](mermaid.Mermaid.md#run
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430)
|
||||
[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
|
||||
|
||||
---
|
||||
|
||||
@@ -116,7 +116,7 @@ This function should be called before the run function.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434)
|
||||
[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440)
|
||||
|
||||
---
|
||||
|
||||
@@ -130,7 +130,7 @@ Use [parse](mermaid.Mermaid.md#parse) and [render](mermaid.Mermaid.md#render) in
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:424](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L424)
|
||||
[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430)
|
||||
|
||||
---
|
||||
|
||||
@@ -180,7 +180,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425)
|
||||
[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
|
||||
|
||||
---
|
||||
|
||||
@@ -190,7 +190,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419)
|
||||
[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425)
|
||||
|
||||
---
|
||||
|
||||
@@ -218,7 +218,7 @@ Used to register external diagram types.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433)
|
||||
[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439)
|
||||
|
||||
---
|
||||
|
||||
@@ -242,7 +242,7 @@ Used to register external diagram types.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
|
||||
[packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438)
|
||||
|
||||
---
|
||||
|
||||
@@ -268,7 +268,7 @@ Used to register external diagram types.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426)
|
||||
[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
|
||||
|
||||
---
|
||||
|
||||
@@ -316,7 +316,7 @@ Renders the mermaid diagrams
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
|
||||
[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
|
||||
|
||||
---
|
||||
|
||||
@@ -351,7 +351,7 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
|
||||
[packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442)
|
||||
|
||||
---
|
||||
|
||||
@@ -361,4 +361,4 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:418](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L418)
|
||||
[packages/mermaid/src/mermaid.ts:424](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L424)
|
||||
|
@@ -20,6 +20,16 @@
|
||||
|
||||
---
|
||||
|
||||
### architecture
|
||||
|
||||
• `Optional` **architecture**: `ArchitectureDiagramConfig`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
|
||||
|
||||
---
|
||||
|
||||
### arrowMarkerAbsolute
|
||||
|
||||
• `Optional` **arrowMarkerAbsolute**: `boolean`
|
||||
@@ -39,7 +49,7 @@ This matters if you are using base tag settings.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
|
||||
[packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209)
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +59,7 @@ This matters if you are using base tag settings.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
|
||||
[packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
|
||||
|
||||
---
|
||||
|
||||
@@ -59,7 +69,7 @@ This matters if you are using base tag settings.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
|
||||
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
|
||||
|
||||
---
|
||||
|
||||
@@ -83,7 +93,7 @@ You can set this attribute to base the seed on a static string.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
|
||||
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
|
||||
|
||||
---
|
||||
|
||||
@@ -101,7 +111,7 @@ should not change unless content is changed.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
|
||||
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
|
||||
|
||||
---
|
||||
|
||||
@@ -111,7 +121,7 @@ should not change unless content is changed.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
|
||||
[packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
|
||||
|
||||
---
|
||||
|
||||
@@ -139,7 +149,7 @@ should not change unless content is changed.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
|
||||
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
|
||||
|
||||
---
|
||||
|
||||
@@ -149,7 +159,7 @@ should not change unless content is changed.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
|
||||
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
|
||||
|
||||
---
|
||||
|
||||
@@ -173,7 +183,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
|
||||
[packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
|
||||
|
||||
---
|
||||
|
||||
@@ -187,7 +197,7 @@ If set to true, ignores legacyMathML.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163)
|
||||
[packages/mermaid/src/config.type.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L172)
|
||||
|
||||
---
|
||||
|
||||
@@ -197,7 +207,7 @@ If set to true, ignores legacyMathML.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
|
||||
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
|
||||
|
||||
---
|
||||
|
||||
@@ -207,7 +217,7 @@ If set to true, ignores legacyMathML.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
|
||||
[packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
|
||||
|
||||
---
|
||||
|
||||
@@ -233,13 +243,26 @@ Defines the seed to be used when using handDrawn look. This is important for the
|
||||
|
||||
---
|
||||
|
||||
### iconLibraries
|
||||
|
||||
• `Optional` **iconLibraries**: ([`IconLibrary`](../modules/mermaid.md#iconlibrary) | `"aws:common"` | `"aws:full"` | `"github"` | `"digital-ocean"`)\[]
|
||||
|
||||
This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code.
|
||||
For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:162](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L162)
|
||||
|
||||
---
|
||||
|
||||
### journey
|
||||
|
||||
• `Optional` **journey**: `JourneyDiagramConfig`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
|
||||
[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
|
||||
|
||||
---
|
||||
|
||||
@@ -300,7 +323,7 @@ Defines which main look to use for the diagram.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
|
||||
[packages/mermaid/src/config.type.ts:213](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L213)
|
||||
|
||||
---
|
||||
|
||||
@@ -334,7 +357,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
|
||||
[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
|
||||
|
||||
---
|
||||
|
||||
@@ -344,7 +367,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
|
||||
[packages/mermaid/src/config.type.ts:208](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L208)
|
||||
|
||||
---
|
||||
|
||||
@@ -354,7 +377,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
|
||||
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
|
||||
|
||||
---
|
||||
|
||||
@@ -364,7 +387,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
|
||||
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
|
||||
|
||||
---
|
||||
|
||||
@@ -374,7 +397,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
|
||||
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
|
||||
|
||||
---
|
||||
|
||||
@@ -384,7 +407,7 @@ The maximum allowed size of the users text diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
|
||||
[packages/mermaid/src/config.type.ts:207](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L207)
|
||||
|
||||
---
|
||||
|
||||
@@ -420,7 +443,7 @@ Level of trust for parsed diagram
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
|
||||
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
|
||||
|
||||
---
|
||||
|
||||
@@ -442,7 +465,7 @@ Dictates whether mermaid starts on Page load
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
|
||||
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
|
||||
|
||||
---
|
||||
|
||||
@@ -455,7 +478,7 @@ This is useful when you want to control how to handle syntax errors in your appl
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209)
|
||||
[packages/mermaid/src/config.type.ts:219](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L219)
|
||||
|
||||
---
|
||||
|
||||
@@ -498,7 +521,7 @@ You may also use `themeCSS` to override this value.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
|
||||
[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
|
||||
|
||||
---
|
||||
|
||||
@@ -508,7 +531,7 @@ You may also use `themeCSS` to override this value.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
|
||||
[packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
|
||||
|
||||
---
|
||||
|
||||
@@ -518,4 +541,4 @@ You may also use `themeCSS` to override this value.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
|
||||
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
|
||||
|
@@ -18,7 +18,7 @@ The nodes to render. If this is set, `querySelector` will be ignored.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:48](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L48)
|
||||
[packages/mermaid/src/mermaid.ts:54](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L54)
|
||||
|
||||
---
|
||||
|
||||
@@ -44,7 +44,7 @@ A callback to call after each diagram is rendered.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:52](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L52)
|
||||
[packages/mermaid/src/mermaid.ts:58](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L58)
|
||||
|
||||
---
|
||||
|
||||
@@ -56,7 +56,7 @@ The query selector to use when finding elements to render. Default: `".mermaid"`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:44](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L44)
|
||||
[packages/mermaid/src/mermaid.ts:50](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L50)
|
||||
|
||||
---
|
||||
|
||||
@@ -68,4 +68,4 @@ If `true`, errors will be logged to the console, but not thrown. Default: `false
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L56)
|
||||
[packages/mermaid/src/mermaid.ts:62](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L62)
|
||||
|
@@ -26,6 +26,41 @@
|
||||
|
||||
## Type Aliases
|
||||
|
||||
### IconLibrary
|
||||
|
||||
Ƭ **IconLibrary**: `Record`<`string`, [`IconResolver`](mermaid.md#iconresolver)>
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/rendering-util/svgRegister.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/svgRegister.ts#L7)
|
||||
|
||||
---
|
||||
|
||||
### IconResolver
|
||||
|
||||
Ƭ **IconResolver**: (`parent`: `Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>, `width?`: `number`) => `Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
#### Type declaration
|
||||
|
||||
▸ (`parent`, `width?`): `Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
##### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------- | :-------------------------------------------------------------------- |
|
||||
| `parent` | `Selection`<`SVGGElement`, `unknown`, `Element` \| `null`, `unknown`> |
|
||||
| `width?` | `number` |
|
||||
|
||||
##### Returns
|
||||
|
||||
`Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/rendering-util/svgRegister.ts:3](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/svgRegister.ts#L3)
|
||||
|
||||
---
|
||||
|
||||
### InternalHelpers
|
||||
|
||||
Ƭ **InternalHelpers**: typeof `internalHelpers`
|
||||
@@ -87,4 +122,29 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440)
|
||||
[packages/mermaid/src/mermaid.ts:446](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L446)
|
||||
|
||||
## Functions
|
||||
|
||||
### createIcon
|
||||
|
||||
▸ **createIcon**(`icon`, `originalSize`): [`IconResolver`](mermaid.md#iconresolver)
|
||||
|
||||
Converts an SVG Icon passed as a string into a properly formatted IconResolver
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------------- | :------- | :-------------------------------------------------------------------------- |
|
||||
| `icon` | `string` | html code for the svg icon as a string (the SVG tag should not be included) |
|
||||
| `originalSize` | `number` | the original size of the SVG Icon in pixels |
|
||||
|
||||
#### Returns
|
||||
|
||||
[`IconResolver`](mermaid.md#iconresolver)
|
||||
|
||||
IconResolver
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/rendering-util/svgRegister.ts:15](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/svgRegister.ts#L15)
|
||||
|
@@ -55,6 +55,10 @@ For a more detailed introduction to Mermaid and some of its more basic uses, loo
|
||||
|
||||
**Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project 🙏**
|
||||
|
||||
Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze.
|
||||
|
||||
[](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
|
||||
|
||||
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
|
||||
|
||||
<a href="https://applitools.com/">
|
||||
|
194
docs/syntax/architecture.md
Normal file
194
docs/syntax/architecture.md
Normal file
@@ -0,0 +1,194 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/architecture.md](../../packages/mermaid/src/docs/syntax/architecture.md).
|
||||
|
||||
# Architecture Diagrams Documentation (v\<MERMAID_RELEASE_VERSION>+)
|
||||
|
||||
> In the context of mermaid-js, the architecture diagram is used to show the relationship between services and resources commonly found within the Cloud or CI/CD deployments. In an architecture diagram, services (nodes) are connected by edges. Related services can be placed within groups to better illustrate how they are organized.
|
||||
|
||||
## Example
|
||||
|
||||
```mermaid-example
|
||||
architecture-beta
|
||||
group api(cloud)[API]
|
||||
|
||||
service db(database)[Database] in api
|
||||
service disk1(disk)[Storage] in api
|
||||
service disk2(disk)[Storage] in api
|
||||
service server(server)[Server] in api
|
||||
|
||||
db:L -- R:server
|
||||
disk1:T -- B:server
|
||||
disk2:T -- B:db
|
||||
```
|
||||
|
||||
```mermaid
|
||||
architecture-beta
|
||||
group api(cloud)[API]
|
||||
|
||||
service db(database)[Database] in api
|
||||
service disk1(disk)[Storage] in api
|
||||
service disk2(disk)[Storage] in api
|
||||
service server(server)[Server] in api
|
||||
|
||||
db:L -- R:server
|
||||
disk1:T -- B:server
|
||||
disk2:T -- B:db
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The building blocks of an architecture are `groups`, `services`, `edges`, and `junctions`.
|
||||
|
||||
For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`.
|
||||
|
||||
To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component.
|
||||
|
||||
### Groups
|
||||
|
||||
The syntax for declaring a group is:
|
||||
|
||||
```
|
||||
group {group id}({icon name})[{title}] (in {parent id})?
|
||||
```
|
||||
|
||||
Put together:
|
||||
|
||||
```
|
||||
group public_api(cloud)[Public API]
|
||||
```
|
||||
|
||||
creates a group identified as `public_api`, uses the icon `cloud`, and has the label `Public API`.
|
||||
|
||||
Additionally, groups can be placed within a group using the optional `in` keyword
|
||||
|
||||
```
|
||||
group private_api(cloud)[Private API] in public_api
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
The syntax for declaring a service is:
|
||||
|
||||
```
|
||||
service {service id}({icon name})[{title}] (in {parent id})?
|
||||
```
|
||||
|
||||
Put together:
|
||||
|
||||
```
|
||||
service database(db)[Database]
|
||||
```
|
||||
|
||||
creates the service identified as `database`, using the icon `db`, with the label `Database`.
|
||||
|
||||
If the service belongs to a group, it can be placed inside it through the optional `in` keyword
|
||||
|
||||
```
|
||||
service database(db)[Database] in private_api
|
||||
```
|
||||
|
||||
### Edges
|
||||
|
||||
The syntax for declaring an edge is:
|
||||
|
||||
```
|
||||
{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}?
|
||||
```
|
||||
|
||||
#### Edge Direction
|
||||
|
||||
The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B`
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
db:R -- L:server
|
||||
```
|
||||
|
||||
creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`.
|
||||
|
||||
```
|
||||
db:T -- L:server
|
||||
```
|
||||
|
||||
creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`.
|
||||
|
||||
#### Arrows
|
||||
|
||||
Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
subnet:R --> L:gateway
|
||||
```
|
||||
|
||||
creates an edge with the arrow going into the `gateway` service
|
||||
|
||||
#### Edges out of Groups
|
||||
|
||||
To have an edge go from a group to another group or service within another group, the `{group}` modifier can be added after the `serviceId`.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
service server[Server] in groupOne
|
||||
service subnet[Subnet] in groupTwo
|
||||
|
||||
server{group}:B --> T:subnet{group}
|
||||
```
|
||||
|
||||
creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`.
|
||||
|
||||
It's important to note that `groupId`s cannot be used for specifying edges and the `{group}` modifier can only be used for services within a group.
|
||||
|
||||
### Junctions
|
||||
|
||||
Junctions are a special type of node which acts as a potential 4-way split between edges.
|
||||
|
||||
The syntax for declaring a junction is:
|
||||
|
||||
```
|
||||
junction {junction id} (in {parent id})?
|
||||
```
|
||||
|
||||
```mermaid-example
|
||||
architecture-beta
|
||||
service left_disk(disk)[Disk]
|
||||
service top_disk(disk)[Disk]
|
||||
service bottom_disk(disk)[Disk]
|
||||
service top_gateway(internet)[Gateway]
|
||||
service bottom_gateway(internet)[Gateway]
|
||||
junction junctionCenter
|
||||
junction junctionRight
|
||||
|
||||
left_disk:R -- L:junctionCenter
|
||||
top_disk:B -- T:junctionCenter
|
||||
bottom_disk:T -- B:junctionCenter
|
||||
junctionCenter:R -- L:junctionRight
|
||||
top_gateway:B -- T:junctionRight
|
||||
bottom_gateway:T -- B:junctionRight
|
||||
```
|
||||
|
||||
```mermaid
|
||||
architecture-beta
|
||||
service left_disk(disk)[Disk]
|
||||
service top_disk(disk)[Disk]
|
||||
service bottom_disk(disk)[Disk]
|
||||
service top_gateway(internet)[Gateway]
|
||||
service bottom_gateway(internet)[Gateway]
|
||||
junction junctionCenter
|
||||
junction junctionRight
|
||||
|
||||
left_disk:R -- L:junctionCenter
|
||||
top_disk:B -- T:junctionCenter
|
||||
bottom_disk:T -- B:junctionCenter
|
||||
junctionCenter:R -- L:junctionRight
|
||||
top_gateway:B -- T:junctionRight
|
||||
bottom_gateway:T -- B:junctionRight
|
||||
```
|
||||
|
||||
## Configuration
|
@@ -71,6 +71,7 @@
|
||||
"@mermaid-js/parser": "workspace:^",
|
||||
"cytoscape": "^3.29.2",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.2.0",
|
||||
"d3": "^7.9.0",
|
||||
"d3-sankey": "^0.12.3",
|
||||
"dagre-d3-es": "7.0.10",
|
||||
@@ -87,6 +88,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@adobe/jsonschema2md": "^8.0.0",
|
||||
"@types/cytoscape-fcose": "^2.2.4",
|
||||
"@types/cytoscape": "^3.21.4",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/d3-sankey": "^0.12.4",
|
||||
|
@@ -154,6 +154,15 @@ export interface MermaidConfig {
|
||||
*
|
||||
*/
|
||||
legacyMathML?: boolean;
|
||||
/**
|
||||
* This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code.
|
||||
* For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram.
|
||||
*
|
||||
*/
|
||||
iconLibraries?: Array<
|
||||
| import('./rendering-util/svgRegister.js').IconLibrary
|
||||
| import('./rendering-util/svg/index.js').IconNamespaceKeys
|
||||
>;
|
||||
/**
|
||||
* This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
|
||||
* fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
|
||||
@@ -191,6 +200,7 @@ export interface MermaidConfig {
|
||||
quadrantChart?: QuadrantChartConfig;
|
||||
xyChart?: XYChartConfig;
|
||||
requirement?: RequirementDiagramConfig;
|
||||
architecture?: ArchitectureDiagramConfig;
|
||||
mindmap?: MindmapDiagramConfig;
|
||||
gitGraph?: GitGraphDiagramConfig;
|
||||
c4?: C4DiagramConfig;
|
||||
@@ -1001,6 +1011,17 @@ export interface RequirementDiagramConfig extends BaseDiagramConfig {
|
||||
rect_padding?: number;
|
||||
line_height?: number;
|
||||
}
|
||||
/**
|
||||
* The object containing configurations specific for architecture diagrams
|
||||
*
|
||||
* This interface was referenced by `MermaidConfig`'s JSON-Schema
|
||||
* via the `definition` "ArchitectureDiagramConfig".
|
||||
*/
|
||||
export interface ArchitectureDiagramConfig extends BaseDiagramConfig {
|
||||
padding?: number;
|
||||
iconSize?: number;
|
||||
fontSize?: number;
|
||||
}
|
||||
/**
|
||||
* The object containing configurations specific for mindmap diagrams
|
||||
*
|
||||
|
@@ -22,6 +22,7 @@ import mindmap from '../diagrams/mindmap/detector.js';
|
||||
import sankey from '../diagrams/sankey/sankeyDetector.js';
|
||||
import { packet } from '../diagrams/packet/detector.js';
|
||||
import block from '../diagrams/block/blockDetector.js';
|
||||
import architecture from '../diagrams/architecture/architectureDetector.js';
|
||||
import { registerLazyLoadedDiagrams } from './detectType.js';
|
||||
import { registerDiagram } from './diagramAPI.js';
|
||||
|
||||
@@ -90,6 +91,7 @@ export const addDiagrams = () => {
|
||||
sankey,
|
||||
packet,
|
||||
xychart,
|
||||
block
|
||||
block,
|
||||
architecture
|
||||
);
|
||||
};
|
||||
|
336
packages/mermaid/src/diagrams/architecture/architectureDb.ts
Normal file
336
packages/mermaid/src/diagrams/architecture/architectureDb.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
// TODO remove no-console
|
||||
/* eslint-disable no-console */
|
||||
import type {
|
||||
ArchitectureState,
|
||||
ArchitectureDB,
|
||||
ArchitectureService,
|
||||
ArchitectureGroup,
|
||||
ArchitectureEdge,
|
||||
ArchitectureDirectionPairMap,
|
||||
ArchitectureDirectionPair,
|
||||
ArchitectureSpatialMap,
|
||||
ArchitectureNode,
|
||||
ArchitectureJunction,
|
||||
} from './architectureTypes.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import {
|
||||
getArchitectureDirectionPair,
|
||||
isArchitectureDirection,
|
||||
isArchitectureJunction,
|
||||
isArchitectureService,
|
||||
shiftPositionByArchitectureDirectionPair,
|
||||
} from './architectureTypes.js';
|
||||
import {
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
} from '../common/commonDb.js';
|
||||
import type { ArchitectureDiagramConfig } from '../../config.type.js';
|
||||
import DEFAULT_CONFIG from '../../defaultConfig.js';
|
||||
import type { D3Element } from '../../types.js';
|
||||
import { ImperativeState } from '../../utils/imperativeState.js';
|
||||
|
||||
const DEFAULT_ARCHITECTURE_CONFIG: Required<ArchitectureDiagramConfig> =
|
||||
DEFAULT_CONFIG.architecture;
|
||||
|
||||
const state = new ImperativeState<ArchitectureState>(() => ({
|
||||
nodes: {},
|
||||
groups: {},
|
||||
edges: [],
|
||||
registeredIds: {},
|
||||
config: DEFAULT_ARCHITECTURE_CONFIG,
|
||||
dataStructures: undefined,
|
||||
elements: {},
|
||||
}));
|
||||
|
||||
const clear = (): void => {
|
||||
state.reset();
|
||||
commonClear();
|
||||
};
|
||||
|
||||
const addService = function ({
|
||||
id,
|
||||
icon,
|
||||
in: parent,
|
||||
title,
|
||||
iconText,
|
||||
}: Omit<ArchitectureService, 'edges'>) {
|
||||
if (state.records.registeredIds[id] !== undefined) {
|
||||
throw new Error(
|
||||
`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`
|
||||
);
|
||||
}
|
||||
if (parent !== undefined) {
|
||||
if (id === parent) {
|
||||
throw new Error(`The service [${id}] cannot be placed within itself`);
|
||||
}
|
||||
if (state.records.registeredIds[parent] === undefined) {
|
||||
throw new Error(
|
||||
`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`
|
||||
);
|
||||
}
|
||||
if (state.records.registeredIds[parent] === 'node') {
|
||||
throw new Error(`The service [${id}]'s parent is not a group`);
|
||||
}
|
||||
}
|
||||
|
||||
state.records.registeredIds[id] = 'node';
|
||||
|
||||
state.records.nodes[id] = {
|
||||
id,
|
||||
type: 'service',
|
||||
icon,
|
||||
iconText,
|
||||
title,
|
||||
edges: [],
|
||||
in: parent,
|
||||
};
|
||||
};
|
||||
|
||||
const getServices = (): ArchitectureService[] =>
|
||||
Object.values(state.records.nodes).filter<ArchitectureService>(isArchitectureService);
|
||||
|
||||
const addJunction = function ({ id, in: parent }: Omit<ArchitectureJunction, 'edges'>) {
|
||||
state.records.registeredIds[id] = 'node';
|
||||
|
||||
state.records.nodes[id] = {
|
||||
id,
|
||||
type: 'junction',
|
||||
edges: [],
|
||||
in: parent,
|
||||
};
|
||||
};
|
||||
|
||||
const getJunctions = (): ArchitectureJunction[] =>
|
||||
Object.values(state.records.nodes).filter<ArchitectureJunction>(isArchitectureJunction);
|
||||
|
||||
const getNodes = (): ArchitectureNode[] => Object.values(state.records.nodes);
|
||||
|
||||
const getNode = (id: string): ArchitectureNode | null => state.records.nodes[id];
|
||||
|
||||
const addGroup = function ({ id, icon, in: parent, title }: ArchitectureGroup) {
|
||||
if (state.records.registeredIds[id] !== undefined) {
|
||||
throw new Error(
|
||||
`The group id [${id}] is already in use by another ${state.records.registeredIds[id]}`
|
||||
);
|
||||
}
|
||||
if (parent !== undefined) {
|
||||
if (id === parent) {
|
||||
throw new Error(`The group [${id}] cannot be placed within itself`);
|
||||
}
|
||||
if (state.records.registeredIds[parent] === undefined) {
|
||||
throw new Error(
|
||||
`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`
|
||||
);
|
||||
}
|
||||
if (state.records.registeredIds[parent] === 'node') {
|
||||
throw new Error(`The group [${id}]'s parent is not a group`);
|
||||
}
|
||||
}
|
||||
|
||||
state.records.registeredIds[id] = 'group';
|
||||
|
||||
state.records.groups[id] = {
|
||||
id,
|
||||
icon,
|
||||
title,
|
||||
in: parent,
|
||||
};
|
||||
};
|
||||
const getGroups = (): ArchitectureGroup[] => {
|
||||
return Object.values(state.records.groups);
|
||||
};
|
||||
|
||||
const addEdge = function ({
|
||||
lhsId,
|
||||
rhsId,
|
||||
lhsDir,
|
||||
rhsDir,
|
||||
lhsInto,
|
||||
rhsInto,
|
||||
lhsGroup,
|
||||
rhsGroup,
|
||||
title,
|
||||
}: ArchitectureEdge<string>) {
|
||||
if (!isArchitectureDirection(lhsDir)) {
|
||||
throw new Error(
|
||||
`Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${lhsDir}`
|
||||
);
|
||||
}
|
||||
if (!isArchitectureDirection(rhsDir)) {
|
||||
throw new Error(
|
||||
`Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${rhsDir}`
|
||||
);
|
||||
}
|
||||
|
||||
if (state.records.nodes[lhsId] === undefined && state.records.groups[lhsId] === undefined) {
|
||||
throw new Error(
|
||||
`The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
|
||||
);
|
||||
}
|
||||
if (state.records.nodes[rhsId] === undefined && state.records.groups[lhsId] === undefined) {
|
||||
throw new Error(
|
||||
`The right-hand id [${rhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
|
||||
);
|
||||
}
|
||||
|
||||
const lhsGroupId = state.records.nodes[lhsId].in;
|
||||
const rhsGroupId = state.records.nodes[rhsId].in;
|
||||
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
|
||||
throw new Error(
|
||||
`The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
|
||||
);
|
||||
}
|
||||
if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
|
||||
throw new Error(
|
||||
`The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
|
||||
);
|
||||
}
|
||||
|
||||
const edge = {
|
||||
lhsId,
|
||||
lhsDir,
|
||||
lhsInto,
|
||||
lhsGroup,
|
||||
rhsId,
|
||||
rhsDir,
|
||||
rhsInto,
|
||||
rhsGroup,
|
||||
title,
|
||||
};
|
||||
|
||||
state.records.edges.push(edge);
|
||||
if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) {
|
||||
state.records.nodes[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
|
||||
state.records.nodes[rhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
|
||||
}
|
||||
};
|
||||
|
||||
const getEdges = (): ArchitectureEdge[] => state.records.edges;
|
||||
|
||||
/**
|
||||
* Returns the current diagram's adjacency list & spatial map.
|
||||
* If they have not been created, run the algorithms to generate them.
|
||||
* @returns
|
||||
*/
|
||||
const getDataStructures = () => {
|
||||
if (state.records.dataStructures === undefined) {
|
||||
// Create an adjacency list of the diagram to perform BFS on
|
||||
// Outer reduce applied on all services
|
||||
// Inner reduce applied on the edges for a service
|
||||
const adjList = Object.entries(state.records.nodes).reduce<
|
||||
Record<string, ArchitectureDirectionPairMap>
|
||||
>((prevOuter, [id, service]) => {
|
||||
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
|
||||
if (edge.lhsId === id) {
|
||||
// source is LHS
|
||||
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
|
||||
if (pair) {
|
||||
prevInner[pair] = edge.rhsId;
|
||||
}
|
||||
} else {
|
||||
// source is RHS
|
||||
const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir);
|
||||
if (pair) {
|
||||
prevInner[pair] = edge.lhsId;
|
||||
}
|
||||
}
|
||||
return prevInner;
|
||||
}, {});
|
||||
return prevOuter;
|
||||
}, {});
|
||||
|
||||
// Configuration for the initial pass of BFS
|
||||
const firstId = Object.keys(adjList)[0];
|
||||
const visited = { [firstId]: 1 };
|
||||
const notVisited = Object.keys(adjList).reduce(
|
||||
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
|
||||
{} as Record<string, number>
|
||||
);
|
||||
|
||||
// Perform BFS on the adjacency list
|
||||
const BFS = (startingId: string): ArchitectureSpatialMap => {
|
||||
const spatialMap = { [startingId]: [0, 0] };
|
||||
const queue = [startingId];
|
||||
while (queue.length > 0) {
|
||||
const id = queue.shift();
|
||||
if (id) {
|
||||
visited[id] = 1;
|
||||
delete notVisited[id];
|
||||
const adj = adjList[id];
|
||||
const [posX, posY] = spatialMap[id];
|
||||
Object.entries(adj).forEach(([dir, rhsId]) => {
|
||||
if (!visited[rhsId]) {
|
||||
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair(
|
||||
[posX, posY],
|
||||
dir as ArchitectureDirectionPair
|
||||
);
|
||||
queue.push(rhsId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return spatialMap;
|
||||
};
|
||||
const spatialMaps = [BFS(firstId)];
|
||||
|
||||
// If our diagram is disconnected, keep adding additional spatial maps until all disconnected graphs have been found
|
||||
while (Object.keys(notVisited).length > 0) {
|
||||
spatialMaps.push(BFS(Object.keys(notVisited)[0]));
|
||||
}
|
||||
state.records.dataStructures = {
|
||||
adjList,
|
||||
spatialMaps,
|
||||
};
|
||||
console.log(state.records.dataStructures);
|
||||
}
|
||||
return state.records.dataStructures;
|
||||
};
|
||||
|
||||
const setElementForId = (id: string, element: D3Element) => {
|
||||
state.records.elements[id] = element;
|
||||
};
|
||||
const getElementById = (id: string) => state.records.elements[id];
|
||||
|
||||
export const db: ArchitectureDB = {
|
||||
clear,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setAccDescription,
|
||||
getAccDescription,
|
||||
|
||||
addService,
|
||||
getServices,
|
||||
addJunction,
|
||||
getJunctions,
|
||||
getNodes,
|
||||
getNode,
|
||||
addGroup,
|
||||
getGroups,
|
||||
addEdge,
|
||||
getEdges,
|
||||
setElementForId,
|
||||
getElementById,
|
||||
getDataStructures,
|
||||
};
|
||||
|
||||
/**
|
||||
* Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined
|
||||
* @param field - the config field to access
|
||||
* @returns
|
||||
*/
|
||||
export function getConfigField<T extends keyof ArchitectureDiagramConfig>(
|
||||
field: T
|
||||
): Required<ArchitectureDiagramConfig>[T] {
|
||||
const arch = getConfig().architecture;
|
||||
if (arch?.[field]) {
|
||||
return arch[field] as Required<ArchitectureDiagramConfig>[T];
|
||||
}
|
||||
return DEFAULT_ARCHITECTURE_CONFIG[field];
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import type {
|
||||
DiagramDetector,
|
||||
DiagramLoader,
|
||||
ExternalDiagramDefinition,
|
||||
} from '../../diagram-api/types.js';
|
||||
|
||||
const id = 'architecture';
|
||||
|
||||
const detector: DiagramDetector = (txt) => {
|
||||
return /^\s*architecture/.test(txt);
|
||||
};
|
||||
|
||||
const loader: DiagramLoader = async () => {
|
||||
const { diagram } = await import('./architectureDiagram.js');
|
||||
return { id, diagram };
|
||||
};
|
||||
|
||||
const architecture: ExternalDiagramDefinition = {
|
||||
id,
|
||||
detector,
|
||||
loader,
|
||||
};
|
||||
|
||||
export default architecture;
|
@@ -0,0 +1,12 @@
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
import { parser } from './architectureParser.js';
|
||||
import { db } from './architectureDb.js';
|
||||
import styles from './architectureStyles.js';
|
||||
import { renderer } from './architectureRenderer.js';
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
parser,
|
||||
db,
|
||||
renderer,
|
||||
styles,
|
||||
};
|
@@ -0,0 +1,24 @@
|
||||
import type { Architecture } from '@mermaid-js/parser';
|
||||
import { parse } from '@mermaid-js/parser';
|
||||
import { log } from '../../logger.js';
|
||||
import type { ParserDefinition } from '../../diagram-api/types.js';
|
||||
import { populateCommonDb } from '../common/populateCommonDb.js';
|
||||
import type { ArchitectureDB } from './architectureTypes.js';
|
||||
import { db } from './architectureDb.js';
|
||||
|
||||
const populateDb = (ast: Architecture, db: ArchitectureDB) => {
|
||||
populateCommonDb(ast, db);
|
||||
ast.groups.map(db.addGroup);
|
||||
ast.services.map((service) => db.addService({ ...service, type: 'service' }));
|
||||
ast.junctions.map((service) => db.addJunction({ ...service, type: 'junction' }));
|
||||
// @ts-ignore TODO our parser guarantees the type is L/R/T/B and not string. How to change to union type?
|
||||
ast.edges.map(db.addEdge);
|
||||
};
|
||||
|
||||
export const parser: ParserDefinition = {
|
||||
parse: async (input: string): Promise<void> => {
|
||||
const ast: Architecture = await parse('architecture', input);
|
||||
log.debug(ast);
|
||||
populateDb(ast, db);
|
||||
},
|
||||
};
|
@@ -0,0 +1,472 @@
|
||||
// TODO remove no-console
|
||||
/* eslint-disable no-console */
|
||||
import type { Position } from 'cytoscape';
|
||||
import cytoscape from 'cytoscape';
|
||||
import type { Diagram } from '../../Diagram.js';
|
||||
import type { FcoseLayoutOptions } from 'cytoscape-fcose';
|
||||
import fcose from 'cytoscape-fcose';
|
||||
import type { DrawDefinition, SVG } from '../../diagram-api/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import type {
|
||||
ArchitectureDataStructures,
|
||||
ArchitectureSpatialMap,
|
||||
EdgeSingularData,
|
||||
EdgeSingular,
|
||||
ArchitectureJunction,
|
||||
NodeSingularData,
|
||||
} from './architectureTypes.js';
|
||||
import {
|
||||
type ArchitectureDB,
|
||||
type ArchitectureDirection,
|
||||
type ArchitectureGroup,
|
||||
type ArchitectureEdge,
|
||||
type ArchitectureService,
|
||||
ArchitectureDirectionName,
|
||||
getOppositeArchitectureDirection,
|
||||
isArchitectureDirectionXY,
|
||||
isArchitectureDirectionY,
|
||||
nodeData,
|
||||
edgeData,
|
||||
} from './architectureTypes.js';
|
||||
import { select } from 'd3';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
import { drawEdges, drawGroups, drawJunctions, drawServices } from './svgDraw.js';
|
||||
import { getConfigField } from './architectureDb.js';
|
||||
|
||||
cytoscape.use(fcose);
|
||||
|
||||
function addServices(services: ArchitectureService[], cy: cytoscape.Core) {
|
||||
services.forEach((service) => {
|
||||
cy.add({
|
||||
group: 'nodes',
|
||||
data: {
|
||||
type: 'service',
|
||||
id: service.id,
|
||||
icon: service.icon,
|
||||
label: service.title,
|
||||
parent: service.in,
|
||||
width: getConfigField('iconSize'),
|
||||
height: getConfigField('iconSize'),
|
||||
} as NodeSingularData,
|
||||
classes: 'node-service',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core) {
|
||||
junctions.forEach((junction) => {
|
||||
cy.add({
|
||||
group: 'nodes',
|
||||
data: {
|
||||
type: 'junction',
|
||||
id: junction.id,
|
||||
parent: junction.in,
|
||||
width: getConfigField('iconSize'),
|
||||
height: getConfigField('iconSize'),
|
||||
} as NodeSingularData,
|
||||
classes: 'node-junction',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function positionNodes(db: ArchitectureDB, cy: cytoscape.Core) {
|
||||
cy.nodes().map((node) => {
|
||||
const data = nodeData(node);
|
||||
if (data.type === 'group') {
|
||||
return;
|
||||
}
|
||||
data.x = node.position().x;
|
||||
data.y = node.position().y;
|
||||
|
||||
const nodeElem = db.getElementById(data.id);
|
||||
nodeElem.attr('transform', 'translate(' + (data.x || 0) + ',' + (data.y || 0) + ')');
|
||||
});
|
||||
}
|
||||
|
||||
function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) {
|
||||
groups.forEach((group) => {
|
||||
cy.add({
|
||||
group: 'nodes',
|
||||
data: {
|
||||
type: 'group',
|
||||
id: group.id,
|
||||
icon: group.icon,
|
||||
label: group.title,
|
||||
parent: group.in,
|
||||
} as NodeSingularData,
|
||||
classes: 'node-group',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
|
||||
edges.forEach((parsedEdge) => {
|
||||
const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } =
|
||||
parsedEdge;
|
||||
const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir)
|
||||
? 'segments'
|
||||
: 'straight';
|
||||
const edge: EdgeSingularData = {
|
||||
id: `${lhsId}-${rhsId}`,
|
||||
label: title,
|
||||
source: lhsId,
|
||||
sourceDir: lhsDir,
|
||||
sourceArrow: lhsInto,
|
||||
sourceGroup: lhsGroup,
|
||||
sourceEndpoint:
|
||||
lhsDir === 'L'
|
||||
? '0 50%'
|
||||
: lhsDir === 'R'
|
||||
? '100% 50%'
|
||||
: lhsDir === 'T'
|
||||
? '50% 0'
|
||||
: '50% 100%',
|
||||
target: rhsId,
|
||||
targetDir: rhsDir,
|
||||
targetArrow: rhsInto,
|
||||
targetGroup: rhsGroup,
|
||||
targetEndpoint:
|
||||
rhsDir === 'L'
|
||||
? '0 50%'
|
||||
: rhsDir === 'R'
|
||||
? '100% 50%'
|
||||
: rhsDir === 'T'
|
||||
? '50% 0'
|
||||
: '50% 100%',
|
||||
};
|
||||
cy.add({
|
||||
group: 'edges',
|
||||
data: edge,
|
||||
classes: edgeType,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getAlignments(spatialMaps: ArchitectureSpatialMap[]): fcose.FcoseAlignmentConstraint {
|
||||
const alignments = spatialMaps.map((spatialMap) => {
|
||||
const horizontalAlignments: Record<number, string[]> = {};
|
||||
const verticalAlignments: Record<number, string[]> = {};
|
||||
// Group service ids in an object with their x and y coordinate as the key
|
||||
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
|
||||
if (!horizontalAlignments[y]) {
|
||||
horizontalAlignments[y] = [];
|
||||
}
|
||||
if (!verticalAlignments[x]) {
|
||||
verticalAlignments[x] = [];
|
||||
}
|
||||
horizontalAlignments[y].push(id);
|
||||
verticalAlignments[x].push(id);
|
||||
});
|
||||
// Merge the values of each object into a list if the inner list has at least 2 elements
|
||||
return {
|
||||
horiz: Object.values(horizontalAlignments).filter((arr) => arr.length > 1),
|
||||
vert: Object.values(verticalAlignments).filter((arr) => arr.length > 1),
|
||||
};
|
||||
});
|
||||
|
||||
// Merge the alignment lists for each spatial map into one 2d array per axis
|
||||
const [horizontal, vertical] = alignments.reduce(
|
||||
([prevHoriz, prevVert], { horiz, vert }) => {
|
||||
return [
|
||||
[...prevHoriz, ...horiz],
|
||||
[...prevVert, ...vert],
|
||||
];
|
||||
},
|
||||
[[] as string[][], [] as string[][]]
|
||||
);
|
||||
|
||||
return {
|
||||
horizontal,
|
||||
vertical,
|
||||
};
|
||||
}
|
||||
|
||||
function getRelativeConstraints(
|
||||
spatialMaps: ArchitectureSpatialMap[]
|
||||
): fcose.FcoseRelativePlacementConstraint[] {
|
||||
const relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = [];
|
||||
const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`;
|
||||
const strToPos = (pos: string) => pos.split(',').map((p) => parseInt(p));
|
||||
|
||||
spatialMaps.forEach((spatialMap) => {
|
||||
const invSpatialMap = Object.fromEntries(
|
||||
Object.entries(spatialMap).map(([id, pos]) => [posToStr(pos), id])
|
||||
);
|
||||
|
||||
// perform BFS
|
||||
const queue = [posToStr([0, 0])];
|
||||
const visited: Record<string, number> = {};
|
||||
const directions: Record<ArchitectureDirection, number[]> = {
|
||||
L: [-1, 0],
|
||||
R: [1, 0],
|
||||
T: [0, 1],
|
||||
B: [0, -1],
|
||||
};
|
||||
while (queue.length > 0) {
|
||||
const curr = queue.shift();
|
||||
if (curr) {
|
||||
visited[curr] = 1;
|
||||
const currId = invSpatialMap[curr];
|
||||
if (currId) {
|
||||
const currPos = strToPos(curr);
|
||||
Object.entries(directions).forEach(([dir, shift]) => {
|
||||
const newPos = posToStr([currPos[0] + shift[0], currPos[1] + shift[1]]);
|
||||
const newId = invSpatialMap[newPos];
|
||||
// If there is an adjacent service to the current one and it has not yet been visited
|
||||
if (newId && !visited[newPos]) {
|
||||
queue.push(newPos);
|
||||
// @ts-ignore cannot determine if left/right or top/bottom are paired together
|
||||
relativeConstraints.push({
|
||||
[ArchitectureDirectionName[dir as ArchitectureDirection]]: newId,
|
||||
[ArchitectureDirectionName[
|
||||
getOppositeArchitectureDirection(dir as ArchitectureDirection)
|
||||
]]: currId,
|
||||
gap: 1.5 * getConfigField('iconSize'),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return relativeConstraints;
|
||||
}
|
||||
|
||||
function layoutArchitecture(
|
||||
services: ArchitectureService[],
|
||||
junctions: ArchitectureJunction[],
|
||||
groups: ArchitectureGroup[],
|
||||
edges: ArchitectureEdge[],
|
||||
{ spatialMaps }: ArchitectureDataStructures
|
||||
): Promise<cytoscape.Core> {
|
||||
return new Promise((resolve) => {
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
const cy = cytoscape({
|
||||
container: document.getElementById('cy'),
|
||||
style: [
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'curve-style': 'straight',
|
||||
label: 'data(label)',
|
||||
'source-endpoint': 'data(sourceEndpoint)',
|
||||
'target-endpoint': 'data(targetEndpoint)',
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'edge.segments',
|
||||
style: {
|
||||
'curve-style': 'segments',
|
||||
'segment-weights': '0',
|
||||
'segment-distances': [0.5],
|
||||
// @ts-ignore Incorrect library types
|
||||
'edge-distances': 'endpoints',
|
||||
'source-endpoint': 'data(sourceEndpoint)',
|
||||
'target-endpoint': 'data(targetEndpoint)',
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
// @ts-ignore Incorrect library types
|
||||
'compound-sizing-wrt-labels': 'include',
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: 'node[label]',
|
||||
style: {
|
||||
'text-valign': 'bottom',
|
||||
'text-halign': 'center',
|
||||
'font-size': `${getConfigField('fontSize')}px`,
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: '.node-service',
|
||||
style: {
|
||||
label: 'data(label)',
|
||||
width: 'data(width)',
|
||||
height: 'data(height)',
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: '.node-junction',
|
||||
style: {
|
||||
width: 'data(width)',
|
||||
height: 'data(height)',
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: '.node-group',
|
||||
style: {
|
||||
// @ts-ignore Incorrect library types
|
||||
padding: `${getConfigField('padding')}px`,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
// Remove element after layout
|
||||
renderEl.remove();
|
||||
|
||||
addGroups(groups, cy);
|
||||
addServices(services, cy);
|
||||
addJunctions(junctions, cy);
|
||||
addEdges(edges, cy);
|
||||
|
||||
// Use the spatial map to create alignment arrays for fcose
|
||||
const alignmentConstraint = getAlignments(spatialMaps);
|
||||
|
||||
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
|
||||
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
|
||||
|
||||
console.log(`Horizontal Alignments:`);
|
||||
console.log(alignmentConstraint.horizontal);
|
||||
console.log(`Vertical Alignments:`);
|
||||
console.log(alignmentConstraint.vertical);
|
||||
console.log(`Relative Alignments:`);
|
||||
console.log(relativePlacementConstraint);
|
||||
|
||||
const layout = cy.layout({
|
||||
name: 'fcose',
|
||||
quality: 'proof',
|
||||
styleEnabled: false,
|
||||
animate: false,
|
||||
nodeDimensionsIncludeLabels: false,
|
||||
// Adjust the edge parameters if it passes through the border of a group
|
||||
// Hacky fix for: https://github.com/iVis-at-Bilkent/cytoscape.js-fcose/issues/67
|
||||
idealEdgeLength(edge: EdgeSingular) {
|
||||
const [nodeA, nodeB] = edge.connectedNodes();
|
||||
const { parent: parentA } = nodeData(nodeA);
|
||||
const { parent: parentB } = nodeData(nodeB);
|
||||
const elasticity =
|
||||
parentA === parentB ? 1.5 * getConfigField('iconSize') : 0.5 * getConfigField('iconSize');
|
||||
return elasticity;
|
||||
},
|
||||
edgeElasticity(edge: EdgeSingular) {
|
||||
const [nodeA, nodeB] = edge.connectedNodes();
|
||||
const { parent: parentA } = nodeData(nodeA);
|
||||
const { parent: parentB } = nodeData(nodeB);
|
||||
const elasticity = parentA === parentB ? 0.45 : 0.001;
|
||||
return elasticity;
|
||||
},
|
||||
alignmentConstraint,
|
||||
relativePlacementConstraint,
|
||||
} as FcoseLayoutOptions);
|
||||
|
||||
// Once the diagram has been generated and the service's position cords are set, adjust the XY edges to have a 90deg bend
|
||||
layout.one('layoutstop', () => {
|
||||
function getSegmentWeights(
|
||||
source: Position,
|
||||
target: Position,
|
||||
pointX: number,
|
||||
pointY: number
|
||||
) {
|
||||
let W, D;
|
||||
const { x: sX, y: sY } = source;
|
||||
const { x: tX, y: tY } = target;
|
||||
|
||||
D =
|
||||
(pointY - sY + ((sX - pointX) * (sY - tY)) / (sX - tX)) /
|
||||
Math.sqrt(1 + Math.pow((sY - tY) / (sX - tX), 2));
|
||||
W = Math.sqrt(Math.pow(pointY - sY, 2) + Math.pow(pointX - sX, 2) - Math.pow(D, 2));
|
||||
|
||||
const distAB = Math.sqrt(Math.pow(tX - sX, 2) + Math.pow(tY - sY, 2));
|
||||
W = W / distAB;
|
||||
|
||||
//check whether the point (pointX, pointY) is on right or left of the line src to tgt. for instance : a point C(X, Y) and line (AB). d=(xB-xA)(yC-yA)-(yB-yA)(xC-xA). if d>0, then C is on left of the line. if d<0, it is on right. if d=0, it is on the line.
|
||||
let delta1 = (tX - sX) * (pointY - sY) - (tY - sY) * (pointX - sX);
|
||||
switch (true) {
|
||||
case delta1 >= 0:
|
||||
delta1 = 1;
|
||||
break;
|
||||
case delta1 < 0:
|
||||
delta1 = -1;
|
||||
break;
|
||||
}
|
||||
//check whether the point (pointX, pointY) is "behind" the line src to tgt
|
||||
let delta2 = (tX - sX) * (pointX - sX) + (tY - sY) * (pointY - sY);
|
||||
switch (true) {
|
||||
case delta2 >= 0:
|
||||
delta2 = 1;
|
||||
break;
|
||||
case delta2 < 0:
|
||||
delta2 = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
D = Math.abs(D) * delta1; //ensure that sign of D is same as sign of delta1. Hence we need to take absolute value of D and multiply by delta1
|
||||
W = W * delta2;
|
||||
|
||||
return {
|
||||
distances: D,
|
||||
weights: W,
|
||||
};
|
||||
}
|
||||
cy.startBatch();
|
||||
for (const edge of Object.values(cy.edges())) {
|
||||
if (edge.data?.()) {
|
||||
const { x: sX, y: sY } = edge.source().position();
|
||||
const { x: tX, y: tY } = edge.target().position();
|
||||
if (sX !== tX && sY !== tY) {
|
||||
const sEP = edge.sourceEndpoint();
|
||||
const tEP = edge.targetEndpoint();
|
||||
const { sourceDir } = edgeData(edge);
|
||||
const [pointX, pointY] = isArchitectureDirectionY(sourceDir)
|
||||
? [sEP.x, tEP.y]
|
||||
: [tEP.x, sEP.y];
|
||||
const { weights, distances } = getSegmentWeights(sEP, tEP, pointX, pointY);
|
||||
edge.style('segment-distances', distances);
|
||||
edge.style('segment-weights', weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
cy.endBatch();
|
||||
layout.run();
|
||||
});
|
||||
layout.run();
|
||||
|
||||
cy.ready((e) => {
|
||||
log.info('Ready', e);
|
||||
resolve(cy);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) => {
|
||||
const db = diagObj.db as ArchitectureDB;
|
||||
|
||||
const services = db.getServices();
|
||||
const junctions = db.getJunctions();
|
||||
const groups = db.getGroups();
|
||||
const edges = db.getEdges();
|
||||
const ds = db.getDataStructures();
|
||||
console.log('Services: ', services);
|
||||
console.log('Edges: ', edges);
|
||||
console.log('Groups: ', groups);
|
||||
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
|
||||
const edgesElem = svg.append('g');
|
||||
edgesElem.attr('class', 'architecture-edges');
|
||||
|
||||
const servicesElem = svg.append('g');
|
||||
servicesElem.attr('class', 'architecture-services');
|
||||
|
||||
const groupElem = svg.append('g');
|
||||
groupElem.attr('class', 'architecture-groups');
|
||||
|
||||
await drawServices(db, servicesElem, services);
|
||||
drawJunctions(db, servicesElem, junctions);
|
||||
|
||||
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
|
||||
|
||||
await drawEdges(edgesElem, cy);
|
||||
await drawGroups(groupElem, cy);
|
||||
positionNodes(db, cy);
|
||||
|
||||
setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
|
||||
|
||||
console.log('==============================================================');
|
||||
};
|
||||
|
||||
export const renderer = { draw };
|
@@ -0,0 +1,38 @@
|
||||
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
|
||||
import type { ArchitectureStyleOptions } from './architectureTypes.js';
|
||||
|
||||
const getStyles: DiagramStylesProvider = (options: ArchitectureStyleOptions) =>
|
||||
`
|
||||
.edge {
|
||||
stroke-width: ${options.archEdgeWidth};
|
||||
stroke: ${options.archEdgeColor};
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
fill: ${options.archEdgeArrowColor};
|
||||
}
|
||||
|
||||
.node-bkg {
|
||||
fill: none;
|
||||
stroke: ${options.archGroupBorderColor};
|
||||
stroke-width: ${options.archGroupBorderWidth};
|
||||
stroke-dasharray: 8;
|
||||
}
|
||||
.node-icon-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.node-icon-text > div {
|
||||
color: #fff;
|
||||
margin: 1px;
|
||||
height: fit-content;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
351
packages/mermaid/src/diagrams/architecture/architectureTypes.ts
Normal file
351
packages/mermaid/src/diagrams/architecture/architectureTypes.ts
Normal file
@@ -0,0 +1,351 @@
|
||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||
import type { ArchitectureDiagramConfig } from '../../config.type.js';
|
||||
import type { D3Element } from '../../types.js';
|
||||
import type cytoscape from 'cytoscape';
|
||||
|
||||
/*=======================================*\
|
||||
| Architecture Diagram Types |
|
||||
\*=======================================*/
|
||||
|
||||
export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B';
|
||||
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>;
|
||||
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>;
|
||||
|
||||
/**
|
||||
* Contains LL, RR, TT, BB which are impossible connections
|
||||
*/
|
||||
export type InvalidArchitectureDirectionPair = `${ArchitectureDirection}${ArchitectureDirection}`;
|
||||
export type ArchitectureDirectionPair = Exclude<
|
||||
InvalidArchitectureDirectionPair,
|
||||
'LL' | 'RR' | 'TT' | 'BB'
|
||||
>;
|
||||
export type ArchitectureDirectionPairXY = Exclude<
|
||||
InvalidArchitectureDirectionPair,
|
||||
'LL' | 'RR' | 'TT' | 'BB' | 'LR' | 'RL' | 'TB' | 'BT'
|
||||
>;
|
||||
|
||||
export const ArchitectureDirectionName = {
|
||||
L: 'left',
|
||||
R: 'right',
|
||||
T: 'top',
|
||||
B: 'bottom',
|
||||
} as const;
|
||||
|
||||
export const ArchitectureDirectionArrow = {
|
||||
L: (scale: number) => `${scale},${scale / 2} 0,${scale} 0,0`,
|
||||
R: (scale: number) => `0,${scale / 2} ${scale},0 ${scale},${scale}`,
|
||||
T: (scale: number) => `0,0 ${scale},0 ${scale / 2},${scale}`,
|
||||
B: (scale: number) => `${scale / 2},0 ${scale},${scale} 0,${scale}`,
|
||||
} as const;
|
||||
|
||||
export const ArchitectureDirectionArrowShift = {
|
||||
L: (orig: number, arrowSize: number) => orig - arrowSize + 2,
|
||||
R: (orig: number, _arrowSize: number) => orig - 2,
|
||||
T: (orig: number, arrowSize: number) => orig - arrowSize + 2,
|
||||
B: (orig: number, _arrowSize: number) => orig - 2,
|
||||
} as const;
|
||||
|
||||
export const getOppositeArchitectureDirection = function (
|
||||
x: ArchitectureDirection
|
||||
): ArchitectureDirection {
|
||||
if (isArchitectureDirectionX(x)) {
|
||||
return x === 'L' ? 'R' : 'L';
|
||||
} else {
|
||||
return x === 'T' ? 'B' : 'T';
|
||||
}
|
||||
};
|
||||
|
||||
export const isArchitectureDirection = function (x: unknown): x is ArchitectureDirection {
|
||||
const temp = x as ArchitectureDirection;
|
||||
return temp === 'L' || temp === 'R' || temp === 'T' || temp === 'B';
|
||||
};
|
||||
|
||||
export const isArchitectureDirectionX = function (
|
||||
x: ArchitectureDirection
|
||||
): x is ArchitectureDirectionX {
|
||||
const temp = x as ArchitectureDirectionX;
|
||||
return temp === 'L' || temp === 'R';
|
||||
};
|
||||
|
||||
export const isArchitectureDirectionY = function (
|
||||
x: ArchitectureDirection
|
||||
): x is ArchitectureDirectionY {
|
||||
const temp = x as ArchitectureDirectionY;
|
||||
return temp === 'T' || temp === 'B';
|
||||
};
|
||||
|
||||
export const isArchitectureDirectionXY = function (
|
||||
a: ArchitectureDirection,
|
||||
b: ArchitectureDirection
|
||||
) {
|
||||
const aX_bY = isArchitectureDirectionX(a) && isArchitectureDirectionY(b);
|
||||
const aY_bX = isArchitectureDirectionY(a) && isArchitectureDirectionX(b);
|
||||
return aX_bY || aY_bX;
|
||||
};
|
||||
|
||||
export const isArchitecturePairXY = function (
|
||||
pair: ArchitectureDirectionPair
|
||||
): pair is ArchitectureDirectionPairXY {
|
||||
const lhs = pair[0] as ArchitectureDirection;
|
||||
const rhs = pair[1] as ArchitectureDirection;
|
||||
const aX_bY = isArchitectureDirectionX(lhs) && isArchitectureDirectionY(rhs);
|
||||
const aY_bX = isArchitectureDirectionY(lhs) && isArchitectureDirectionX(rhs);
|
||||
return aX_bY || aY_bX;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies that the architecture direction pair does not contain an invalid match (LL, RR, TT, BB)
|
||||
* @param x - architecture direction pair which could potentially be invalid
|
||||
* @returns true if the pair is not LL, RR, TT, or BB
|
||||
*/
|
||||
export const isValidArchitectureDirectionPair = function (
|
||||
x: InvalidArchitectureDirectionPair
|
||||
): x is ArchitectureDirectionPair {
|
||||
return x !== 'LL' && x !== 'RR' && x !== 'TT' && x !== 'BB';
|
||||
};
|
||||
|
||||
export type ArchitectureDirectionPairMap = {
|
||||
[key in ArchitectureDirectionPair]?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a pair of the directions of each side of an edge. This function should be used instead of manually creating it to ensure that the source is always the first character.
|
||||
*
|
||||
* Note: Undefined is returned when sourceDir and targetDir are the same. In theory this should never happen since the diagram parser throws an error if a user defines it as such.
|
||||
* @param sourceDir - source direction
|
||||
* @param targetDir - target direction
|
||||
* @returns
|
||||
*/
|
||||
export const getArchitectureDirectionPair = function (
|
||||
sourceDir: ArchitectureDirection,
|
||||
targetDir: ArchitectureDirection
|
||||
): ArchitectureDirectionPair | undefined {
|
||||
const pair: `${ArchitectureDirection}${ArchitectureDirection}` = `${sourceDir}${targetDir}`;
|
||||
return isValidArchitectureDirectionPair(pair) ? pair : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an x,y position for an arrow and the direction of the edge it belongs to, return a factor for slightly shifting the edge
|
||||
* @param param0 - [x, y] coordinate pair
|
||||
* @param pair - architecture direction pair
|
||||
* @returns a new [x, y] coordinate pair
|
||||
*/
|
||||
export const shiftPositionByArchitectureDirectionPair = function (
|
||||
[x, y]: number[],
|
||||
pair: ArchitectureDirectionPair
|
||||
): number[] {
|
||||
const lhs = pair[0] as ArchitectureDirection;
|
||||
const rhs = pair[1] as ArchitectureDirection;
|
||||
if (isArchitectureDirectionX(lhs)) {
|
||||
if (isArchitectureDirectionY(rhs)) {
|
||||
return [x + (lhs === 'L' ? -1 : 1), y + (rhs === 'T' ? 1 : -1)];
|
||||
} else {
|
||||
return [x + (lhs === 'L' ? -1 : 1), y];
|
||||
}
|
||||
} else {
|
||||
if (isArchitectureDirectionX(rhs)) {
|
||||
return [x + (rhs === 'L' ? 1 : -1), y + (lhs === 'T' ? 1 : -1)];
|
||||
} else {
|
||||
return [x, y + (lhs === 'T' ? 1 : -1)];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given the directional pair of an XY edge, get the scale factors necessary to shift the coordinates inwards towards the edge
|
||||
* @param pair - XY pair of an edge
|
||||
* @returns - number[] containing [+/- 1, +/- 1]
|
||||
*/
|
||||
export const getArchitectureDirectionXYFactors = function (
|
||||
pair: ArchitectureDirectionPairXY
|
||||
): number[] {
|
||||
if (pair === 'LT' || pair === 'TL') {
|
||||
return [1, 1];
|
||||
} else if (pair === 'BL' || pair === 'LB') {
|
||||
return [1, -1];
|
||||
} else if (pair === 'BR' || pair === 'RB') {
|
||||
return [-1, -1];
|
||||
} else {
|
||||
return [-1, 1];
|
||||
}
|
||||
};
|
||||
|
||||
export interface ArchitectureStyleOptions {
|
||||
archEdgeColor: string;
|
||||
archEdgeArrowColor: string;
|
||||
archEdgeWidth: string;
|
||||
archGroupBorderColor: string;
|
||||
archGroupBorderWidth: string;
|
||||
}
|
||||
|
||||
export interface ArchitectureService {
|
||||
id: string;
|
||||
type: 'service';
|
||||
edges: ArchitectureEdge[];
|
||||
icon?: string;
|
||||
iconText?: string;
|
||||
title?: string;
|
||||
in?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface ArchitectureJunction {
|
||||
id: string;
|
||||
type: 'junction';
|
||||
edges: ArchitectureEdge[];
|
||||
in?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export type ArchitectureNode = ArchitectureService | ArchitectureJunction;
|
||||
|
||||
export const isArchitectureService = function (x: ArchitectureNode): x is ArchitectureService {
|
||||
const temp = x as ArchitectureService;
|
||||
return temp.type === 'service';
|
||||
};
|
||||
|
||||
export const isArchitectureJunction = function (x: ArchitectureNode): x is ArchitectureJunction {
|
||||
const temp = x as ArchitectureJunction;
|
||||
return temp.type === 'junction';
|
||||
};
|
||||
|
||||
export interface ArchitectureGroup {
|
||||
id: string;
|
||||
icon?: string;
|
||||
title?: string;
|
||||
in?: string;
|
||||
}
|
||||
|
||||
export interface ArchitectureEdge<DT = ArchitectureDirection> {
|
||||
lhsId: string;
|
||||
lhsDir: DT;
|
||||
lhsInto?: boolean;
|
||||
lhsGroup?: boolean;
|
||||
rhsId: string;
|
||||
rhsDir: DT;
|
||||
rhsInto?: boolean;
|
||||
rhsGroup?: boolean;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface ArchitectureDB extends DiagramDB {
|
||||
clear: () => void;
|
||||
addService: (service: Omit<ArchitectureService, 'edges'>) => void;
|
||||
getServices: () => ArchitectureService[];
|
||||
addJunction: (service: Omit<ArchitectureJunction, 'edges'>) => void;
|
||||
getJunctions: () => ArchitectureJunction[];
|
||||
getNodes: () => ArchitectureNode[];
|
||||
getNode: (id: string) => ArchitectureNode | null;
|
||||
addGroup: (group: ArchitectureGroup) => void;
|
||||
getGroups: () => ArchitectureGroup[];
|
||||
addEdge: (edge: ArchitectureEdge) => void;
|
||||
getEdges: () => ArchitectureEdge[];
|
||||
setElementForId: (id: string, element: D3Element) => void;
|
||||
getElementById: (id: string) => D3Element;
|
||||
getDataStructures: () => ArchitectureDataStructures;
|
||||
}
|
||||
|
||||
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
|
||||
export type ArchitectureSpatialMap = Record<string, number[]>;
|
||||
export interface ArchitectureDataStructures {
|
||||
adjList: ArchitectureAdjacencyList;
|
||||
spatialMaps: ArchitectureSpatialMap[];
|
||||
}
|
||||
|
||||
export interface ArchitectureState extends Record<string, unknown> {
|
||||
nodes: Record<string, ArchitectureNode>;
|
||||
groups: Record<string, ArchitectureGroup>;
|
||||
edges: ArchitectureEdge[];
|
||||
registeredIds: Record<string, 'node' | 'group'>;
|
||||
dataStructures?: ArchitectureDataStructures;
|
||||
elements: Record<string, D3Element>;
|
||||
config: ArchitectureDiagramConfig;
|
||||
}
|
||||
|
||||
/*=======================================*\
|
||||
| Cytoscape Override Types |
|
||||
\*=======================================*/
|
||||
|
||||
export interface EdgeSingularData {
|
||||
id: string;
|
||||
label?: string;
|
||||
source: string;
|
||||
sourceDir: ArchitectureDirection;
|
||||
sourceArrow?: boolean;
|
||||
sourceGroup?: boolean;
|
||||
target: string;
|
||||
targetDir: ArchitectureDirection;
|
||||
targetArrow?: boolean;
|
||||
targetGroup?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const edgeData = (edge: cytoscape.EdgeSingular) => {
|
||||
return edge.data() as EdgeSingularData;
|
||||
};
|
||||
|
||||
export interface EdgeSingular extends cytoscape.EdgeSingular {
|
||||
_private: {
|
||||
bodyBounds: unknown;
|
||||
rscratch: {
|
||||
startX: number;
|
||||
startY: number;
|
||||
midX: number;
|
||||
midY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
};
|
||||
};
|
||||
data(): EdgeSingularData;
|
||||
data<T extends keyof EdgeSingularData>(key: T): EdgeSingularData[T];
|
||||
}
|
||||
|
||||
export type NodeSingularData =
|
||||
| {
|
||||
type: 'service';
|
||||
id: string;
|
||||
icon?: string;
|
||||
label?: string;
|
||||
parent?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
| {
|
||||
type: 'junction';
|
||||
id: string;
|
||||
parent?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
| {
|
||||
type: 'group';
|
||||
id: string;
|
||||
icon?: string;
|
||||
label?: string;
|
||||
parent?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export const nodeData = (node: cytoscape.NodeSingular) => {
|
||||
return node.data() as NodeSingularData;
|
||||
};
|
||||
|
||||
export interface NodeSingular extends cytoscape.NodeSingular {
|
||||
_private: {
|
||||
bodyBounds: {
|
||||
h: number;
|
||||
w: number;
|
||||
x1: number;
|
||||
x2: number;
|
||||
y1: number;
|
||||
y2: number;
|
||||
};
|
||||
children: cytoscape.NodeSingular[];
|
||||
};
|
||||
data(): NodeSingularData;
|
||||
data<T extends keyof NodeSingularData>(key: T): NodeSingularData[T];
|
||||
}
|
366
packages/mermaid/src/diagrams/architecture/svgDraw.ts
Normal file
366
packages/mermaid/src/diagrams/architecture/svgDraw.ts
Normal file
@@ -0,0 +1,366 @@
|
||||
// TODO remove no-console
|
||||
/* eslint-disable no-console */
|
||||
import type { D3Element } from '../../types.js';
|
||||
import { createText } from '../../rendering-util/createText.js';
|
||||
import {
|
||||
ArchitectureDirectionArrow,
|
||||
type ArchitectureDB,
|
||||
type ArchitectureService,
|
||||
ArchitectureDirectionArrowShift,
|
||||
isArchitectureDirectionX,
|
||||
isArchitectureDirectionY,
|
||||
edgeData,
|
||||
nodeData,
|
||||
isArchitectureDirectionXY,
|
||||
getArchitectureDirectionPair,
|
||||
getArchitectureDirectionXYFactors,
|
||||
isArchitecturePairXY,
|
||||
type ArchitectureJunction,
|
||||
} from './architectureTypes.js';
|
||||
import type cytoscape from 'cytoscape';
|
||||
import { getIcon } from '../../rendering-util/svgRegister.js';
|
||||
import { db, getConfigField } from './architectureDb.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
|
||||
export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) {
|
||||
const padding = getConfigField('padding');
|
||||
const iconSize = getConfigField('iconSize');
|
||||
const halfIconSize = iconSize / 2;
|
||||
const arrowSize = iconSize / 6;
|
||||
const halfArrowSize = arrowSize / 2;
|
||||
|
||||
await Promise.all(
|
||||
cy.edges().map(async (edge) => {
|
||||
const {
|
||||
source,
|
||||
sourceDir,
|
||||
sourceArrow,
|
||||
sourceGroup,
|
||||
target,
|
||||
targetDir,
|
||||
targetArrow,
|
||||
targetGroup,
|
||||
label,
|
||||
} = edgeData(edge);
|
||||
let { x: startX, y: startY } = edge[0].sourceEndpoint();
|
||||
const { x: midX, y: midY } = edge[0].midpoint();
|
||||
let { x: endX, y: endY } = edge[0].targetEndpoint();
|
||||
|
||||
// Adjust the edge distance if it has the {group} modifier
|
||||
const groupEdgeShift = padding + 4;
|
||||
// +18 comes from the service label height that extends the padding on the bottom side of each group
|
||||
if (sourceGroup) {
|
||||
if (isArchitectureDirectionX(sourceDir)) {
|
||||
startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
||||
} else {
|
||||
startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetGroup) {
|
||||
if (isArchitectureDirectionX(targetDir)) {
|
||||
endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
||||
} else {
|
||||
endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node
|
||||
if (!sourceGroup && db.getNode(source)?.type === 'junction') {
|
||||
if (isArchitectureDirectionX(sourceDir)) {
|
||||
startX += sourceDir === 'L' ? halfIconSize : -halfIconSize;
|
||||
} else {
|
||||
startY += sourceDir === 'T' ? halfIconSize : -halfIconSize;
|
||||
}
|
||||
}
|
||||
if (!targetGroup && db.getNode(target)?.type === 'junction') {
|
||||
if (isArchitectureDirectionX(targetDir)) {
|
||||
endX += targetDir === 'L' ? halfIconSize : -halfIconSize;
|
||||
} else {
|
||||
endY += targetDir === 'T' ? halfIconSize : -halfIconSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (edge[0]._private.rscratch) {
|
||||
// const bounds = edge[0]._private.rscratch;
|
||||
|
||||
const g = edgesEl.insert('g');
|
||||
|
||||
g.insert('path')
|
||||
.attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `)
|
||||
.attr('class', 'edge');
|
||||
|
||||
if (sourceArrow) {
|
||||
const xShift = isArchitectureDirectionX(sourceDir)
|
||||
? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize)
|
||||
: startX - halfArrowSize;
|
||||
const yShift = isArchitectureDirectionY(sourceDir)
|
||||
? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize)
|
||||
: startY - halfArrowSize;
|
||||
|
||||
g.insert('polygon')
|
||||
.attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize))
|
||||
.attr('transform', `translate(${xShift},${yShift})`)
|
||||
.attr('class', 'arrow');
|
||||
}
|
||||
if (targetArrow) {
|
||||
const xShift = isArchitectureDirectionX(targetDir)
|
||||
? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize)
|
||||
: endX - halfArrowSize;
|
||||
const yShift = isArchitectureDirectionY(targetDir)
|
||||
? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize)
|
||||
: endY - halfArrowSize;
|
||||
|
||||
g.insert('polygon')
|
||||
.attr('points', ArchitectureDirectionArrow[targetDir](arrowSize))
|
||||
.attr('transform', `translate(${xShift},${yShift})`)
|
||||
.attr('class', 'arrow');
|
||||
}
|
||||
|
||||
if (label) {
|
||||
const axis = !isArchitectureDirectionXY(sourceDir, targetDir)
|
||||
? isArchitectureDirectionX(sourceDir)
|
||||
? 'X'
|
||||
: 'Y'
|
||||
: 'XY';
|
||||
|
||||
let width = 0;
|
||||
if (axis === 'X') {
|
||||
width = Math.abs(startX - endX);
|
||||
} else if (axis === 'Y') {
|
||||
// Reduce width by a factor of 1.5 to avoid overlapping service labels
|
||||
width = Math.abs(startY - endY) / 1.5;
|
||||
} else {
|
||||
width = Math.abs(startX - endX) / 2;
|
||||
}
|
||||
|
||||
const textElem = g.append('g');
|
||||
await createText(
|
||||
textElem,
|
||||
label,
|
||||
{
|
||||
useHtmlLabels: false,
|
||||
width,
|
||||
classes: 'architecture-service-label',
|
||||
},
|
||||
getConfig()
|
||||
);
|
||||
|
||||
textElem
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle');
|
||||
|
||||
if (axis === 'X') {
|
||||
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')');
|
||||
} else if (axis === 'Y') {
|
||||
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)');
|
||||
} else if (axis === 'XY') {
|
||||
const pair = getArchitectureDirectionPair(sourceDir, targetDir);
|
||||
if (pair && isArchitecturePairXY(pair)) {
|
||||
const bboxOrig = textElem.node().getBoundingClientRect();
|
||||
const [x, y] = getArchitectureDirectionXYFactors(pair);
|
||||
|
||||
textElem
|
||||
.attr('dominant-baseline', 'auto')
|
||||
.attr('transform', `rotate(${-1 * x * y * 45})`);
|
||||
|
||||
// Calculate the new width/height with the rotation applied, and transform to the proper position
|
||||
const bboxNew = textElem.node().getBoundingClientRect();
|
||||
textElem.attr(
|
||||
'transform',
|
||||
`
|
||||
translate(${midX}, ${midY - bboxOrig.height / 2})
|
||||
translate(${(x * bboxNew.width) / 2}, ${(y * bboxNew.height) / 2})
|
||||
rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2})
|
||||
`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) {
|
||||
const padding = getConfigField('padding');
|
||||
const groupIconSize = padding * 0.75;
|
||||
|
||||
const fontSize = getConfigField('fontSize');
|
||||
|
||||
const iconSize = getConfigField('iconSize');
|
||||
const halfIconSize = iconSize / 2;
|
||||
|
||||
await Promise.all(
|
||||
cy.nodes().map(async (node) => {
|
||||
const data = nodeData(node);
|
||||
if (data.type === 'group') {
|
||||
const { h, w, x1, y1 } = node.boundingBox();
|
||||
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`);
|
||||
|
||||
groupsEl
|
||||
.append('rect')
|
||||
.attr('x', x1 + halfIconSize)
|
||||
.attr('y', y1 + halfIconSize)
|
||||
.attr('width', w)
|
||||
.attr('height', h)
|
||||
.attr('class', 'node-bkg');
|
||||
|
||||
const groupLabelContainer = groupsEl.append('g');
|
||||
let shiftedX1 = x1;
|
||||
let shiftedY1 = y1;
|
||||
if (data.icon) {
|
||||
const bkgElem = groupLabelContainer.append('g');
|
||||
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
||||
bkgElem.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(shiftedX1 + halfIconSize + 1) +
|
||||
', ' +
|
||||
(shiftedY1 + halfIconSize + 1) +
|
||||
')'
|
||||
);
|
||||
shiftedX1 += groupIconSize;
|
||||
// TODO: test with more values
|
||||
// - 1 - 2 comes from the Y axis transform of the icon and label
|
||||
shiftedY1 += fontSize / 2 - 1 - 2;
|
||||
}
|
||||
if (data.label) {
|
||||
const textElem = groupLabelContainer.append('g');
|
||||
await createText(
|
||||
textElem,
|
||||
data.label,
|
||||
{
|
||||
useHtmlLabels: false,
|
||||
width: w,
|
||||
classes: 'architecture-service-label',
|
||||
},
|
||||
getConfig()
|
||||
);
|
||||
textElem
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'start')
|
||||
.attr('text-anchor', 'start');
|
||||
|
||||
textElem.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(shiftedX1 + halfIconSize + 4) +
|
||||
', ' +
|
||||
(shiftedY1 + halfIconSize + 2) +
|
||||
')'
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const drawServices = async function (
|
||||
db: ArchitectureDB,
|
||||
elem: D3Element,
|
||||
services: ArchitectureService[]
|
||||
): Promise<number> {
|
||||
for (const service of services) {
|
||||
const serviceElem = elem.append('g');
|
||||
const iconSize = getConfigField('iconSize');
|
||||
|
||||
if (service.title) {
|
||||
const textElem = serviceElem.append('g');
|
||||
await createText(
|
||||
textElem,
|
||||
service.title,
|
||||
{
|
||||
useHtmlLabels: false,
|
||||
width: iconSize * 1.5,
|
||||
classes: 'architecture-service-label',
|
||||
},
|
||||
getConfig()
|
||||
);
|
||||
|
||||
textElem
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle');
|
||||
|
||||
textElem.attr('transform', 'translate(' + iconSize / 2 + ', ' + iconSize + ')');
|
||||
}
|
||||
|
||||
let bkgElem = serviceElem.append('g');
|
||||
if (service.icon) {
|
||||
// TODO: should a warning be given to end-users saying which icon names are available?
|
||||
// if (!isIconNameInUse(service.icon)) {
|
||||
// throw new Error(`Invalid SVG Icon name: "${service.icon}"`);
|
||||
// }
|
||||
bkgElem = getIcon(service.icon)?.(bkgElem, iconSize);
|
||||
} else if (service.iconText) {
|
||||
bkgElem = getIcon('blank')?.(bkgElem, iconSize);
|
||||
const textElemContainer = bkgElem.append('g');
|
||||
const fo = textElemContainer
|
||||
.append('foreignObject')
|
||||
.attr('width', iconSize)
|
||||
.attr('height', iconSize);
|
||||
const divElem = fo
|
||||
.append('div')
|
||||
.attr('class', 'node-icon-text')
|
||||
.attr('style', `height: ${iconSize}px;`)
|
||||
.append('div')
|
||||
.html(service.iconText);
|
||||
const fontSize =
|
||||
parseInt(
|
||||
window
|
||||
.getComputedStyle(divElem.node(), null)
|
||||
.getPropertyValue('font-size')
|
||||
.replace(/\D/g, '')
|
||||
) ?? 16;
|
||||
divElem.attr('style', `-webkit-line-clamp: ${Math.floor((iconSize - 2) / fontSize)};`);
|
||||
} else {
|
||||
bkgElem
|
||||
.append('path')
|
||||
.attr('class', 'node-bkg')
|
||||
.attr('id', 'node-' + service.id)
|
||||
.attr(
|
||||
'd',
|
||||
`M0 ${iconSize} v${-iconSize} q0,-5 5,-5 h${iconSize} q5,0 5,5 v${iconSize} H0 Z`
|
||||
);
|
||||
}
|
||||
|
||||
serviceElem.attr('class', 'architecture-service');
|
||||
|
||||
const { width, height } = serviceElem._groups[0][0].getBBox();
|
||||
service.width = width;
|
||||
service.height = height;
|
||||
db.setElementForId(service.id, serviceElem);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const drawJunctions = function (
|
||||
db: ArchitectureDB,
|
||||
elem: D3Element,
|
||||
junctions: ArchitectureJunction[]
|
||||
) {
|
||||
junctions.forEach((junction) => {
|
||||
const junctionElem = elem.append('g');
|
||||
const iconSize = getConfigField('iconSize');
|
||||
|
||||
const bkgElem = junctionElem.append('g');
|
||||
bkgElem
|
||||
.append('rect')
|
||||
.attr('id', 'node-' + junction.id)
|
||||
.attr('fill-opacity', '0')
|
||||
.attr('width', iconSize)
|
||||
.attr('height', iconSize);
|
||||
|
||||
junctionElem.attr('class', 'architecture-junction');
|
||||
|
||||
const { width, height } = junctionElem._groups[0][0].getBBox();
|
||||
junctionElem.width = width;
|
||||
junctionElem.height = height;
|
||||
db.setElementForId(junction.id, junctionElem);
|
||||
});
|
||||
};
|
@@ -7,13 +7,14 @@ import type {
|
||||
const id = 'flowchart-v2';
|
||||
|
||||
const detector: DiagramDetector = (txt, config) => {
|
||||
if (
|
||||
config?.flowchart?.defaultRenderer === 'dagre-d3' ||
|
||||
config?.flowchart?.defaultRenderer === 'elk'
|
||||
) {
|
||||
if (config?.flowchart?.defaultRenderer === 'dagre-d3') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config?.flowchart?.defaultRenderer === 'elk') {
|
||||
config.layout = 'elk';
|
||||
}
|
||||
|
||||
// If we have configured to use dagre-wrapper then we should return true in this function for graph code thus making it use the new flowchart diagram
|
||||
if (/^\s*graph/.test(txt) && config?.flowchart?.defaultRenderer === 'dagre-wrapper') {
|
||||
return true;
|
||||
|
@@ -157,6 +157,7 @@ function sidebarSyntax() {
|
||||
{ text: 'XY Chart 🔥', link: '/syntax/xyChart' },
|
||||
{ text: 'Block Diagram 🔥', link: '/syntax/block' },
|
||||
{ text: 'Packet 🔥', link: '/syntax/packet' },
|
||||
{ text: 'Architecture 🔥', link: '/syntax/architecture' },
|
||||
{ text: 'Other Examples', link: '/syntax/examples' },
|
||||
],
|
||||
},
|
||||
@@ -176,6 +177,7 @@ function sidebarConfig() {
|
||||
{ text: 'Directives', link: '/config/directives' },
|
||||
{ text: 'Theming', link: '/config/theming' },
|
||||
{ text: 'Math', link: '/config/math' },
|
||||
{ text: 'Icons', link: '/config/icons' },
|
||||
{ text: 'Accessibility', link: '/config/accessibility' },
|
||||
{ text: 'Mermaid CLI', link: '/config/mermaidCLI' },
|
||||
{ text: 'FAQ', link: '/config/faq' },
|
||||
|
61
packages/mermaid/src/docs/config/icons.md
Normal file
61
packages/mermaid/src/docs/config/icons.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# SVG Icons (v???+)
|
||||
|
||||
SVG Icons can be used with supported diagrams. Alongside the icon packs included with Mermaid, 3rd party libraries can be included in the configuration to cover additional use-cases.
|
||||
|
||||
## Supported Diagrams
|
||||
|
||||
| Diagram | Usage |
|
||||
| ------------ | --------------------------------- |
|
||||
| Architecture | Icon names are surrounded by `()` |
|
||||
|
||||
## Included Icon Packs
|
||||
|
||||
| Icon Pack | Prefix |
|
||||
| ------------- | ------ |
|
||||
| default | N/A |
|
||||
| Amazon AWS | `aws:` |
|
||||
| Digital Ocean | `do:` |
|
||||
| GitHub | `gh:` |
|
||||
|
||||
Note that in order to use non-generic icons that are provided with Mermaid, the packs must be explicitly loaded when on initialization initialized.
|
||||
|
||||
```js
|
||||
import sampleIconPack from 'sample-icon-pack';
|
||||
|
||||
mermaid.initialize({
|
||||
iconLibraries: ['aws:common', 'aws:full', 'github', 'digital-ocean'],
|
||||
});
|
||||
```
|
||||
|
||||
## Using Custom Icon Packs
|
||||
|
||||
Custom icon packs can be used by including them in the `iconLibraries` array on mermaid initialization.
|
||||
|
||||
```js
|
||||
import sampleIconPack from 'sample-icon-pack';
|
||||
|
||||
mermaid.initialize({
|
||||
iconLibraries: [sampleIconPack, 'aws:full', ...],
|
||||
});
|
||||
```
|
||||
|
||||
## Creating Custom Icon Packs
|
||||
|
||||
```js
|
||||
import { createIcon } from 'mermaid';
|
||||
import type { IconLibrary, IconResolver } from 'mermaid';
|
||||
|
||||
// type IconLibrary = Record<string, IconResolver>;
|
||||
// createIcon: (icon: string, originalSize: number) => IconResolver
|
||||
const myIconLibrary: IconLibrary = {
|
||||
defaultCloudExample: createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<path d="..." style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`,
|
||||
80
|
||||
)
|
||||
};
|
||||
|
||||
export default myIconLibrary
|
||||
```
|
@@ -50,6 +50,10 @@ For a more detailed introduction to Mermaid and some of its more basic uses, loo
|
||||
|
||||
**Thanks to all involved, people committing pull requests, people answering questions and special thanks to Tyler Long who is helping me maintain the project 🙏**
|
||||
|
||||
Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze.
|
||||
|
||||
[](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
|
||||
|
||||
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
|
||||
|
||||
<a href="https://applitools.com/">
|
||||
|
156
packages/mermaid/src/docs/syntax/architecture.md
Normal file
156
packages/mermaid/src/docs/syntax/architecture.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Architecture Diagrams Documentation (v<MERMAID_RELEASE_VERSION>+)
|
||||
|
||||
> In the context of mermaid-js, the architecture diagram is used to show the relationship between services and resources commonly found within the Cloud or CI/CD deployments. In an architecture diagram, services (nodes) are connected by edges. Related services can be placed within groups to better illustrate how they are organized.
|
||||
|
||||
## Example
|
||||
|
||||
```mermaid-example
|
||||
architecture-beta
|
||||
group api(cloud)[API]
|
||||
|
||||
service db(database)[Database] in api
|
||||
service disk1(disk)[Storage] in api
|
||||
service disk2(disk)[Storage] in api
|
||||
service server(server)[Server] in api
|
||||
|
||||
db:L -- R:server
|
||||
disk1:T -- B:server
|
||||
disk2:T -- B:db
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The building blocks of an architecture are `groups`, `services`, `edges`, and `junctions`.
|
||||
|
||||
For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`.
|
||||
|
||||
To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component.
|
||||
|
||||
### Groups
|
||||
|
||||
The syntax for declaring a group is:
|
||||
|
||||
```
|
||||
group {group id}({icon name})[{title}] (in {parent id})?
|
||||
```
|
||||
|
||||
Put together:
|
||||
|
||||
```
|
||||
group public_api(cloud)[Public API]
|
||||
```
|
||||
|
||||
creates a group identified as `public_api`, uses the icon `cloud`, and has the label `Public API`.
|
||||
|
||||
Additionally, groups can be placed within a group using the optional `in` keyword
|
||||
|
||||
```
|
||||
group private_api(cloud)[Private API] in public_api
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
The syntax for declaring a service is:
|
||||
|
||||
```
|
||||
service {service id}({icon name})[{title}] (in {parent id})?
|
||||
```
|
||||
|
||||
Put together:
|
||||
|
||||
```
|
||||
service database(db)[Database]
|
||||
```
|
||||
|
||||
creates the service identified as `database`, using the icon `db`, with the label `Database`.
|
||||
|
||||
If the service belongs to a group, it can be placed inside it through the optional `in` keyword
|
||||
|
||||
```
|
||||
service database(db)[Database] in private_api
|
||||
```
|
||||
|
||||
### Edges
|
||||
|
||||
The syntax for declaring an edge is:
|
||||
|
||||
```
|
||||
{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}?
|
||||
```
|
||||
|
||||
#### Edge Direction
|
||||
|
||||
The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B`
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
db:R -- L:server
|
||||
```
|
||||
|
||||
creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`.
|
||||
|
||||
```
|
||||
db:T -- L:server
|
||||
```
|
||||
|
||||
creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`.
|
||||
|
||||
#### Arrows
|
||||
|
||||
Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
subnet:R --> L:gateway
|
||||
```
|
||||
|
||||
creates an edge with the arrow going into the `gateway` service
|
||||
|
||||
#### Edges out of Groups
|
||||
|
||||
To have an edge go from a group to another group or service within another group, the `{group}` modifier can be added after the `serviceId`.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
service server[Server] in groupOne
|
||||
service subnet[Subnet] in groupTwo
|
||||
|
||||
server{group}:B --> T:subnet{group}
|
||||
```
|
||||
|
||||
creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`.
|
||||
|
||||
It's important to note that `groupId`s cannot be used for specifying edges and the `{group}` modifier can only be used for services within a group.
|
||||
|
||||
### Junctions
|
||||
|
||||
Junctions are a special type of node which acts as a potential 4-way split between edges.
|
||||
|
||||
The syntax for declaring a junction is:
|
||||
|
||||
```
|
||||
junction {junction id} (in {parent id})?
|
||||
```
|
||||
|
||||
```mermaid-example
|
||||
architecture-beta
|
||||
service left_disk(disk)[Disk]
|
||||
service top_disk(disk)[Disk]
|
||||
service bottom_disk(disk)[Disk]
|
||||
service top_gateway(internet)[Gateway]
|
||||
service bottom_gateway(internet)[Gateway]
|
||||
junction junctionCenter
|
||||
junction junctionRight
|
||||
|
||||
left_disk:R -- L:junctionCenter
|
||||
top_disk:B -- T:junctionCenter
|
||||
bottom_disk:T -- B:junctionCenter
|
||||
junctionCenter:R -- L:junctionRight
|
||||
top_gateway:B -- T:junctionRight
|
||||
bottom_gateway:T -- B:junctionRight
|
||||
```
|
||||
|
||||
## Configuration
|
@@ -19,6 +19,8 @@ import type { LayoutData } from './rendering-util/types.js';
|
||||
import type { ParseOptions, ParseResult, RenderResult } from './types.js';
|
||||
import type { DetailedError } from './utils.js';
|
||||
import utils, { isDetailedError } from './utils.js';
|
||||
import type { IconLibrary, IconResolver } from './rendering-util/svgRegister.js';
|
||||
import { createIcon } from './rendering-util/svgRegister.js';
|
||||
|
||||
export type {
|
||||
DetailedError,
|
||||
@@ -35,8 +37,12 @@ export type {
|
||||
SVG,
|
||||
SVGGroup,
|
||||
UnknownDiagramError,
|
||||
IconLibrary,
|
||||
IconResolver,
|
||||
};
|
||||
|
||||
export { createIcon };
|
||||
|
||||
export interface RunOptions {
|
||||
/**
|
||||
* The query selector to use when finding elements to render. Default: `".mermaid"`.
|
||||
|
@@ -23,6 +23,9 @@ import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.
|
||||
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
|
||||
import { preprocessDiagram } from './preprocess.js';
|
||||
import { decodeEntities } from './utils.js';
|
||||
import type { IconLibrary } from './rendering-util/svgRegister.js';
|
||||
import { registerIcons } from './rendering-util/svgRegister.js';
|
||||
import defaultIconLibrary from './rendering-util/svg/index.js';
|
||||
import { toBase64 } from './utils/base64.js';
|
||||
import type { D3Element, ParseOptions, ParseResult, RenderResult } from './types.js';
|
||||
import assignWithDepth from './assignWithDepth.js';
|
||||
@@ -477,7 +480,7 @@ const render = async function (
|
||||
* @param userOptions - Initial Mermaid options
|
||||
*/
|
||||
function initialize(userOptions: MermaidConfig = {}) {
|
||||
const options = assignWithDepth({}, userOptions);
|
||||
const options: MermaidConfig = assignWithDepth({}, userOptions);
|
||||
// Handle legacy location of font-family configuration
|
||||
if (options?.fontFamily && !options.themeVariables?.fontFamily) {
|
||||
if (!options.themeVariables) {
|
||||
@@ -489,6 +492,29 @@ function initialize(userOptions: MermaidConfig = {}) {
|
||||
// Set default options
|
||||
configApi.saveConfigFromInitialize(options);
|
||||
|
||||
registerIcons(defaultIconLibrary);
|
||||
if (options?.iconLibraries) {
|
||||
// TODO: find a better way to handle this, assumed to be resolved by the time diagrams are being generated
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
options.iconLibraries.forEach(async (library) => {
|
||||
if (typeof library === 'string') {
|
||||
let lib: IconLibrary = {};
|
||||
if (library === 'aws:common') {
|
||||
lib = (await import('./rendering-util/svg/aws/awsCommon.js')).default;
|
||||
} else if (library === 'aws:full') {
|
||||
lib = (await import('./rendering-util/svg/aws/awsFull.js')).default;
|
||||
} else if (library === 'digital-ocean') {
|
||||
lib = (await import('./rendering-util/svg/digital-ocean/digitalOcean.js')).default;
|
||||
} else if (library === 'github') {
|
||||
lib = (await import('./rendering-util/svg/github/github.js')).default;
|
||||
}
|
||||
registerIcons(lib);
|
||||
} else {
|
||||
registerIcons(library);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (options?.theme && options.theme in theme) {
|
||||
// Todo merge with user options
|
||||
options.themeVariables = theme[options.theme as keyof typeof theme].getThemeVariables(
|
||||
|
@@ -111,7 +111,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
if (graph.children(v).length > 0) {
|
||||
// This is a cluster but not to be rendered recursively
|
||||
// Render as before
|
||||
log.info(
|
||||
log.trace(
|
||||
'Cluster - the non recursive path XBX',
|
||||
v,
|
||||
node.id,
|
||||
@@ -120,11 +120,11 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
||||
'Graph:',
|
||||
graph
|
||||
);
|
||||
log.info(findNonClusterChild(node.id, graph));
|
||||
log.trace(findNonClusterChild(node.id, graph));
|
||||
clusterDb.set(node.id, { id: findNonClusterChild(node.id, graph), node });
|
||||
// insertCluster(clusters, graph.node(v));
|
||||
} else {
|
||||
log.warn('Node - the non recursive path XAX', v, nodes, graph.node(v), dir);
|
||||
log.trace('Node - the non recursive path XAX', v, nodes, graph.node(v), dir);
|
||||
await insertNode(nodes, graph.node(v), dir);
|
||||
}
|
||||
}
|
||||
|
237
packages/mermaid/src/rendering-util/svg/aws/awsCommon.ts
Normal file
237
packages/mermaid/src/rendering-util/svg/aws/awsCommon.ts
Normal file
File diff suppressed because one or more lines are too long
2816
packages/mermaid/src/rendering-util/svg/aws/awsFull.ts
Normal file
2816
packages/mermaid/src/rendering-util/svg/aws/awsFull.ts
Normal file
File diff suppressed because one or more lines are too long
11
packages/mermaid/src/rendering-util/svg/default/blank.ts
Normal file
11
packages/mermaid/src/rendering-util/svg/default/blank.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
</g>`,
|
||||
80
|
||||
);
|
13
packages/mermaid/src/rendering-util/svg/default/cloud.ts
Normal file
13
packages/mermaid/src/rendering-util/svg/default/cloud.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<path d="m65,47.5c0,2.76-2.24,5-5,5H20c-2.76,0-5-2.24-5-5,0-1.87,1.03-3.51,2.56-4.36-.04-.21-.06-.42-.06-.64,0-2.6,2.48-4.74,5.65-4.97,1.65-4.51,6.34-7.76,11.85-7.76.86,0,1.69.08,2.5.23,2.09-1.57,4.69-2.5,7.5-2.5,6.1,0,11.19,4.38,12.28,10.17,2.14.56,3.72,2.51,3.72,4.83,0,.03,0,.07-.01.1,2.29.46,4.01,2.48,4.01,4.9Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`,
|
||||
80
|
||||
);
|
18
packages/mermaid/src/rendering-util/svg/default/database.ts
Normal file
18
packages/mermaid/src/rendering-util/svg/default/database.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<path id="b" data-name="4" d="m20,57.86c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path id="c" data-name="3" d="m20,45.95c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path id="d" data-name="2" d="m20,34.05c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse id="e" data-name="1" cx="40" cy="22.14" rx="20" ry="7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="20" y1="57.86" x2="20" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="60" y1="57.86" x2="60" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`,
|
||||
80
|
||||
);
|
20
packages/mermaid/src/rendering-util/svg/default/disk.ts
Normal file
20
packages/mermaid/src/rendering-util/svg/default/disk.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<rect x="20" y="15" width="40" height="50" rx="1" ry="1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="24" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="56" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="24" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="56" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="40" cy="33.75" rx="14" ry="14.58" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="40" cy="33.75" rx="4" ry="4.17" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path d="m37.51,42.52l-4.83,13.22c-.26.71-1.1,1.02-1.76.64l-4.18-2.42c-.66-.38-.81-1.26-.33-1.84l9.01-10.8c.88-1.05,2.56-.08,2.09,1.2Z" style="fill: #fff; stroke-width: 0px;"/>
|
||||
</g>`,
|
||||
80
|
||||
);
|
19
packages/mermaid/src/rendering-util/svg/default/internet.ts
Normal file
19
packages/mermaid/src/rendering-util/svg/default/internet.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<circle cx="40" cy="40" r="22.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="40" y1="17.5" x2="40" y2="62.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="17.5" y1="40" x2="62.5" y2="40" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path d="m39.99,17.51c-15.28,11.1-15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path d="m40.01,17.51c15.28,11.1,15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="19.75" y1="30.1" x2="60.25" y2="30.1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="19.75" y1="49.9" x2="60.25" y2="49.9" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`,
|
||||
80
|
||||
);
|
42
packages/mermaid/src/rendering-util/svg/default/server.ts
Normal file
42
packages/mermaid/src/rendering-util/svg/default/server.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<rect x="17.5" y="17.5" width="45" height="45" rx="2" ry="2" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="17.5" y1="32.5" x2="62.5" y2="32.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="17.5" y1="47.5" x2="62.5" y2="47.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<g>
|
||||
<path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/>
|
||||
<path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/>
|
||||
<path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/>
|
||||
<path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle cx="32.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="27.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="22.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle cx="32.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="27.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="22.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
<g>
|
||||
<circle cx="32.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="27.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="22.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
</g>`,
|
||||
80
|
||||
);
|
13
packages/mermaid/src/rendering-util/svg/default/unknown.ts
Normal file
13
packages/mermaid/src/rendering-util/svg/default/unknown.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Designer: Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<text transform="translate(21.16 64.67)" style="fill: #fff; font-family: ArialMT, Arial; font-size: 67.75px;"><tspan x="0" y="0">?</tspan></text>
|
||||
</g>`,
|
||||
80
|
||||
);
|
@@ -0,0 +1,321 @@
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
// cSpell:disable
|
||||
const digitalOceanIcons = {
|
||||
'do:api': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}.cls-4{font-size:16px;fill:#fff;font-family:ProximaNova-Extrabld,
|
||||
Proxima
|
||||
Nova;}.cls-5{letter-spacing:0em;}.cls-6{letter-spacing:-0.01em;}.cls-7{letter-spacing:0em;}.cls-8{letter-spacing:0em;}.cls-9{letter-spacing:0em;}.cls-10{letter-spacing:0em;}.cls-11{letter-spacing:0em;}.cls-12{letter-spacing:0em;}.cls-13{letter-spacing:0em;}.cls-14{letter-spacing:-0.01em;}.cls-15{letter-spacing:-0.02em;}.cls-16{letter-spacing:0em;}.cls-17{letter-spacing:0em;}.cls-18{letter-spacing:0em;}.cls-19{letter-spacing:0em;}.cls-20{letter-spacing:-0.08em;}.cls-21{letter-spacing:0em;}.cls-22{letter-spacing:0em;}.cls-23{letter-spacing:0em;}.cls-24{letter-spacing:0em;}.cls-25{letter-spacing:0em;}.cls-26{letter-spacing:0em;}.cls-27{letter-spacing:0em;}.cls-28{letter-spacing:-0.01em;}.cls-29{letter-spacing:0em;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M32.8,22.44V19.65l-3.09-1a9.47,9.47,0,0,0-.89-2.1l1.51-2.9-2-2-2.88,1.45h0a9.45,9.45,0,0,0-2-.88h0l-1-3.07H19.59l-1,3.11a9.39,9.39,0,0,0-2.09.89h0l-2.89-1.5-2,2,1.5,2.89h0a8.91,8.91,0,0,0-.88,2L9.2,19.65v2.83l3.1,1h0a9.07,9.07,0,0,0,.82,2h0l-1.46,2.87,2,2,2.9-1.51a9,9,0,0,0,2,.89l1,3.11H22.4l1-3.08h0a8.65,8.65,0,0,0,2-.82h0l2.89,1.51,2-2.06L28.8,25.49a9.48,9.48,0,0,0,.88-2.07Zm-11.8,2A3.45,3.45,0,1,1,24.44,21v0A3.44,3.44,0,0,1,21,24.42Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:cli': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<rect class="cls-3" x="9.89" y="14.16" width="22.22" height="13.68" />
|
||||
<polyline class="cls-3" points="14.34 24.5 17.84 21 14.34 17.5" />
|
||||
<line class="cls-3" x1="20.13" y1="23.89" x2="27.53" y2="23.89" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:dns': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<circle class="cls-3" cx="21" cy="21" r="11.87" />
|
||||
<line class="cls-3" x1="21" y1="9.13" x2="21" y2="32.87" />
|
||||
<line class="cls-3" x1="9.81" y1="17.04" x2="32.19" y2="17.04" />
|
||||
<line class="cls-3" x1="9.81" y1="24.96" x2="32.19" y2="24.96" />
|
||||
<path class="cls-3" d="M21,32.87c-3.27,0-5.93-8.6-5.93-11.87S17.73,9.13,21,9.13" />
|
||||
<path class="cls-3" d="M21.09,9.13C24.36,9.13,27,17.73,27,21s-2.66,11.87-5.93,11.87" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:droplet': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M21,33.06A10.2,10.2,0,0,1,10.83,23C10.83,14.61,21,8.94,21,8.94s10.17,5.87,10.17,14.15A10.14,10.14,0,0,1,21,33.06Z" />
|
||||
<path class="cls-3"
|
||||
d="M21,26.28a3.37,3.37,0,0,1-3.39-3.39c0-2.61,3.39-5.09,3.39-5.09s3.39,2.42,3.39,5.09A3.37,3.37,0,0,1,21,26.28Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:kubernetes': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M31.15,21.18A10.19,10.19,0,1,1,21,11,10.19,10.19,0,0,1,31.15,21.18Zm-6.51,0A3.64,3.64,0,1,1,21,17.56,3.64,3.64,0,0,1,24.64,21.2ZM21,17.47v0ZM10.1,12.33,18,18.79Zm21.79,0L24,18.79ZM34.72,24.2,24.75,22Zm-27.44,0,10-2.18Zm19.93,9.62-4.53-9.43Zm-12.41,0,4.45-9.43Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:local-ssd': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M21,33.06A10.2,10.2,0,0,1,10.83,23C10.83,14.61,21,8.94,21,8.94s10.17,5.87,10.17,14.15A10.14,10.14,0,0,1,21,33.06Z" />
|
||||
<path class="cls-3"
|
||||
d="M21,26.28a3.37,3.37,0,0,1-3.39-3.39c0-2.61,3.39-5.09,3.39-5.09s3.39,2.42,3.39,5.09A3.37,3.37,0,0,1,21,26.28Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:mysql': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-4{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3,.cls-5{fill:#fff;}.cls-4{stroke:#fff;stroke-width:1.49px;}.cls-5{font-size:16px;font-family:ProximaNova-Extrabld,
|
||||
Proxima
|
||||
Nova;}.cls-6{letter-spacing:0em;}.cls-7{letter-spacing:-0.01em;}.cls-8{letter-spacing:0em;}.cls-9{letter-spacing:0em;}.cls-10{letter-spacing:0em;}.cls-11{letter-spacing:0em;}.cls-12{letter-spacing:0em;}.cls-13{letter-spacing:0em;}.cls-14{letter-spacing:0em;}.cls-15{letter-spacing:-0.01em;}.cls-16{letter-spacing:-0.02em;}.cls-17{letter-spacing:0em;}.cls-18{letter-spacing:0em;}.cls-19{letter-spacing:0em;}.cls-20{letter-spacing:0em;}.cls-21{letter-spacing:-0.08em;}.cls-22{letter-spacing:0em;}.cls-23{letter-spacing:0em;}.cls-24{letter-spacing:0em;}.cls-25{letter-spacing:0em;}.cls-26{letter-spacing:0em;}.cls-27{letter-spacing:0em;}.cls-28{letter-spacing:0em;}.cls-29{letter-spacing:-0.01em;}.cls-30{letter-spacing:0em;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M22.15,29V21.86a.63.63,0,0,1,.59-.58.86.86,0,0,1,.84.33c.2.36.43.72.64,1.08.37.62.84,1.36,1.21,2,.37-.6.85-1.34,1.21-2,.2-.36.43-.72.64-1.08a.86.86,0,0,1,.84-.33.65.65,0,0,1,.61.58V29a.69.69,0,0,1-.77.59A.68.68,0,0,1,27.2,29v-4.5c-.28.44-.55.88-.75,1.22l-.35.56a.81.81,0,0,1-.69.35.76.76,0,0,1-.7-.35l-.35-.56c-.2-.34-.48-.78-.73-1.22V29a.71.71,0,0,1-.77.59A.69.69,0,0,1,22.15,29Z" />
|
||||
<path class="cls-4" d="M9.65,20.87v4.34c0,1.43,4,2.59,8.89,2.59" />
|
||||
<path class="cls-4" d="M27.41,19V16.57" />
|
||||
<path class="cls-4" d="M9.65,16.57v4.34c0,1.43,4,2.58,8.89,2.58H19" />
|
||||
<path class="cls-4"
|
||||
d="M27.41,12.24c0-1.42-4-2.58-8.87-2.58s-8.89,1.16-8.89,2.58v4.33c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59Z" />
|
||||
<ellipse class="cls-4" cx="18.54" cy="12.24" rx="8.88" ry="2.58" />
|
||||
<path class="cls-4"
|
||||
d="M27.41,12.24c0-1.42-4-2.58-8.87-2.58s-8.89,1.16-8.89,2.58v4.33c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59Z" />
|
||||
<path class="cls-4"
|
||||
d="M13.89,14.44c-2.54-.46-4.23-1.27-4.23-2.2,0-1.43,4-2.58,8.88-2.58s8.87,1.15,8.87,2.58-4,2.58-8.87,2.58a27,27,0,0,1-4.65-.38" />
|
||||
<circle class="cls-4" cx="25.44" cy="25.43" r="6.91" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:redis-copy': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}.cls-4{fill:#fff;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3" d="M11.59,23.14v4.34c0,1.43,4,2.59,8.89,2.59" />
|
||||
<path class="cls-3" d="M29.35,23.14V18.85" />
|
||||
<path class="cls-3" d="M11.59,23.14V18.85" />
|
||||
<path class="cls-3"
|
||||
d="M29.35,14.51c0-1.42-4-2.58-8.87-2.58s-8.89,1.16-8.89,2.58v4.34c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59Z" />
|
||||
<ellipse class="cls-3" cx="20.48" cy="14.51" rx="8.88" ry="2.58" />
|
||||
<path class="cls-3"
|
||||
d="M29.35,14.51c0-1.42-4-2.58-8.87-2.58s-8.89,1.16-8.89,2.58v4.34c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59Z" />
|
||||
<path class="cls-3"
|
||||
d="M15.83,16.71c-2.54-.45-4.23-1.27-4.23-2.2,0-1.43,4-2.58,8.88-2.58s8.87,1.15,8.87,2.58-4,2.58-8.87,2.58a27,27,0,0,1-4.65-.38" />
|
||||
<path class="cls-3" d="M11.59,23.14v4.34c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59V23.14" />
|
||||
<path class="cls-3" d="M29.35,23.14c0,1.43-4,2.58-8.87,2.58a26,26,0,0,1-4.65-.38" />
|
||||
<path class="cls-3" d="M15.83,25.34c-2.54-.45-4.23-1.27-4.23-2.2" />
|
||||
<path class="cls-3" d="M29.35,27.49c0,1.43-4,2.58-8.87,2.58a27,27,0,0,1-4.65-.38" />
|
||||
<path class="cls-3" d="M15.83,29.69c-2.54-.45-4.23-1.27-4.23-2.2" />
|
||||
<path class="cls-4" d="M30.41,26.2v0Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:redis': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}.cls-4{fill:#fff;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3" d="M9.65,20.87v4.34c0,1.43,4,2.59,8.89,2.59" />
|
||||
<path class="cls-3" d="M27.41,19V16.57" />
|
||||
<path class="cls-3" d="M9.65,16.57v4.34c0,1.43,4,2.58,8.89,2.58H19" />
|
||||
<path class="cls-3"
|
||||
d="M27.41,12.24c0-1.42-4-2.58-8.87-2.58s-8.89,1.16-8.89,2.58v4.33c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59Z" />
|
||||
<ellipse class="cls-3" cx="18.54" cy="12.24" rx="8.88" ry="2.58" />
|
||||
<path class="cls-3"
|
||||
d="M27.41,12.24c0-1.42-4-2.58-8.87-2.58s-8.89,1.16-8.89,2.58v4.33c0,1.43,4,2.59,8.89,2.59s8.87-1.16,8.87-2.59Z" />
|
||||
<path class="cls-3"
|
||||
d="M13.89,14.44c-2.54-.46-4.23-1.27-4.23-2.2,0-1.43,4-2.58,8.88-2.58s8.87,1.15,8.87,2.58-4,2.58-8.87,2.58a27,27,0,0,1-4.65-.38" />
|
||||
<circle class="cls-3" cx="25.44" cy="25.43" r="6.91" />
|
||||
<path class="cls-4" d="M28.47,23.93v0Z" />
|
||||
<path class="cls-4"
|
||||
d="M28.47,24a2.68,2.68,0,0,0-2.68-2.65H23.88a.75.75,0,0,0-.57.21.77.77,0,0,0-.23.57v6.73a.77.77,0,0,0,1.53,0V26.62h.53l1.5,2.58a.76.76,0,0,0,.66.37.75.75,0,0,0,.38-.1.76.76,0,0,0,.27-1l-1.17-2a2.78,2.78,0,0,0,.9-.58A2.72,2.72,0,0,0,28.47,24Zm-3.86-1.12h1.14a1.18,1.18,0,0,1,.82.33,1.11,1.11,0,0,1,0,1.58,1.14,1.14,0,0,1-.82.33H24.61Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:monitoring': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3" d="M29.6,14.92v12.7a1.5,1.5,0,0,1-1.51,1.51H14.79" />
|
||||
<path class="cls-3" d="M12.4,26.74V14.38a1.51,1.51,0,0,1,1.51-1.51H27.47" />
|
||||
<line class="cls-3" x1="29.57" y1="21" x2="12.36" y2="21" />
|
||||
<path class="cls-3"
|
||||
d="M13.5,27l5-9.82a.33.33,0,0,1,.45-.14.32.32,0,0,1,.14.14l3.74,7.61a.33.33,0,0,0,.45.13.29.29,0,0,0,.14-.13l5.09-9.88" />
|
||||
<circle class="cls-3" cx="12.4" cy="29.13" r="2.39" />
|
||||
<circle class="cls-3" cx="29.6" cy="12.87" r="2.39" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:projects': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{fill:none;stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{fill:#fff;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M28.45,12.75H25.76A2.79,2.79,0,0,0,23,10.41h0a2.26,2.26,0,0,0,.27-1h0a2.27,2.27,0,0,0-4.53,0,2.39,2.39,0,0,0,.27,1h0a2.79,2.79,0,0,0-2.75,2.34h-2.7A1.68,1.68,0,0,0,12,14.52V31a1.68,1.68,0,0,0,1.6,1.74H28.45A1.68,1.68,0,0,0,30,31V14.5A1.68,1.68,0,0,0,28.45,12.75ZM20,9.37a1,1,0,0,1,2.09,0h0a1,1,0,0,1-1,1H21A1,1,0,0,1,20,9.37Zm-1,2.26h4a1.57,1.57,0,0,1,1.51,1.12h-7A1.57,1.57,0,0,1,19,11.63ZM28.82,31c0,.29-.17.53-.37.53H13.56c-.2,0-.38-.24-.38-.53V14.5c0-.28.18-.53.38-.53H28.45c.2,0,.37.25.37.53Z" />
|
||||
<rect class="cls-3" x="19.02" y="18.88" width="6.77" height="1.22" />
|
||||
<rect class="cls-3" x="19.02" y="22.18" width="6.77" height="1.22" />
|
||||
<rect class="cls-3" x="19.02" y="25.49" width="6.77" height="1.22" />
|
||||
<rect class="cls-3" x="16.21" y="18.88" width="1.18" height="1.22" />
|
||||
<rect class="cls-3" x="16.21" y="22.18" width="1.18" height="1.22" />
|
||||
<rect class="cls-3" x="16.21" y="25.49" width="1.18" height="1.22" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:spaces-cdn': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{fill:none;stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{fill:#fff;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3"
|
||||
d="M12.7,13.49a1.22,1.22,0,0,0,.66-.2,1,1,0,0,0,.33-.68,1.22,1.22,0,0,0-.19-.69.88.88,0,0,0-.69-.33h0a1,1,0,0,0-1,.86,1.16,1.16,0,0,0,.21.68A.91.91,0,0,0,12.7,13.49Zm-2.6,3.89a1,1,0,0,0,1-.87,1.26,1.26,0,0,0-.2-.68.85.85,0,0,0-.69-.34h-.06a1,1,0,0,0-.95.87,1.12,1.12,0,0,0,.21.68,1.09,1.09,0,0,0,.67.34ZM10.17,21a1.26,1.26,0,0,0-.2-.68A.9.9,0,0,0,9.28,20H9.22a1,1,0,0,0-.62.21.87.87,0,0,0-.33.69,1.08,1.08,0,0,0,.2.67.84.84,0,0,0,.68.35.87.87,0,0,0,1-.7.61.61,0,0,0,0-.19Zm0,3.61a1.07,1.07,0,0,0-.69.21.88.88,0,0,0-.34.69,1.16,1.16,0,0,0,.21.67.87.87,0,0,0,.68.35,1.15,1.15,0,0,0,.72-.21.84.84,0,0,0,.34-.68.87.87,0,0,0-.69-1Zm2.67,3.9a1,1,0,0,0-1,.86,1.2,1.2,0,0,0,.21.69.89.89,0,0,0,.68.34,1.21,1.21,0,0,0,.66-.19.85.85,0,0,0,.33-.68,1.22,1.22,0,0,0-.19-.69A1.13,1.13,0,0,0,12.84,28.51ZM16.72,31a1,1,0,0,0-.68.18.86.86,0,0,0-.34.68,1.12,1.12,0,0,0,.21.68.83.83,0,0,0,.68.34,1.11,1.11,0,0,0,.68-.2.86.86,0,0,0,.34-.68,1.12,1.12,0,0,0-.2-.69A1,1,0,0,0,16.72,31Z" />
|
||||
<circle class="cls-3" cx="25.39" cy="26.73" r="0.95" />
|
||||
<circle class="cls-3" cx="27.92" cy="23.18" r="0.95" />
|
||||
<circle class="cls-3" cx="27.99" cy="18.88" r="0.95" />
|
||||
<circle class="cls-3" cx="25.32" cy="15.2" r="0.95" />
|
||||
<path class="cls-3"
|
||||
d="M16.66,10.9a1,1,0,0,0,1-.87.94.94,0,0,0-.86-1h-.1a.92.92,0,0,0-.61.2.88.88,0,0,0-.34.69,1.08,1.08,0,0,0,.19.65A.84.84,0,0,0,16.66,10.9ZM21,28.84V27.2a6.27,6.27,0,0,1,0-12.54V13a7.92,7.92,0,0,0,0,15.83Zm.42-20.53V10a11.05,11.05,0,0,1,0,22.1v1.64a12.69,12.69,0,0,0,0-25.38Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:spaces-object-storage': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{fill:none;stroke:#000;stroke-miterlimit:10;}.cls-2{font-size:16px;font-family:ProximaNova-Extrabld,
|
||||
Proxima
|
||||
Nova;}.cls-2,.cls-20{fill:#fff;}.cls-3{letter-spacing:0em;}.cls-4{letter-spacing:0em;}.cls-5{letter-spacing:0em;}.cls-6{letter-spacing:0em;}.cls-7{letter-spacing:0em;}.cls-8{letter-spacing:-0.01em;}.cls-9{letter-spacing:0em;}.cls-10{letter-spacing:-0.01em;}.cls-11{letter-spacing:0em;}.cls-12{letter-spacing:0em;}.cls-13{letter-spacing:0.02em;}.cls-14{letter-spacing:-0.01em;}.cls-15{letter-spacing:-0.01em;}.cls-16{letter-spacing:0em;}.cls-17{letter-spacing:0em;}.cls-18{letter-spacing:0em;}.cls-19{fill:#0069ff;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-19" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-20"
|
||||
d="M12.7,13.49a1.22,1.22,0,0,0,.66-.2,1,1,0,0,0,.33-.68,1.22,1.22,0,0,0-.19-.69.88.88,0,0,0-.69-.33h0a1,1,0,0,0-1,.86,1.16,1.16,0,0,0,.21.68A.91.91,0,0,0,12.7,13.49Zm-2.6,3.89a1,1,0,0,0,1-.87,1.26,1.26,0,0,0-.2-.68.85.85,0,0,0-.69-.34h-.06a1,1,0,0,0-.95.87,1.12,1.12,0,0,0,.21.68,1.09,1.09,0,0,0,.67.34ZM10.17,21a1.26,1.26,0,0,0-.2-.68A.9.9,0,0,0,9.28,20H9.22a1,1,0,0,0-.62.21.87.87,0,0,0-.33.69,1.08,1.08,0,0,0,.2.67.84.84,0,0,0,.68.35.87.87,0,0,0,1-.7.61.61,0,0,0,0-.19Zm0,3.61a1.07,1.07,0,0,0-.69.21.88.88,0,0,0-.34.69,1.16,1.16,0,0,0,.21.67.87.87,0,0,0,.68.35,1.15,1.15,0,0,0,.72-.21.84.84,0,0,0,.34-.68.87.87,0,0,0-.69-1Zm2.67,3.9a1,1,0,0,0-1,.86,1.2,1.2,0,0,0,.21.69.89.89,0,0,0,.68.34,1.21,1.21,0,0,0,.66-.19.85.85,0,0,0,.33-.68,1.22,1.22,0,0,0-.19-.69A1.13,1.13,0,0,0,12.84,28.51ZM16.72,31a1,1,0,0,0-.68.18.86.86,0,0,0-.34.68,1.12,1.12,0,0,0,.21.68.83.83,0,0,0,.68.34,1.11,1.11,0,0,0,.68-.2.86.86,0,0,0,.34-.68,1.12,1.12,0,0,0-.2-.69A1,1,0,0,0,16.72,31Z" />
|
||||
<circle class="cls-20" cx="25.39" cy="26.73" r="0.95" />
|
||||
<circle class="cls-20" cx="27.92" cy="23.18" r="0.95" />
|
||||
<circle class="cls-20" cx="27.99" cy="18.88" r="0.95" />
|
||||
<circle class="cls-20" cx="25.32" cy="15.2" r="0.95" />
|
||||
<path class="cls-20"
|
||||
d="M16.66,10.9a1,1,0,0,0,1-.87.94.94,0,0,0-.86-1h-.1a.92.92,0,0,0-.61.2.88.88,0,0,0-.34.69,1.08,1.08,0,0,0,.19.65A.84.84,0,0,0,16.66,10.9ZM21,28.84V27.2a6.27,6.27,0,0,1,0-12.54V13a7.92,7.92,0,0,0,0,15.83Zm.42-20.53V10a11.05,11.05,0,0,1,0,22.1v1.64a12.69,12.69,0,0,0,0-25.38Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:teams': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3" d="M33.83,23.9a5.13,5.13,0,0,0-4.94-5.29h-.41a5.1,5.1,0,0,0-5.3,4.9v.39" />
|
||||
<circle class="cls-3" cx="28.48" cy="15.86" r="2.7" />
|
||||
<path class="cls-3"
|
||||
d="M18.82,23.9a5.11,5.11,0,0,0-4.92-5.29h-.42a5.1,5.1,0,0,0-5.3,4.91,2.41,2.41,0,0,0,0,.38" />
|
||||
<circle class="cls-3" cx="13.48" cy="15.86" r="2.7" />
|
||||
<path class="cls-3"
|
||||
d="M26.33,28.79a5.1,5.1,0,0,0-4.92-5.29,2.94,2.94,0,0,0-.42,0,5.12,5.12,0,0,0-5.3,4.94,2.67,2.67,0,0,0,0,.4" />
|
||||
<circle class="cls-3" cx="20.99" cy="20.8" r="2.7" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:terraform-provider': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-3{fill:none;stroke-miterlimit:10;}.cls-1{stroke:#000;}.cls-2{fill:#0069ff;}.cls-3{stroke:#fff;stroke-width:1.49px;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<path class="cls-3" d="M10.89,17.53l4.61,2.64V14.93l-4.61-2.64Z" />
|
||||
<path class="cls-3" d="M18.63,21.94l4.62,2.65V19.34L18.63,16.7Z" />
|
||||
<path class="cls-3" d="M26.5,19.34v5.25l4.61-2.65V16.7Z" />
|
||||
<path class="cls-3" d="M18.63,31l4.62,2.64V28.43l-4.6-2.62Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
'do:volumes-block-storage': createIcon(
|
||||
`<g>
|
||||
<defs>
|
||||
<style>.cls-1,.cls-3,.cls-4,.cls-5,.cls-6,.cls-7{fill:none;}.cls-1{stroke:#000;stroke-miterlimit:10;}.cls-2{fill:#0069ff;}.cls-3,.cls-4,.cls-5,.cls-6,.cls-7{stroke:#fff;stroke-width:1.49px;}.cls-3,.cls-4,.cls-5,.cls-6{stroke-linecap:round;}.cls-4{stroke-dasharray:0
|
||||
3.11;}.cls-5{stroke-dasharray:0 2.94;}.cls-6{stroke-dasharray:0 2.95;}</style>
|
||||
</defs>
|
||||
<g>
|
||||
<circle class="cls-2" cx="21" cy="21" r="20.79" />
|
||||
<line class="cls-3" x1="20.98" y1="21.2" x2="20.98" y2="21.2" />
|
||||
<line class="cls-4" x1="20.98" y1="18.09" x2="20.98" y2="10.32" />
|
||||
<line class="cls-3" x1="20.98" y1="8.76" x2="20.98" y2="8.76" />
|
||||
<line class="cls-3" x1="10.92" y1="27.22" x2="10.92" y2="27.22" />
|
||||
<line class="cls-5" x1="13.42" y1="25.66" x2="19.68" y2="21.78" />
|
||||
<line class="cls-3" x1="20.93" y1="21" x2="20.93" y2="21" />
|
||||
<line class="cls-6" x1="23.44" y1="22.54" x2="29.72" y2="26.4" />
|
||||
<line class="cls-3" x1="30.98" y1="27.17" x2="30.98" y2="27.17" />
|
||||
<path class="cls-7"
|
||||
d="M21,21V33.44M21,21.2,11,15m20.11,0L21,21.2m-10.1-6.42V27.17l10,6.22L31,27.17V14.73l-10-6.17Z" />
|
||||
</g>
|
||||
</g>`,
|
||||
41.61
|
||||
),
|
||||
};
|
||||
|
||||
export default digitalOceanIcons;
|
26
packages/mermaid/src/rendering-util/svg/github/github.ts
Normal file
26
packages/mermaid/src/rendering-util/svg/github/github.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createIcon } from '../../svgRegister.js';
|
||||
|
||||
const githubIcons = {
|
||||
'gh:action': createIcon(
|
||||
`<g>
|
||||
<path d="M46.62.3c23.51,2.97,39.85,24.77,36.41,48.15-3.04,18.63-19.42,34.09-38.47,34.89-2.09-.07-2.23,1.19-1.34,3.21,3.19,9.13,13.37,14.89,22.82,13.27.55-.09,1.02-.48,1.21-1,.05-.14.1-.3.13-.46.58-2.8,1.67-5.54,3.04-8.07,13.28-23.34,47.81-18.65,53.98,7.57.23,1.5,1.13,2.27,2.52,2.22,4.21.01,8.46,0,12.72,0,2.24.2,2.74-1.22,3.06-3.18.01-.07.03-.14.04-.2,3.26-11.55,14-20.72,26.11-21.53,15.8-1.4,30.54,11.69,31.32,27.5,1.12,23.22-23.72,38.33-43.92,26.85-6.53-3.98-12-11.09-13.56-18.62-.17-.77-.35-1.65-1.02-2.1-.14-.09-.31-.15-.47-.19-2.48-.57-5.2-.08-7.73-.23-2.53.16-5.21-.34-7.66.23-.16.04-.33.09-.47.19-.58.4-.78,1.14-.91,1.82-.3,2.05-1.09,4.03-1.95,5.94-12.63,25.63-49.17,20.91-55.28-6.89-.04-.2-.11-.4-.24-.55-.43-.54-1.08-.72-1.84-.76-7.29.43-14.77-1.31-20.49-6.1,0,0-.01,0-.02-.01-1.06-.63-2.41,0-2.47,1.23-.61,12.63-.33,23.57-.18,36.22s10.41,26.21,22.99,27.03c1.25.15,2.22-.37,2.48-1.7,8.11-33.08,54.11-29.65,57.79,4.14,1.2,17.3-14.66,32.64-31.85,30.93-12.42-1.1-23.31-10.88-25.89-23.1-.21-1.05-.98-1.78-2.06-1.83-19.33-.73-32.03-16.21-32.03-35.45,0-.04,0-3.67,0-3.71-.06-17.49-.02-33.52-.02-50.98.27-2.06-1.33-2.74-3.05-3.06C10.77,76.56-2.44,56.23.38,36.18,3.55,14.18,24.38-2.44,46.62.3ZM41.85,75.27c44.21-2.12,44.37-64.72-.02-66.93-.08,0-.17,0-.25,0-3.69.33-7.43.81-10.96,1.98C-4.29,23.25,4.48,74.43,41.68,75.27c.05,0,.11,0,.17,0ZM75.05,171.2c-.09,13.85,14.54,24.2,27.55,19.6,17.62-6.27,19.43-30.16,2.64-38.57-13.49-6.81-30.67,3.69-30.19,18.97ZM95.82,125.2c16.03.11,25.96-16.9,18.54-30.94-9.48-16.62-34.24-13.51-38.65,5.26-3.3,12.67,7.01,25.63,20.11,25.68ZM171.09,125.19c14.62.2,24.97-15.63,19.18-28.94-6.98-16.77-30.91-17.13-38.28-.53-6.29,13.76,3.91,29.61,19.1,29.47Z" style="fill: #228afe; stroke-width: 0px;"/>
|
||||
<path d="M167.78,199.97c-12.35-1.36-23-11.04-25.4-23.25-.2-1.05-.9-1.59-1.92-1.59-1.75-.08-3.65.24-5.12-.7-1.86-1.06-2.38-3.51-1.36-5.35,1.26-2.51,4.42-1.98,6.72-2.25,1.71-.19,1.68-2.06,2.04-3.37.67-2.29,1.52-4.67,2.84-6.84,10.46-18.3,36.75-19.95,48.97-2.66,15.1,20.27-1.86,48.47-26.61,46.02l-.16-.03ZM170.33,150.06c-17.86.92-26.76,21.77-14.72,35.12,9.48,9.89,25.18,8.64,32.89-2.69,9.06-14.11-1.07-32.64-17.99-32.43h-.18Z" style="fill: #79b9fe; stroke-width: 0px;"/>
|
||||
<path d="M33.52,20.07c1.33.07,2.9.44,4.08,1.22,5.34,3.05,10.44,6.45,15.62,9.75,2.96,2.15,6.82,3.53,8.76,6.78,1.35,2.66,1.34,6.21-.33,8.69-3.01,3.94-7.92,5.77-11.91,8.56-3.01,1.8-6.09,3.62-8.95,5.47-2,1.19-4.26,2.93-6.62,2.9-3.05.04-6.36-1.31-7.78-4-1.18-2.06-1.35-4.27-1.24-6.57,0-3.35,0-6.7,0-10.05,0-4.56,0-9.12,0-13.67-.4-4.71,3.69-8.75,8.22-9.07h.15ZM54.58,42.18c-6.35-5-13.46-9.01-20.07-13.33-.28-.17-.83-.49-1.02-.22-.48,8.53-.04,17.15-.16,25.79.04.62.33.8.89.52,6.79-4.13,13.78-8.4,20.36-12.66v-.09Z" style="fill: #228afe; stroke-width: 0px;"/>
|
||||
<path d="M108.23,100.28c-4.18,4.75-8.97,9.06-13.33,13.56-2.84,3.36-6.28.73-8.56-1.87-2.21-2.49-6.33-4.87-4.58-8.69.72-2.09,4.24-3.27,6.04-1.56,1.12,1,2.06,2.04,3.1,2.97.21.13.46.27.71.24.44-.04.8-.42,1.13-.73,2.63-2.6,5.29-5.23,7.87-7.91,1.01-1.05,2.4-1.92,3.86-2.01,2.98.03,4.95,3.08,3.84,5.82l-.08.17Z" style="fill: #228afe; stroke-width: 0px;"/>
|
||||
<path d="M165.95,115.32c-3.16-.96-4.95-4-7.45-6.2-.66-.71-1.43-1.39-1.85-2.28-.71-2.11-.09-4.52,2.07-5.54,2.51-1.22,4.14.35,5.81,2.09.76.65,1.59,1.81,2.35,1.5,3.09-2.55,5.82-5.72,8.72-8.56,1.5-1.51,3.64-2.72,5.72-1.63,1.55.67,1.99,1.84,2.36,3.2.29,1.28-.44,2.53-1.3,3.43-4.22,4.25-8.49,8.52-12.73,12.76-.9.97-2.21,1.52-3.53,1.26l-.18-.04Z" style="fill: #228afe; stroke-width: 0px;"/>
|
||||
<circle cx="87.78" cy="170.74" r="5" style="fill: #79b9fe; stroke-width: 0px;"/>
|
||||
<circle cx="104.28" cy="170.74" r="5" style="fill: #79b9fe; stroke-width: 0px;"/>
|
||||
</g>`,
|
||||
200
|
||||
),
|
||||
'gh:github': createIcon(
|
||||
`<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
||||
fill="#24292f" />
|
||||
</g>`,
|
||||
98
|
||||
),
|
||||
};
|
||||
|
||||
export default githubIcons;
|
32
packages/mermaid/src/rendering-util/svg/index.ts
Normal file
32
packages/mermaid/src/rendering-util/svg/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { IconLibrary } from '../svgRegister.js';
|
||||
import database from './default/database.js';
|
||||
import server from './default/server.js';
|
||||
import disk from './default/disk.js';
|
||||
import internet from './default/internet.js';
|
||||
import cloud from './default/cloud.js';
|
||||
import unknown from './default/unknown.js';
|
||||
import blank from './default/blank.js';
|
||||
|
||||
/** Creates a resolver to the path to lazy-load included icon packs */
|
||||
const getIconNamespaces = (basePath: string) => ({
|
||||
'aws:common': `${basePath}/aws/awsCommon.js`,
|
||||
'aws:full': `${basePath}/aws/awsFull.js`,
|
||||
github: `${basePath}/github/github.js`,
|
||||
'digital-ocean': `${basePath}/digital-ocean/digitalOcean.js`,
|
||||
});
|
||||
|
||||
type IconNamespaceKeys = keyof ReturnType<typeof getIconNamespaces>;
|
||||
|
||||
const defaultIconLibrary: IconLibrary = {
|
||||
database: database,
|
||||
server: server,
|
||||
disk: disk,
|
||||
internet: internet,
|
||||
cloud: cloud,
|
||||
unknown: unknown,
|
||||
blank: blank,
|
||||
};
|
||||
|
||||
export default defaultIconLibrary;
|
||||
export { getIconNamespaces };
|
||||
export type { IconNamespaceKeys };
|
60
packages/mermaid/src/rendering-util/svgRegister.ts
Normal file
60
packages/mermaid/src/rendering-util/svgRegister.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { Selection } from 'd3-selection';
|
||||
|
||||
type IconResolver = (
|
||||
parent: Selection<SVGGElement, unknown, Element | null, unknown>,
|
||||
width?: number
|
||||
) => Selection<SVGGElement, unknown, Element | null, unknown>;
|
||||
type IconLibrary = Record<string, IconResolver>;
|
||||
|
||||
/**
|
||||
* Converts an SVG Icon passed as a string into a properly formatted IconResolver
|
||||
* @param icon - html code for the svg icon as a string (the SVG tag should not be included)
|
||||
* @param originalSize - the original size of the SVG Icon in pixels
|
||||
* @returns IconResolver
|
||||
*/
|
||||
const createIcon: (icon: string, originalSize: number) => IconResolver = (icon, originalSize) => {
|
||||
return (
|
||||
parent: Selection<SVGGElement, unknown, Element | null, unknown>,
|
||||
size: number = originalSize
|
||||
) => {
|
||||
parent.html(`<g style="transform: scale(${size / originalSize})">${icon}</g>`);
|
||||
return parent;
|
||||
};
|
||||
};
|
||||
|
||||
const icons: IconLibrary = {};
|
||||
|
||||
const isIconNameInUse = (name: string): boolean => {
|
||||
return icons[name] !== undefined;
|
||||
};
|
||||
|
||||
const registerIcon = (name: string, resolver: IconResolver) => {
|
||||
if (!isIconNameInUse(name)) {
|
||||
icons[name] = resolver;
|
||||
}
|
||||
};
|
||||
|
||||
const registerIcons = (library: IconLibrary) => {
|
||||
Object.entries(library).forEach(([name, resolver]) => {
|
||||
if (!isIconNameInUse(name)) {
|
||||
icons[name] = resolver;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getIcon = (name: string): IconResolver | null => {
|
||||
if (isIconNameInUse(name)) {
|
||||
return icons[name];
|
||||
}
|
||||
return icons.unknown;
|
||||
};
|
||||
|
||||
export {
|
||||
registerIcon,
|
||||
registerIcons,
|
||||
getIcon,
|
||||
isIconNameInUse,
|
||||
createIcon,
|
||||
IconLibrary,
|
||||
IconResolver,
|
||||
};
|
@@ -46,6 +46,7 @@ required:
|
||||
- quadrantChart
|
||||
- xyChart
|
||||
- requirement
|
||||
- architecture
|
||||
- mindmap
|
||||
- gitGraph
|
||||
- c4
|
||||
@@ -226,6 +227,11 @@ properties:
|
||||
fall back to legacy rendering for KaTeX.
|
||||
type: boolean
|
||||
default: false
|
||||
iconLibraries:
|
||||
description: |
|
||||
This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code.
|
||||
For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram.
|
||||
tsType: Array<import('./rendering-util/svgRegister.js').IconLibrary | import('./rendering-util/svg/index.js').IconNamespaceKeys>
|
||||
forceLegacyMathML:
|
||||
description: |
|
||||
This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
|
||||
@@ -274,6 +280,8 @@ properties:
|
||||
$ref: '#/$defs/XYChartConfig'
|
||||
requirement:
|
||||
$ref: '#/$defs/RequirementDiagramConfig'
|
||||
architecture:
|
||||
$ref: '#/$defs/ArchitectureDiagramConfig'
|
||||
mindmap:
|
||||
$ref: '#/$defs/MindmapDiagramConfig'
|
||||
gitGraph:
|
||||
@@ -921,6 +929,28 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file)
|
||||
type: number
|
||||
default: 20
|
||||
|
||||
ArchitectureDiagramConfig:
|
||||
title: Architecture Diagram Config
|
||||
allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }]
|
||||
description: The object containing configurations specific for architecture diagrams
|
||||
type: object
|
||||
unevaluatedProperties: false
|
||||
required:
|
||||
- useMaxWidth
|
||||
- padding
|
||||
- iconSize
|
||||
- fontSize
|
||||
properties:
|
||||
padding:
|
||||
type: number
|
||||
default: 40
|
||||
iconSize:
|
||||
type: number
|
||||
default: 80
|
||||
fontSize:
|
||||
type: number
|
||||
default: 16
|
||||
|
||||
MindmapDiagramConfig:
|
||||
title: Mindmap Diagram Config
|
||||
allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }]
|
||||
|
@@ -220,6 +220,13 @@ class Theme {
|
||||
this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black';
|
||||
this.pieOpacity = this.pieOpacity || '0.7';
|
||||
|
||||
/* architecture */
|
||||
this.archEdgeColor = this.archEdgeColor || '#777';
|
||||
this.archEdgeArrowColor = this.archEdgeArrowColor || '#777';
|
||||
this.archEdgeWidth = this.archEdgeWidth || '3';
|
||||
this.archGroupBorderColor = this.archGroupBorderColor || '#000';
|
||||
this.archGroupBorderWidth = this.archGroupBorderWidth || '2px';
|
||||
|
||||
/* quadrant-graph */
|
||||
this.quadrant1Fill = this.quadrant1Fill || this.primaryColor;
|
||||
this.quadrant2Fill = this.quadrant2Fill || adjust(this.primaryColor, { r: 5, g: 5, b: 5 });
|
||||
|
@@ -84,6 +84,13 @@ class Theme {
|
||||
this.personBorder = this.primaryBorderColor;
|
||||
this.personBkg = this.mainBkg;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = 'calculated';
|
||||
this.archEdgeArrowColor = 'calculated';
|
||||
this.archEdgeWidth = '3';
|
||||
this.archGroupBorderColor = this.primaryBorderColor;
|
||||
this.archGroupBorderWidth = '2px';
|
||||
|
||||
/* state colors */
|
||||
this.labelColor = 'calculated';
|
||||
|
||||
@@ -132,6 +139,10 @@ class Theme {
|
||||
this.doneTaskBkgColor = this.mainContrastColor;
|
||||
this.taskTextDarkColor = this.darkTextColor;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = this.lineColor;
|
||||
this.archEdgeArrowColor = this.lineColor;
|
||||
|
||||
/* state colors */
|
||||
this.transitionColor = this.transitionColor || this.lineColor;
|
||||
this.transitionLabelColor = this.transitionLabelColor || this.textColor;
|
||||
|
@@ -112,6 +112,13 @@ class Theme {
|
||||
this.personBorder = this.primaryBorderColor;
|
||||
this.personBkg = this.mainBkg;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = 'calculated';
|
||||
this.archEdgeArrowColor = 'calculated';
|
||||
this.archEdgeWidth = '3';
|
||||
this.archGroupBorderColor = this.primaryBorderColor;
|
||||
this.archGroupBorderWidth = '2px';
|
||||
|
||||
/* state colors */
|
||||
this.labelColor = 'black';
|
||||
this.errorBkgColor = '#552222';
|
||||
@@ -194,6 +201,10 @@ class Theme {
|
||||
this.taskTextColor = this.taskTextLightColor;
|
||||
this.taskTextOutsideColor = this.taskTextDarkColor;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = this.lineColor;
|
||||
this.archEdgeArrowColor = this.lineColor;
|
||||
|
||||
/* state colors */
|
||||
this.transitionColor = this.transitionColor || this.lineColor;
|
||||
this.transitionLabelColor = this.transitionLabelColor || this.textColor;
|
||||
|
@@ -86,6 +86,13 @@ class Theme {
|
||||
this.personBorder = this.primaryBorderColor;
|
||||
this.personBkg = this.mainBkg;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = 'calculated';
|
||||
this.archEdgeArrowColor = 'calculated';
|
||||
this.archEdgeWidth = '3';
|
||||
this.archGroupBorderColor = this.primaryBorderColor;
|
||||
this.archGroupBorderWidth = '2px';
|
||||
|
||||
/* state colors */
|
||||
this.labelColor = 'black';
|
||||
|
||||
@@ -162,6 +169,10 @@ class Theme {
|
||||
this.activeTaskBorderColor = this.taskBorderColor;
|
||||
this.activeTaskBkgColor = this.mainBkg;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = this.lineColor;
|
||||
this.archEdgeArrowColor = this.lineColor;
|
||||
|
||||
/* state colors */
|
||||
this.transitionColor = this.transitionColor || this.lineColor;
|
||||
this.transitionLabelColor = this.transitionLabelColor || this.textColor;
|
||||
|
@@ -98,6 +98,13 @@ class Theme {
|
||||
this.personBorder = this.primaryBorderColor;
|
||||
this.personBkg = this.mainBkg;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = 'calculated';
|
||||
this.archEdgeArrowColor = 'calculated';
|
||||
this.archEdgeWidth = '3';
|
||||
this.archGroupBorderColor = this.primaryBorderColor;
|
||||
this.archGroupBorderWidth = '2px';
|
||||
|
||||
/* state colors */
|
||||
this.labelColor = 'black';
|
||||
|
||||
@@ -199,6 +206,10 @@ class Theme {
|
||||
|
||||
this.todayLineColor = this.critBkgColor;
|
||||
|
||||
/* Architecture Diagram variables */
|
||||
this.archEdgeColor = this.lineColor;
|
||||
this.archEdgeArrowColor = this.lineColor;
|
||||
|
||||
/* state colors */
|
||||
this.transitionColor = this.transitionColor || '#000';
|
||||
this.transitionLabelColor = this.transitionLabelColor || this.textColor;
|
||||
|
@@ -4,22 +4,42 @@
|
||||
{
|
||||
"id": "info",
|
||||
"grammar": "src/language/info/info.langium",
|
||||
"fileExtensions": [".mmd", ".mermaid"]
|
||||
"fileExtensions": [
|
||||
".mmd",
|
||||
".mermaid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "packet",
|
||||
"grammar": "src/language/packet/packet.langium",
|
||||
"fileExtensions": [".mmd", ".mermaid"]
|
||||
"fileExtensions": [
|
||||
".mmd",
|
||||
".mermaid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pie",
|
||||
"grammar": "src/language/pie/pie.langium",
|
||||
"fileExtensions": [".mmd", ".mermaid"]
|
||||
"fileExtensions": [
|
||||
".mmd",
|
||||
".mermaid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "architecture",
|
||||
"grammar": "src/language/architecture/architecture.langium",
|
||||
"fileExtensions": [
|
||||
".mmd",
|
||||
".mermaid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gitGraph",
|
||||
"grammar": "src/language/gitGraph/gitGraph.langium",
|
||||
"fileExtensions": [".mmd", ".mermaid"]
|
||||
"fileExtensions": [
|
||||
".mmd",
|
||||
".mermaid"
|
||||
]
|
||||
}
|
||||
],
|
||||
"mode": "production",
|
||||
|
@@ -0,0 +1,55 @@
|
||||
grammar Architecture
|
||||
import "../common/common";
|
||||
|
||||
entry Architecture:
|
||||
NEWLINE*
|
||||
"architecture-beta"
|
||||
(
|
||||
NEWLINE* TitleAndAccessibilities
|
||||
| NEWLINE* Statement*
|
||||
| NEWLINE*
|
||||
)
|
||||
;
|
||||
|
||||
fragment Statement:
|
||||
groups+=Group
|
||||
| services+=Service
|
||||
| junctions+=Junction
|
||||
| edges+=Edge
|
||||
;
|
||||
|
||||
fragment LeftPort:
|
||||
':'lhsDir=ARROW_DIRECTION
|
||||
;
|
||||
|
||||
fragment RightPort:
|
||||
rhsDir=ARROW_DIRECTION':'
|
||||
;
|
||||
|
||||
fragment Arrow:
|
||||
LeftPort lhsInto?=ARROW_INTO? ('--' | '-' title=ARCH_TITLE '-') rhsInto?=ARROW_INTO? RightPort
|
||||
;
|
||||
|
||||
Group:
|
||||
'group' id=ARCH_ID icon=ARCH_ICON? title=ARCH_TITLE? ('in' in=ARCH_ID)? EOL
|
||||
;
|
||||
|
||||
Service:
|
||||
'service' id=ARCH_ID (iconText=ARCH_TEXT_ICON | icon=ARCH_ICON)? title=ARCH_TITLE? ('in' in=ARCH_ID)? EOL
|
||||
;
|
||||
|
||||
Junction:
|
||||
'junction' id=ARCH_ID ('in' in=ARCH_ID)? EOL
|
||||
;
|
||||
|
||||
Edge:
|
||||
lhsId=ARCH_ID lhsGroup?=ARROW_GROUP? Arrow rhsId=ARCH_ID rhsGroup?=ARROW_GROUP? EOL
|
||||
;
|
||||
|
||||
terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B';
|
||||
terminal ARCH_ID: /[\w]+/;
|
||||
terminal ARCH_TEXT_ICON: /\("[^"]+"\)/;
|
||||
terminal ARCH_ICON: /\([\w:]+\)/;
|
||||
terminal ARCH_TITLE: /\[[\w ]+\]/;
|
||||
terminal ARROW_GROUP: /\{group\}/;
|
||||
terminal ARROW_INTO: /<|>/;
|
1
packages/parser/src/language/architecture/index.ts
Normal file
1
packages/parser/src/language/architecture/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './module.js';
|
79
packages/parser/src/language/architecture/module.ts
Normal file
79
packages/parser/src/language/architecture/module.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import type {
|
||||
DefaultSharedCoreModuleContext,
|
||||
LangiumCoreServices,
|
||||
LangiumSharedCoreServices,
|
||||
Module,
|
||||
PartialLangiumCoreServices,
|
||||
} from 'langium';
|
||||
import {
|
||||
EmptyFileSystem,
|
||||
createDefaultCoreModule,
|
||||
createDefaultSharedCoreModule,
|
||||
inject,
|
||||
} from 'langium';
|
||||
|
||||
import { MermaidGeneratedSharedModule, ArchitectureGeneratedModule } from '../generated/module.js';
|
||||
import { ArchitectureTokenBuilder } from './tokenBuilder.js';
|
||||
import { ArchitectureValueConverter } from './valueConverter.js';
|
||||
|
||||
/**
|
||||
* Declaration of `Architecture` services.
|
||||
*/
|
||||
interface ArchitectureAddedServices {
|
||||
parser: {
|
||||
TokenBuilder: ArchitectureTokenBuilder;
|
||||
ValueConverter: ArchitectureValueConverter;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Union of Langium default services and `Architecture` services.
|
||||
*/
|
||||
export type ArchitectureServices = LangiumCoreServices & ArchitectureAddedServices;
|
||||
|
||||
/**
|
||||
* Dependency injection module that overrides Langium default services and
|
||||
* contributes the declared `Architecture` services.
|
||||
*/
|
||||
export const ArchitectureModule: Module<
|
||||
ArchitectureServices,
|
||||
PartialLangiumCoreServices & ArchitectureAddedServices
|
||||
> = {
|
||||
parser: {
|
||||
TokenBuilder: () => new ArchitectureTokenBuilder(),
|
||||
ValueConverter: () => new ArchitectureValueConverter(),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the full set of services required by Langium.
|
||||
*
|
||||
* First inject the shared services by merging two modules:
|
||||
* - Langium default shared services
|
||||
* - Services generated by langium-cli
|
||||
*
|
||||
* Then inject the language-specific services by merging three modules:
|
||||
* - Langium default language-specific services
|
||||
* - Services generated by langium-cli
|
||||
* - Services specified in this file
|
||||
* @param context - Optional module context with the LSP connection
|
||||
* @returns An object wrapping the shared services and the language-specific services
|
||||
*/
|
||||
export function createArchitectureServices(
|
||||
context: DefaultSharedCoreModuleContext = EmptyFileSystem
|
||||
): {
|
||||
shared: LangiumSharedCoreServices;
|
||||
Architecture: ArchitectureServices;
|
||||
} {
|
||||
const shared: LangiumSharedCoreServices = inject(
|
||||
createDefaultSharedCoreModule(context),
|
||||
MermaidGeneratedSharedModule
|
||||
);
|
||||
const Architecture: ArchitectureServices = inject(
|
||||
createDefaultCoreModule({ shared }),
|
||||
ArchitectureGeneratedModule,
|
||||
ArchitectureModule
|
||||
);
|
||||
shared.ServiceRegistry.register(Architecture);
|
||||
return { shared, Architecture };
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
import { AbstractMermaidTokenBuilder } from '../common/index.js';
|
||||
|
||||
export class ArchitectureTokenBuilder extends AbstractMermaidTokenBuilder {
|
||||
public constructor() {
|
||||
super(['architecture']);
|
||||
}
|
||||
}
|
20
packages/parser/src/language/architecture/valueConverter.ts
Normal file
20
packages/parser/src/language/architecture/valueConverter.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { CstNode, GrammarAST, ValueType } from 'langium';
|
||||
|
||||
import { AbstractMermaidValueConverter } from '../common/index.js';
|
||||
|
||||
export class ArchitectureValueConverter extends AbstractMermaidValueConverter {
|
||||
protected runCustomConverter(
|
||||
rule: GrammarAST.AbstractRule,
|
||||
input: string,
|
||||
_cstNode: CstNode
|
||||
): ValueType | undefined {
|
||||
if (rule.name === 'ARCH_ICON') {
|
||||
return input.replace(/[()]/g, '').trim();
|
||||
} else if (rule.name === 'ARCH_TEXT_ICON') {
|
||||
return input.replace(/["()]/g, '');
|
||||
} else if (rule.name === 'ARCH_TITLE') {
|
||||
return input.replace(/[[\]]/g, '').trim();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ export {
|
||||
PacketBlock,
|
||||
Pie,
|
||||
PieSection,
|
||||
Architecture,
|
||||
GitGraph,
|
||||
Branch,
|
||||
Commit,
|
||||
@@ -16,16 +17,19 @@ export {
|
||||
isPacketBlock,
|
||||
isPie,
|
||||
isPieSection,
|
||||
isArchitecture,
|
||||
isGitGraph,
|
||||
isBranch,
|
||||
isCommit,
|
||||
isMerge,
|
||||
} from './generated/ast.js';
|
||||
|
||||
export {
|
||||
InfoGeneratedModule,
|
||||
MermaidGeneratedSharedModule,
|
||||
PacketGeneratedModule,
|
||||
PieGeneratedModule,
|
||||
ArchitectureGeneratedModule,
|
||||
GitGraphGeneratedModule,
|
||||
} from './generated/module.js';
|
||||
|
||||
@@ -34,3 +38,4 @@ export * from './common/index.js';
|
||||
export * from './info/index.js';
|
||||
export * from './packet/index.js';
|
||||
export * from './pie/index.js';
|
||||
export * from './architecture/index.js';
|
||||
|
@@ -6,7 +6,6 @@ export class PieValueConverter extends AbstractMermaidValueConverter {
|
||||
protected runCustomConverter(
|
||||
rule: GrammarAST.AbstractRule,
|
||||
input: string,
|
||||
|
||||
_cstNode: CstNode
|
||||
): ValueType | undefined {
|
||||
if (rule.name !== 'PIE_SECTION_LABEL') {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import type { LangiumParser, ParseResult } from 'langium';
|
||||
|
||||
import type { Info, Packet, Pie, GitGraph } from './index.js';
|
||||
import type { Info, Packet, Pie, Architecture, GitGraph } from './index.js';
|
||||
|
||||
export type DiagramAST = Info | Packet | Pie | GitGraph;
|
||||
export type DiagramAST = Info | Packet | Pie | Architecture | GitGraph;
|
||||
|
||||
const parsers: Record<string, LangiumParser> = {};
|
||||
const initializers = {
|
||||
@@ -21,6 +21,11 @@ const initializers = {
|
||||
const parser = createPieServices().Pie.parser.LangiumParser;
|
||||
parsers.pie = parser;
|
||||
},
|
||||
architecture: async () => {
|
||||
const { createArchitectureServices } = await import('./language/architecture/index.js');
|
||||
const parser = createArchitectureServices().Architecture.parser.LangiumParser;
|
||||
parsers.architecture = parser;
|
||||
},
|
||||
gitGraph: async () => {
|
||||
const { createGitGraphServices } = await import('./language/gitGraph/index.js');
|
||||
const parser = createGitGraphServices().GitGraph.parser.LangiumParser;
|
||||
@@ -31,6 +36,7 @@ const initializers = {
|
||||
export async function parse(diagramType: 'info', text: string): Promise<Info>;
|
||||
export async function parse(diagramType: 'packet', text: string): Promise<Packet>;
|
||||
export async function parse(diagramType: 'pie', text: string): Promise<Pie>;
|
||||
export async function parse(diagramType: 'architecture', text: string): Promise<Architecture>;
|
||||
export async function parse(diagramType: 'gitGraph', text: string): Promise<GitGraph>;
|
||||
|
||||
export async function parse<T extends DiagramAST>(
|
||||
|
1430
pnpm-lock.yaml
generated
1430
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user