Compare commits

..

16 Commits

Author SHA1 Message Date
omkarht
10bb1f9261 fix: my sample commit summary here
Some optional description over here if you need to add more info

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 19:41:28 +05:30
omkarht
f0c422ba37 fix: sample commit 2
on-behalf-of: @Mermaid-Chart hello@mermaidchart.com
2025-07-15 19:38:27 +05:30
omkarht
d1acc182cf fix:sample change
on-behalf-of: @Mermaid-Chart hello@mermaidchart.com
2025-07-15 19:28:52 +05:30
darshanr0107
d4e341368a fix: my test commit 7
Some optional description over here if you need to add more info

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 19:24:45 +05:30
darshanr0107
9773e6e1e2 fix: test commit from darshan
Some optional description over here if you need to add more info

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 19:14:14 +05:30
darshanr0107
5277af895b fix: test commit 3
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 18:58:58 +05:30
darshanr0107
f2803ed901 fix: my commit summary here
Some optional description over here if you need to add more info

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 18:14:31 +05:30
darshanr0107
e883140971 Merge branch 'test-commits' of https://github.com/mermaid-js/mermaid into test-commits 2025-07-15 18:09:26 +05:30
darshanr0107
a085db10cd fix: test commit 2
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 18:09:11 +05:30
omkarht
e73105bcec fix: test commit
on-behalf-of: @Mermaid-Chart hello@mermaidchart.com
2025-07-15 18:03:38 +05:30
darshanr0107
fb80afe3ae fix: test commit 1
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 17:33:36 +05:30
shubham-mermaid
4cb80e22ee fix: another test commit 5
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 17:28:15 +05:30
shubham-mermaid
e584c79fcc fix: another test commit 4
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 17:26:19 +05:30
shubham-mermaid
3ddbdbbb30 fix: another test commit 2
Some optional description over here if you need to add more info

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-15 17:25:05 +05:30
shubham-mermaid
9d50014705 fix: another test commit
on-behalf-of: @Mermaid-Chart
2025-07-15 17:20:58 +05:30
shubham-mermaid
a8ee7dca30 fix: test commit
this is test commit to verify  template

on-behalf-of: Mermaid-Chart
2025-07-15 17:17:42 +05:30
137 changed files with 2813 additions and 6788 deletions

View File

@@ -1,5 +0,0 @@
---
'@mermaid-js/mermaid-zenuml': patch
---
Fixed a critical bug that the ZenUML diagram is not rendered.

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
chore: Remove the "-beta" suffix from the XYChart, Block, Sankey diagrams to reflect their stable status

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Position the edge label in state diagram correctly relative to the edge

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Apply correct dateFormat in Gantt chart to show only day when specified

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: handle exclude dates properly in Gantt charts when using dateFormat: 'YYYY-MM-DD HH:mm:ss'

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
feat: Add `getRegisteredDiagramsMetadata` to `mermaid`, which returns all the registered diagram IDs in mermaid

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: fixed connection gaps in flowchart for roundedRect, stadium and diamond shape

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Update casing of ID in requirement diagram

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Added support for per link curve styling in flowchart diagram using edge ids

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Make flowchart elk detector regex match less greedy

View File

@@ -1,8 +0,0 @@
---
'mermaid': patch
---
fix(block): overflowing blocks no longer affect later lines
This may change the layout of block diagrams that have overflowing lines
(i.e. block diagrams that use up more columns that the `columns` specifier).

View File

@@ -1,7 +0,0 @@
---
'mermaid': patch
---
fix: log warning for blocks exceeding column width
This update adds a validation check that logs a warning message when a block's width exceeds the defined column layout.

View File

@@ -0,0 +1,5 @@
---
'@mermaid-js/examples': minor
---
feat: Add examples for diagrams in the `@mermaid-js/examples` package

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Add escaped class literal name on namespace

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Allow equals sign in sequenceDiagram labels

View File

@@ -1,9 +0,0 @@
---
'mermaid': patch
---
Add validation for negative values in pie charts:
Prevents crashes during parsing by validating values post-parsing.
Provides clearer, user-friendly error messages for invalid negative inputs.

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
chore: migrate to class-based ArchitectureDB implementation

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: node border style for handdrawn shapes

View File

@@ -0,0 +1,7 @@
---
'@mermaid-js/examples': patch
'mermaid': patch
'@mermaid-js/parser': patch
---
chore: Move packet diagram out of beta

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Update flowchart direction TD's behavior to be the same as TB

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: correctly render non-directional lines for '---' in block diagrams

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: adjust sequence diagram title positioning to prevent overlap with top border in Safari

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
chore: Update packet diagram to use new class-based database structure

View File

@@ -0,0 +1,7 @@
---
'mermaid': patch
---
fix(timeline): fix loading `leftMargin` from config
The `timeline.leftMargin` config value should now correctly control the size of the left margin, instead of being ignored.

3
.github/lychee.toml vendored
View File

@@ -52,9 +52,6 @@ exclude = [
# Swimm returns 404, even though the link is valid # Swimm returns 404, even though the link is valid
"https://docs.swimm.io", "https://docs.swimm.io",
# Certificate Error
"https://noteshub.app",
# Timeout # Timeout
"https://huehive.co", "https://huehive.co",
"https://foswiki.org", "https://foswiki.org",

View File

@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request - name: Commit and create pull request
uses: peter-evans/create-pull-request@1310d7dab503600742045e6fd4b84dda64352858 uses: peter-evans/create-pull-request@889dce9eaba7900ce30494f5e1ac7220b27e5c81
with: with:
add-paths: | add-paths: |
cypress/timings.json cypress/timings.json

View File

@@ -1 +0,0 @@
./packages/mermaid/CHANGELOG.md

1005
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ describe('Block diagram', () => {
it('BL2: should handle columns statement in sub-blocks', () => { it('BL2: should handle columns statement in sub-blocks', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
id1["Hello"] id1["Hello"]
block block
columns 3 columns 3
@@ -32,7 +32,7 @@ describe('Block diagram', () => {
it('BL3: should align block widths and handle columns statement in sub-blocks', () => { it('BL3: should align block widths and handle columns statement in sub-blocks', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
block block
columns 1 columns 1
id1 id1
@@ -48,7 +48,7 @@ describe('Block diagram', () => {
it('BL4: should align block widths and handle columns statements in deeper sub-blocks then 1 level', () => { it('BL4: should align block widths and handle columns statements in deeper sub-blocks then 1 level', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 1 columns 1
block block
columns 1 columns 1
@@ -68,7 +68,7 @@ describe('Block diagram', () => {
it('BL5: should align block widths and handle columns statements in deeper sub-blocks then 1 level (alt)', () => { it('BL5: should align block widths and handle columns statements in deeper sub-blocks then 1 level (alt)', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 1 columns 1
block block
id1 id1
@@ -87,7 +87,7 @@ describe('Block diagram', () => {
it('BL6: should handle block arrows and spece statements', () => { it('BL6: should handle block arrows and spece statements', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
space:3 space:3
ida idb idc ida idb idc
@@ -106,7 +106,7 @@ describe('Block diagram', () => {
it('BL7: should handle different types of edges', () => { it('BL7: should handle different types of edges', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
A space:5 A space:5
A --o B A --o B
@@ -119,7 +119,7 @@ describe('Block diagram', () => {
it('BL8: should handle sub-blocks without columns statements', () => { it('BL8: should handle sub-blocks without columns statements', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 2 columns 2
C A B C A B
block block
@@ -133,7 +133,7 @@ describe('Block diagram', () => {
it('BL9: should handle edges from blocks in sub blocks to other blocks', () => { it('BL9: should handle edges from blocks in sub blocks to other blocks', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
B space B space
block block
@@ -147,7 +147,7 @@ describe('Block diagram', () => {
it('BL10: should handle edges from composite blocks', () => { it('BL10: should handle edges from composite blocks', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
B space B space
block BL block BL
@@ -161,7 +161,7 @@ describe('Block diagram', () => {
it('BL11: should handle edges to composite blocks', () => { it('BL11: should handle edges to composite blocks', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
B space B space
block BL block BL
@@ -175,7 +175,7 @@ describe('Block diagram', () => {
it('BL12: edges should handle labels', () => { it('BL12: edges should handle labels', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A A
space space
A -- "apa" --> E A -- "apa" --> E
@@ -186,7 +186,7 @@ describe('Block diagram', () => {
it('BL13: should handle block arrows in different directions', () => { it('BL13: should handle block arrows in different directions', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
space blockArrowId1<["down"]>(down) space space blockArrowId1<["down"]>(down) space
blockArrowId2<["right"]>(right) blockArrowId3<["Sync"]>(x, y) blockArrowId4<["left"]>(left) blockArrowId2<["right"]>(right) blockArrowId3<["Sync"]>(x, y) blockArrowId4<["left"]>(left)
@@ -199,7 +199,7 @@ describe('Block diagram', () => {
it('BL14: should style statements and class statements', () => { it('BL14: should style statements and class statements', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A A
B B
classDef blue fill:#66f,stroke:#333,stroke-width:2px; classDef blue fill:#66f,stroke:#333,stroke-width:2px;
@@ -212,7 +212,7 @@ describe('Block diagram', () => {
it('BL15: width alignment - D and E should share available space', () => { it('BL15: width alignment - D and E should share available space', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
block block
D D
E E
@@ -225,7 +225,7 @@ describe('Block diagram', () => {
it('BL16: width alignment - C should be as wide as the composite block', () => { it('BL16: width alignment - C should be as wide as the composite block', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
block block
A("This is the text") A("This is the text")
B B
@@ -238,7 +238,7 @@ describe('Block diagram', () => {
it('BL17: width alignment - blocks should be equal in width', () => { it('BL17: width alignment - blocks should be equal in width', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A("This is the text") A("This is the text")
B B
C C
@@ -249,7 +249,7 @@ describe('Block diagram', () => {
it('BL18: block types 1 - square, rounded and circle', () => { it('BL18: block types 1 - square, rounded and circle', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A["square"] A["square"]
B("rounded") B("rounded")
C(("circle")) C(("circle"))
@@ -260,7 +260,7 @@ describe('Block diagram', () => {
it('BL19: block types 2 - odd, diamond and hexagon', () => { it('BL19: block types 2 - odd, diamond and hexagon', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A>"rect_left_inv_arrow"] A>"rect_left_inv_arrow"]
B{"diamond"} B{"diamond"}
C{{"hexagon"}} C{{"hexagon"}}
@@ -271,7 +271,7 @@ describe('Block diagram', () => {
it('BL20: block types 3 - stadium', () => { it('BL20: block types 3 - stadium', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A(["stadium"]) A(["stadium"])
`, `,
{} {}
@@ -280,7 +280,7 @@ describe('Block diagram', () => {
it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A[/"lean right"/] A[/"lean right"/]
B[\"lean left"\] B[\"lean left"\]
C[/"trapezoid"\] C[/"trapezoid"\]
@@ -292,7 +292,7 @@ describe('Block diagram', () => {
it('BL22: block types 1 - square, rounded and circle', () => { it('BL22: block types 1 - square, rounded and circle', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A["square"] A["square"]
B("rounded") B("rounded")
C(("circle")) C(("circle"))
@@ -303,7 +303,7 @@ describe('Block diagram', () => {
it('BL23: sizing - it should be possible to make a block wider', () => { it('BL23: sizing - it should be possible to make a block wider', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
A("rounded"):2 A("rounded"):2
B:2 B:2
C C
@@ -314,7 +314,7 @@ describe('Block diagram', () => {
it('BL24: sizing - it should be possible to make a composite block wider', () => { it('BL24: sizing - it should be possible to make a composite block wider', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
block:2 block:2
A A
end end
@@ -326,7 +326,7 @@ describe('Block diagram', () => {
it('BL25: block in the middle with space on each side', () => { it('BL25: block in the middle with space on each side', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
space space
middle["In the middle"] middle["In the middle"]
@@ -337,7 +337,7 @@ describe('Block diagram', () => {
}); });
it('BL26: space and an edge', () => { it('BL26: space and an edge', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 5 columns 5
A space B A space B
A --x B A --x B
@@ -347,7 +347,7 @@ describe('Block diagram', () => {
}); });
it('BL27: block sizes for regular blocks', () => { it('BL27: block sizes for regular blocks', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
a["A wide one"] b:2 c:2 d a["A wide one"] b:2 c:2 d
`, `,
@@ -356,7 +356,7 @@ describe('Block diagram', () => {
}); });
it('BL28: composite block with a set width - f should use the available space', () => { it('BL28: composite block with a set width - f should use the available space', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
a:3 a:3
block:e:3 block:e:3
@@ -370,7 +370,7 @@ describe('Block diagram', () => {
it('BL29: composite block with a set width - f and g should split the available space', () => { it('BL29: composite block with a set width - f and g should split the available space', () => {
imgSnapshotTest( imgSnapshotTest(
`block `block-beta
columns 3 columns 3
a:3 a:3
block:e:3 block:e:3
@@ -384,28 +384,4 @@ describe('Block diagram', () => {
{} {}
); );
}); });
it('BL30: block should overflow if too wide for columns', () => {
imgSnapshotTest(
`block-beta
columns 2
fit:2
overflow:3
short:1
also_overflow:2
`,
{}
);
});
it('BL31: edge without arrow syntax should render with no arrowheads', () => {
imgSnapshotTest(
`block-beta
a
b
a --- b
`,
{}
);
});
}); });

View File

@@ -495,34 +495,4 @@ describe('Class diagram', () => {
cy.get('a').should('have.attr', 'target', '_blank').should('have.attr', 'rel', 'noopener'); cy.get('a').should('have.attr', 'target', '_blank').should('have.attr', 'rel', 'noopener');
}); });
}); });
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
`
classDiagram
class Person {
+String name
-Int id
#double age
+Text demographicProfile
}
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
it('should handle backticks for namespace and class names', () => {
imgSnapshotTest(
`
classDiagram
namespace \`A::B\` {
class \`IPC::Sender\`
}
RenderProcessHost --|> \`IPC::Sender\`
`,
{}
);
});
}); });

View File

@@ -354,19 +354,4 @@ ORDER ||--|{ LINE-ITEM : contains
{ logLevel: 1 } { logLevel: 1 }
); );
}); });
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
`
erDiagram
p[Photograph] {
varchar(12) jobId
date dateCreated
}
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
}); });

View File

@@ -1113,55 +1113,4 @@ end
); );
}); });
}); });
describe('Flowchart Node Shape Rendering', () => {
it('should render a stadium-shaped node', () => {
imgSnapshotTest(
`flowchart TB
A(["Start"]) --> n1["Untitled Node"]
A --> n2["Untitled Node"]
`,
{}
);
});
it('should render a diamond-shaped node using shape config', () => {
imgSnapshotTest(
`flowchart BT
n2["Untitled Node"] --> n1["Diamond"]
n1@{ shape: diam}
`,
{}
);
});
it('should render a rounded rectangle and a normal rectangle', () => {
imgSnapshotTest(
`flowchart BT
n2["Untitled Node"] --> n1["Rounded Rectangle"]
n3["Untitled Node"] --> n1
n1@{ shape: rounded}
n3@{ shape: rect}
`,
{}
);
});
});
it('6617: Per Link Curve Styling using edge Ids', () => {
imgSnapshotTest(
`flowchart TD
A e1@-->B e5@--> E
E e7@--> D
B e3@-->D
A e2@-->C e4@-->D
C e6@--> F
F e8@--> D
e1@{ curve: natural }
e2@{ curve: stepAfter }
e3@{ curve: monotoneY }
e4@{ curve: bumpY }
e5@{ curve: linear }
e6@{ curve: catmullRom }
e7@{ curve: cardinal }
`
);
});
}); });

View File

@@ -565,18 +565,6 @@ describe('Gantt diagram', () => {
); );
}); });
it('should render only the day when using dateFormat D', () => {
imgSnapshotTest(
`
gantt
title Test
dateFormat D
A :a, 1, 1d
`,
{}
);
});
// TODO: fix it // TODO: fix it
// //
// This test is skipped deliberately // This test is skipped deliberately
@@ -659,49 +647,6 @@ describe('Gantt diagram', () => {
); );
}); });
it('should render a gantt diagram excluding a specific date in YYYY-MM-DD HH:mm:ss format', () => {
imgSnapshotTest(
`
gantt
dateFormat YYYY-MM-DD HH:mm:ss
excludes 2025-07-07
section Section
A task :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30
Another task:after a1, 20h
`,
{}
);
});
it('should render a gantt diagram excluding saturday and sunday in YYYY-MM-DD HH:mm:ss format', () => {
imgSnapshotTest(
`
gantt
dateFormat YYYY-MM-DD HH:mm:ss
excludes weekends
weekend saturday
section Section
A task :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30
Another task:after a1, 20h
`,
{}
);
});
it('should render a gantt diagram excluding friday and saturday in YYYY-MM-DD HH:mm:ss format', () => {
imgSnapshotTest(
`
gantt
dateFormat YYYY-MM-DD HH:mm:ss
excludes weekends
weekend friday
section Section
A task :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30
Another task:after a1, 20h
`,
{}
);
});
it("should render when there's a semicolon in the title", () => { it("should render when there's a semicolon in the title", () => {
imgSnapshotTest( imgSnapshotTest(
` `

View File

@@ -246,22 +246,5 @@ Word!\`]
); );
}); });
}); });
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
`
mindmap
root
Photograph
Waterfall
Landscape
Geography
Mountains
Rocks
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
/* The end */ /* The end */
}); });

View File

@@ -82,13 +82,4 @@ describe('pie chart', () => {
` `
); );
}); });
it('should render pie slices only for non-zero values but shows all legends', () => {
imgSnapshotTest(
` pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 1
`
);
});
}); });

View File

@@ -15,7 +15,7 @@ describe('Sankey Diagram', () => {
describe('when given a linkColor', function () { describe('when given a linkColor', function () {
this.beforeAll(() => { this.beforeAll(() => {
cy.wrap( cy.wrap(
`sankey `sankey-beta
a,b,10 a,b,10
` `
).as('graph'); ).as('graph');
@@ -62,7 +62,7 @@ describe('Sankey Diagram', () => {
this.beforeAll(() => { this.beforeAll(() => {
cy.wrap( cy.wrap(
` `
sankey sankey-beta
a,b,8 a,b,8
b,c,8 b,c,8

View File

@@ -602,231 +602,6 @@ State1 --> [*]
-- --
55 55
} }
`,
{}
);
});
it('should render edge labels correctly', () => {
imgSnapshotTest(
`---
title: On The Way To Something Something DarkSide
config:
look: default
theme: default
---
stateDiagram-v2
state State1_____________
{
c0
}
state State2_____________
{
c1
}
state State3_____________
{
c7
}
state State4_____________
{
c2
}
state State5_____________
{
c3
}
state State6_____________
{
c4
}
state State7_____________
{
c5
}
state State8_____________
{
c6
}
[*] --> State1_____________
State1_____________ --> State2_____________ : Transition1_____
State2_____________ --> State4_____________ : Transition2_____
State2_____________ --> State3_____________ : Transition3_____
State3_____________ --> State2_____________
State4_____________ --> State2_____________ : Transition5_____
State4_____________ --> State5_____________ : Transition6_____
State5_____________ --> State6_____________ : Transition7_____
State6_____________ --> State4_____________ : Transition8_____
State2_____________ --> State7_____________ : Transition4_____
State4_____________ --> State7_____________ : Transition4_____
State5_____________ --> State7_____________ : Transition4_____
State6_____________ --> State7_____________ : Transition4_____
State7_____________ --> State1_____________ : Transition9_____
State5_____________ --> State8_____________ : Transition10____
State8_____________ --> State5_____________ : Transition11____
`,
{}
);
});
it('should render edge labels correctly with multiple transitions', () => {
imgSnapshotTest(
`---
title: Multiple Transitions
config:
look: default
theme: default
---
stateDiagram-v2
state State1_____________
{
c0
}
state State2_____________
{
c1
}
state State3_____________
{
c7
}
state State4_____________
{
c2
}
state State5_____________
{
c3
}
state State6_____________
{
c4
}
state State7_____________
{
c5
}
state State8_____________
{
c6
}
state State9_____________
{
c9
}
[*] --> State1_____________
State1_____________ --> State2_____________ : Transition1_____
State2_____________ --> State4_____________ : Transition2_____
State2_____________ --> State3_____________ : Transition3_____
State3_____________ --> State2_____________
State4_____________ --> State2_____________ : Transition5_____
State4_____________ --> State5_____________ : Transition6_____
State5_____________ --> State6_____________ : Transition7_____
State6_____________ --> State4_____________ : Transition8_____
State2_____________ --> State7_____________ : Transition4_____
State4_____________ --> State7_____________ : Transition4_____
State5_____________ --> State7_____________ : Transition4_____
State6_____________ --> State7_____________ : Transition4_____
State7_____________ --> State1_____________ : Transition9_____
State5_____________ --> State8_____________ : Transition10____
State8_____________ --> State5_____________ : Transition11____
State9_____________ --> State8_____________ : Transition12____
`,
{}
);
});
it('should render edge labels correctly with multiple states', () => {
imgSnapshotTest(
`---
title: Multiple States
config:
look: default
theme: default
---
stateDiagram-v2
state State1_____________
{
c0
}
state State2_____________
{
c1
}
state State3_____________
{
c7
}
state State4_____________
{
c2
}
state State5_____________
{
c3
}
state State6_____________
{
c4
}
state State7_____________
{
c5
}
state State8_____________
{
c6
}
state State9_____________
{
c9
}
state State10_____________
{
c10
}
[*] --> State1_____________
State1_____________ --> State2_____________ : Transition1_____
State2_____________ --> State3_____________ : Transition2_____
State3_____________ --> State4_____________ : Transition3_____
State4_____________ --> State5_____________ : Transition4_____
State5_____________ --> State6_____________ : Transition5_____
State6_____________ --> State7_____________ : Transition6_____
State7_____________ --> State8_____________ : Transition7_____
State8_____________ --> State9_____________ : Transition8_____
State9_____________ --> State10_____________ : Transition9_____
`, `,
{} {}
); );

View File

@@ -1,7 +1,7 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
describe('XY Chart', () => { describe('XY Chart', () => {
it('should render the simplest possible xy-beta chart', () => { it('should render the simplest possible chart', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart-beta xychart-beta
@@ -10,19 +10,10 @@ describe('XY Chart', () => {
{} {}
); );
}); });
it('should render the simplest possible xy chart', () => {
imgSnapshotTest(
`
xychart
line [10, 30, 20]
`,
{}
);
});
it('Should render a complete chart', () => { it('Should render a complete chart', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -35,7 +26,7 @@ describe('XY Chart', () => {
it('Should render a chart without title', () => { it('Should render a chart without title', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000] bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
@@ -47,7 +38,7 @@ describe('XY Chart', () => {
it('y-axis title not required', () => { it('y-axis title not required', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis 4000 --> 11000 y-axis 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000] bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
@@ -59,7 +50,7 @@ describe('XY Chart', () => {
it('Should render a chart without y-axis with different range', () => { it('Should render a chart without y-axis with different range', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000] bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000]
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800] line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
@@ -70,7 +61,7 @@ describe('XY Chart', () => {
it('x axis title not required', () => { it('x axis title not required', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000] bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000]
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800] line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
@@ -81,7 +72,7 @@ describe('XY Chart', () => {
it('Multiple plots can be rendered', () => { it('Multiple plots can be rendered', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
line [23, 46, 77, 34] line [23, 46, 77, 34]
line [45, 32, 33, 12] line [45, 32, 33, 12]
bar [87, 54, 99, 85] bar [87, 54, 99, 85]
@@ -95,7 +86,7 @@ describe('XY Chart', () => {
it('Decimals and negative numbers are supported', () => { it('Decimals and negative numbers are supported', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
y-axis -2.4 --> 3.5 y-axis -2.4 --> 3.5
line [+1.3, .6, 2.4, -.34] line [+1.3, .6, 2.4, -.34]
`, `,
@@ -113,7 +104,7 @@ describe('XY Chart', () => {
height: 20 height: 20
plotReservedSpacePercent: 100 plotReservedSpacePercent: 100
--- ---
xychart xychart-beta
line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800] line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
`, `,
{} {}
@@ -139,7 +130,7 @@ describe('XY Chart', () => {
showTick: false showTick: false
showAxisLine: false showAxisLine: false
--- ---
xychart xychart-beta
bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800] bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
`, `,
{} {}
@@ -149,7 +140,7 @@ describe('XY Chart', () => {
imgSnapshotTest( imgSnapshotTest(
` `
%%{init: {"xyChart": {"width": 1000, "height": 600, "titlePadding": 5, "titleFontSize": 10, "xAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "yAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "plotBorderWidth": 5, "chartOrientation": "horizontal", "plotReservedSpacePercent": 60 }}}%% %%{init: {"xyChart": {"width": 1000, "height": 600, "titlePadding": 5, "titleFontSize": 10, "xAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "yAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "plotBorderWidth": 5, "chartOrientation": "horizontal", "plotReservedSpacePercent": 60 }}}%%
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -190,7 +181,7 @@ describe('XY Chart', () => {
plotReservedSpacePercent: 60 plotReservedSpacePercent: 60
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -211,7 +202,7 @@ describe('XY Chart', () => {
yAxis: yAxis:
showTitle: false showTitle: false
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -232,7 +223,7 @@ describe('XY Chart', () => {
yAxis: yAxis:
showLabel: false showLabel: false
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -253,7 +244,7 @@ describe('XY Chart', () => {
yAxis: yAxis:
showTick: false showTick: false
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -274,7 +265,7 @@ describe('XY Chart', () => {
yAxis: yAxis:
showAxisLine: false showAxisLine: false
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -303,7 +294,7 @@ describe('XY Chart', () => {
xAxisLineColor: "#87ceeb" xAxisLineColor: "#87ceeb"
plotColorPalette: "#008000, #faba63" plotColorPalette: "#008000, #faba63"
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -316,7 +307,7 @@ describe('XY Chart', () => {
it('should use the correct distances between data points', () => { it('should use the correct distances between data points', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
x-axis 0 --> 2 x-axis 0 --> 2
line [0, 1, 0, 1] line [0, 1, 0, 1]
bar [1, 0, 1, 0] bar [1, 0, 1, 0]
@@ -334,7 +325,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -353,7 +344,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -366,7 +357,7 @@ describe('XY Chart', () => {
it('should render vertical bar chart without labels by default', () => { it('should render vertical bar chart without labels by default', () => {
imgSnapshotTest( imgSnapshotTest(
` `
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -384,7 +375,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -402,7 +393,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Multiple Bar Plots" title "Multiple Bar Plots"
x-axis Categories [A, B, C] x-axis Categories [A, B, C]
y-axis "Values" 0 --> 100 y-axis "Values" 0 --> 100
@@ -421,7 +412,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Multiple Bar Plots" title "Multiple Bar Plots"
x-axis Categories [A, B, C] x-axis Categories [A, B, C]
y-axis "Values" 0 --> 100 y-axis "Values" 0 --> 100
@@ -439,7 +430,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Single Bar Chart" title "Single Bar Chart"
x-axis Categories [A] x-axis Categories [A]
y-axis "Value" 0 --> 100 y-axis "Value" 0 --> 100
@@ -458,7 +449,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Single Bar Chart" title "Single Bar Chart"
x-axis Categories [A] x-axis Categories [A]
y-axis "Value" 0 --> 100 y-axis "Value" 0 --> 100
@@ -476,7 +467,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Decimal and Negative Values" title "Decimal and Negative Values"
x-axis Categories [A, B, C] x-axis Categories [A, B, C]
y-axis -10 --> 10 y-axis -10 --> 10
@@ -495,7 +486,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Decimal and Negative Values" title "Decimal and Negative Values"
x-axis Categories [A, B, C] x-axis Categories [A, B, C]
y-axis -10 --> 10 y-axis -10 --> 10
@@ -513,7 +504,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan,b,c] x-axis Months [jan,b,c]
y-axis "Revenue (in $)" 4000 --> 12000 y-axis "Revenue (in $)" 4000 --> 12000
@@ -570,7 +561,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan,b,c] x-axis Months [jan,b,c]
y-axis "Revenue (in $)" 4000 --> 12000 y-axis "Revenue (in $)" 4000 --> 12000
@@ -624,7 +615,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s] x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s]
y-axis "Revenue (in $)" 4000 --> 12000 y-axis "Revenue (in $)" 4000 --> 12000
@@ -681,7 +672,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s] x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s]
y-axis "Revenue (in $)" 4000 --> 12000 y-axis "Revenue (in $)" 4000 --> 12000
@@ -735,7 +726,7 @@ describe('XY Chart', () => {
xyChart: xyChart:
showDataLabel: true showDataLabel: true
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan] x-axis Months [jan]
y-axis "Revenue (in $)" 3000 --> 12000 y-axis "Revenue (in $)" 3000 --> 12000
@@ -792,7 +783,7 @@ describe('XY Chart', () => {
showDataLabel: true showDataLabel: true
chartOrientation: horizontal chartOrientation: horizontal
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan] x-axis Months [jan]
y-axis "Revenue (in $)" 3000 --> 12000 y-axis "Revenue (in $)" 3000 --> 12000

View File

@@ -1,35 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<h1>Pie chart demos</h1>
<pre class="mermaid">
pie title Default text position: Animal adoption
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"dogs" : -60.67
"rats" : 40.12
</pre>
<hr />
<script type="module">
import mermaid from '/mermaid.esm.mjs';
mermaid.initialize({
theme: 'forest',
logLevel: 3,
securityLevel: 'loose',
});
</script>
</body>
</html>

View File

@@ -75,7 +75,6 @@
end end
D --> D D --> D
end end
C1 --> C1
end end
</pre> </pre>

View File

@@ -2,219 +2,219 @@
"durations": [ "durations": [
{ {
"spec": "cypress/integration/other/configuration.spec.js", "spec": "cypress/integration/other/configuration.spec.js",
"duration": 5815 "duration": 5672
}, },
{ {
"spec": "cypress/integration/other/external-diagrams.spec.js", "spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2035 "duration": 1990
}, },
{ {
"spec": "cypress/integration/other/ghsa.spec.js", "spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3386 "duration": 3186
}, },
{ {
"spec": "cypress/integration/other/iife.spec.js", "spec": "cypress/integration/other/iife.spec.js",
"duration": 2089 "duration": 1948
}, },
{ {
"spec": "cypress/integration/other/interaction.spec.js", "spec": "cypress/integration/other/interaction.spec.js",
"duration": 11578 "duration": 11938
}, },
{ {
"spec": "cypress/integration/other/rerender.spec.js", "spec": "cypress/integration/other/rerender.spec.js",
"duration": 2119 "duration": 1932
}, },
{ {
"spec": "cypress/integration/other/xss.spec.js", "spec": "cypress/integration/other/xss.spec.js",
"duration": 27282 "duration": 27237
}, },
{ {
"spec": "cypress/integration/rendering/appli.spec.js", "spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3377 "duration": 3170
}, },
{ {
"spec": "cypress/integration/rendering/architecture.spec.ts", "spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 97 "duration": 104
}, },
{ {
"spec": "cypress/integration/rendering/block.spec.js", "spec": "cypress/integration/rendering/block.spec.js",
"duration": 18137 "duration": 17390
}, },
{ {
"spec": "cypress/integration/rendering/c4.spec.js", "spec": "cypress/integration/rendering/c4.spec.js",
"duration": 5455 "duration": 5296
}, },
{ {
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js", "spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 40850 "duration": 39004
}, },
{ {
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js", "spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 37964 "duration": 37653
}, },
{ {
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js", "spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 23446 "duration": 23278
}, },
{ {
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js", "spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 37207 "duration": 36645
}, },
{ {
"spec": "cypress/integration/rendering/classDiagram.spec.js", "spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 16531 "duration": 15418
}, },
{ {
"spec": "cypress/integration/rendering/conf-and-directives.spec.js", "spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9385 "duration": 9684
}, },
{ {
"spec": "cypress/integration/rendering/current.spec.js", "spec": "cypress/integration/rendering/current.spec.js",
"duration": 2697 "duration": 2570
}, },
{ {
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js", "spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 88648 "duration": 84687
}, },
{ {
"spec": "cypress/integration/rendering/erDiagram.spec.js", "spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 15094 "duration": 14819
}, },
{ {
"spec": "cypress/integration/rendering/errorDiagram.spec.js", "spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3548 "duration": 3371
}, },
{ {
"spec": "cypress/integration/rendering/flowchart-elk.spec.js", "spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 44889 "duration": 39925
}, },
{ {
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js", "spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 30487 "duration": 34694
}, },
{ {
"spec": "cypress/integration/rendering/flowchart-icon.spec.js", "spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7375 "duration": 7137
}, },
{ {
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts", "spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 24913 "duration": 24740
}, },
{ {
"spec": "cypress/integration/rendering/flowchart-v2.spec.js", "spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 51927 "duration": 42077
}, },
{ {
"spec": "cypress/integration/rendering/flowchart.spec.js", "spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 31676 "duration": 30642
}, },
{ {
"spec": "cypress/integration/rendering/gantt.spec.js", "spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 19897 "duration": 18085
}, },
{ {
"spec": "cypress/integration/rendering/gitGraph.spec.js", "spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 53450 "duration": 50107
}, },
{ {
"spec": "cypress/integration/rendering/iconShape.spec.ts", "spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 287035 "duration": 276279
}, },
{ {
"spec": "cypress/integration/rendering/imageShape.spec.ts", "spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 58555 "duration": 56505
}, },
{ {
"spec": "cypress/integration/rendering/info.spec.ts", "spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3179 "duration": 3036
}, },
{ {
"spec": "cypress/integration/rendering/journey.spec.js", "spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7099 "duration": 6889
}, },
{ {
"spec": "cypress/integration/rendering/kanban.spec.ts", "spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7628 "duration": 7353
}, },
{ {
"spec": "cypress/integration/rendering/katex.spec.js", "spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3764 "duration": 3580
}, },
{ {
"spec": "cypress/integration/rendering/marker_unique_id.spec.js", "spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2573 "duration": 2508
}, },
{ {
"spec": "cypress/integration/rendering/mindmap.spec.ts", "spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 11269 "duration": 10939
}, },
{ {
"spec": "cypress/integration/rendering/newShapes.spec.ts", "spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 148389 "duration": 149102
}, },
{ {
"spec": "cypress/integration/rendering/oldShapes.spec.ts", "spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 113395 "duration": 113987
}, },
{ {
"spec": "cypress/integration/rendering/packet.spec.ts", "spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4714 "duration": 4060
}, },
{ {
"spec": "cypress/integration/rendering/pie.spec.ts", "spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 6446 "duration": 5715
}, },
{ {
"spec": "cypress/integration/rendering/quadrantChart.spec.js", "spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 9133 "duration": 8945
}, },
{ {
"spec": "cypress/integration/rendering/radar.spec.js", "spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5544 "duration": 5337
}, },
{ {
"spec": "cypress/integration/rendering/requirement.spec.js", "spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2709 "duration": 2643
}, },
{ {
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js", "spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 55647 "duration": 52072
}, },
{ {
"spec": "cypress/integration/rendering/sankey.spec.ts", "spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 6751 "duration": 6692
}, },
{ {
"spec": "cypress/integration/rendering/sequencediagram.spec.js", "spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 36618 "duration": 35721
}, },
{ {
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js", "spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 29642 "duration": 26030
}, },
{ {
"spec": "cypress/integration/rendering/stateDiagram.spec.js", "spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16037 "duration": 16333
}, },
{ {
"spec": "cypress/integration/rendering/theme.spec.js", "spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30006 "duration": 29287
}, },
{ {
"spec": "cypress/integration/rendering/timeline.spec.ts", "spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8451 "duration": 8491
}, },
{ {
"spec": "cypress/integration/rendering/treemap.spec.ts", "spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 11996 "duration": 12291
}, },
{ {
"spec": "cypress/integration/rendering/xyChart.spec.js", "spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 20627 "duration": 20651
}, },
{ {
"spec": "cypress/integration/rendering/zenuml.spec.js", "spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3472 "duration": 3218
} }
] ]
} }

View File

@@ -10,7 +10,7 @@
<body> <body>
<h1>Block diagram demos</h1> <h1>Block diagram demos</h1>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
columns 1 columns 1
db(("DB")) db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down) blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -26,7 +26,7 @@ columns 1
style B fill:#f9F,stroke:#333,stroke-width:4px style B fill:#f9F,stroke:#333,stroke-width:4px
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
A1["square"] A1["square"]
B1("rounded") B1("rounded")
C1(("circle")) C1(("circle"))
@@ -36,7 +36,7 @@ block
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
A1(["stadium"]) A1(["stadium"])
A2[["subroutine"]] A2[["subroutine"]]
B1[("cylinder")] B1[("cylinder")]
@@ -48,7 +48,7 @@ block
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
block:e:4 block:e:4
columns 2 columns 2
f f
@@ -57,7 +57,7 @@ block
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
block:e:4 block:e:4
columns 2 columns 2
f f
@@ -67,7 +67,7 @@ block
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
columns 3 columns 3
a:3 a:3
block:e:3 block:e:3
@@ -80,7 +80,7 @@ block
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
columns 4 columns 4
a b c d a b c d
block:e:4 block:e:4
@@ -97,19 +97,19 @@ flowchart LR
X-- "a label" -->z X-- "a label" -->z
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
columns 5 columns 5
A space B A space B
A --x B A --x B
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
columns 3 columns 3
a["A wide one"] b:2 c:2 d a["A wide one"] b:2 c:2 d
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
columns 3 columns 3
a b c a b c
e:3 e:3
@@ -117,7 +117,7 @@ columns 3
</pre> </pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
block block-beta
A1:3 A1:3
A2:1 A2:1

View File

@@ -20,14 +20,12 @@
width: 800 width: 800
nodeAlignment: left nodeAlignment: left
--- ---
sankey sankey-beta
a,b,8 Revenue,Expenses,10
b,c,8 Revenue,Profit,10
c,d,8 Expenses,Manufacturing,5
d,e,8 Expenses,Tax,3
Expenses,Research,2
x,c,4
c,y,4
</pre> </pre>
<h2>Energy flow</h2> <h2>Energy flow</h2>
@@ -42,7 +40,7 @@
linkColor: gradient linkColor: gradient
nodeAlignment: justify nodeAlignment: justify
--- ---
sankey sankey-beta
Agricultural 'waste',Bio-conversion,124.729 Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597 Bio-conversion,Liquid,0.597

View File

@@ -16,7 +16,7 @@
<body> <body>
<h1>XY Charts demos</h1> <h1>XY Charts demos</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart xychart-beta
title "Sales Revenue (in $)" title "Sales Revenue (in $)"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -26,7 +26,7 @@
<hr /> <hr />
<h1>XY Charts horizontal</h1> <h1>XY Charts horizontal</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart horizontal xychart-beta horizontal
title "Basic xychart" title "Basic xychart"
x-axis "this is x axis" [category1, "category 2", category3, category4] x-axis "this is x axis" [category1, "category 2", category3, category4]
y-axis yaxisText 10 --> 150 y-axis yaxisText 10 --> 150
@@ -36,7 +36,7 @@
<hr /> <hr />
<h1>XY Charts only lines and bar</h1> <h1>XY Charts only lines and bar</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart xychart-beta
line [23, 46, 77, 34] line [23, 46, 77, 34]
line [45, 32, 33, 12] line [45, 32, 33, 12]
line [87, 54, 99, 85] line [87, 54, 99, 85]
@@ -48,13 +48,13 @@
<hr /> <hr />
<h1>XY Charts with +ve and -ve numbers</h1> <h1>XY Charts with +ve and -ve numbers</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart xychart-beta
line [+1.3, .6, 2.4, -.34] line [+1.3, .6, 2.4, -.34]
</pre> </pre>
<h1>XY Charts Bar with multiple category</h1> <h1>XY Charts Bar with multiple category</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart xychart-beta
title "Basic xychart with many categories" title "Basic xychart with many categories"
x-axis "this is x axis" [category1, "category 2", category3, category4, category5, category6, category7] x-axis "this is x axis" [category1, "category 2", category3, category4, category5, category6, category7]
y-axis yaxisText 10 --> 150 y-axis yaxisText 10 --> 150
@@ -63,7 +63,7 @@
<h1>XY Charts line with multiple category</h1> <h1>XY Charts line with multiple category</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart xychart-beta
title "Line chart with many category" title "Line chart with many category"
x-axis "this is x axis" [category1, "category 2", category3, category4, category5, category6, category7] x-axis "this is x axis" [category1, "category 2", category3, category4, category5, category6, category7]
y-axis yaxisText 10 --> 150 y-axis yaxisText 10 --> 150
@@ -72,7 +72,7 @@
<h1>XY Charts category with large text</h1> <h1>XY Charts category with large text</h1>
<pre class="mermaid"> <pre class="mermaid">
xychart xychart-beta
title "Basic xychart with many categories with category overlap" title "Basic xychart with many categories with category overlap"
x-axis "this is x axis" [category1, "Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", category3, category4, category5, category6, category7] x-axis "this is x axis" [category1, "Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", category3, category4, category5, category6, category7]
y-axis yaxisText 10 --> 150 y-axis yaxisText 10 --> 150
@@ -89,7 +89,7 @@ config:
height: 20 height: 20
plotReservedSpacePercent: 100 plotReservedSpacePercent: 100
--- ---
xychart xychart-beta
line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800] line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
</pre> </pre>
@@ -103,7 +103,7 @@ config:
height: 20 height: 20
plotReservedSpacePercent: 100 plotReservedSpacePercent: 100
--- ---
xychart xychart-beta
bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800] bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
</pre> </pre>
@@ -136,7 +136,7 @@ config:
chartOrientation: horizontal chartOrientation: horizontal
plotReservedSpacePercent: 60 plotReservedSpacePercent: 60
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -162,7 +162,7 @@ config:
xAxisLineColor: "#87ceeb" xAxisLineColor: "#87ceeb"
plotColorPalette: "#008000, #faba63" plotColorPalette: "#008000, #faba63"
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000

View File

@@ -10,7 +10,7 @@
# Interface: ParseOptions # Interface: ParseOptions
Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L72) Defined in: [packages/mermaid/src/types.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L59)
## Properties ## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mer
> `optional` **suppressErrors**: `boolean` > `optional` **suppressErrors**: `boolean`
Defined in: [packages/mermaid/src/types.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L77) Defined in: [packages/mermaid/src/types.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L64)
If `true`, parse will return `false` instead of throwing error when the diagram is invalid. If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
The `parseError` function will not be called. The `parseError` function will not be called.

View File

@@ -10,7 +10,7 @@
# Interface: ParseResult # Interface: ParseResult
Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80) Defined in: [packages/mermaid/src/types.ts:67](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L67)
## Properties ## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mer
> **config**: [`MermaidConfig`](MermaidConfig.md) > **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88) Defined in: [packages/mermaid/src/types.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L75)
The config passed as YAML frontmatter or directives The config passed as YAML frontmatter or directives
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
> **diagramType**: `string` > **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84) Defined in: [packages/mermaid/src/types.ts:71](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L71)
The diagram type, e.g. 'flowchart', 'sequence', etc. The diagram type, e.g. 'flowchart', 'sequence', etc.

View File

@@ -10,7 +10,7 @@
# Interface: RenderResult # Interface: RenderResult
Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98) Defined in: [packages/mermaid/src/types.ts:85](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L85)
## Properties ## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mer
> `optional` **bindFunctions**: (`element`) => `void` > `optional` **bindFunctions**: (`element`) => `void`
Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116) Defined in: [packages/mermaid/src/types.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L103)
Bind function to be called after the svg has been inserted into the DOM. Bind function to be called after the svg has been inserted into the DOM.
This is necessary for adding event listeners to the elements in the svg. This is necessary for adding event listeners to the elements in the svg.
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
> **diagramType**: `string` > **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106) Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93)
The diagram type, e.g. 'flowchart', 'sequence', etc. The diagram type, e.g. 'flowchart', 'sequence', etc.
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
> **svg**: `string` > **svg**: `string`
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102) Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
The svg code for the rendered graph. The svg code for the rendered graph.

View File

@@ -1,189 +0,0 @@
---
references:
- "File: /packages/mermaid/src/diagrams/flowchart/flowDiagram.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDb.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDetector.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/styles.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/types.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowChartShapes.js"
- "File: /packages/mermaid/src/diagrams/flowchart/parser/flowParser.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/elk/detector.ts"
generationTime: 2025-07-23T10:31:53.266Z
---
flowchart TD
%% Entry Points and Detection
Input["User Input Text"] --> Detection{Detection Phase}
Detection --> flowDetector["flowDetector.ts<br/>detector(txt, config)"]
Detection --> flowDetectorV2["flowDetector-v2.ts<br/>detector(txt, config)"]
Detection --> elkDetector["elk/detector.ts<br/>detector(txt, config)"]
flowDetector --> |"Checks /^\s*graph/"| DetectLegacy{Legacy Flowchart?}
flowDetectorV2 --> |"Checks /^\s*flowchart/"| DetectNew{New Flowchart?}
elkDetector --> |"Checks /^\s*flowchart-elk/"| DetectElk{ELK Layout?}
DetectLegacy --> |Yes| LoadDiagram
DetectNew --> |Yes| LoadDiagram
DetectElk --> |Yes| LoadDiagram
%% Loading Phase
LoadDiagram["loader() function"] --> flowDiagram["flowDiagram.ts<br/>diagram object"]
flowDiagram --> DiagramStructure{Diagram Components}
DiagramStructure --> Parser["parser: flowParser"]
DiagramStructure --> Database["db: new FlowDB()"]
DiagramStructure --> Renderer["renderer: flowRenderer-v3-unified"]
DiagramStructure --> Styles["styles: flowStyles"]
DiagramStructure --> Init["init: (cnf: MermaidConfig)"]
%% Parser Phase
Parser --> flowParser["parser/flowParser.ts<br/>newParser.parse(src)"]
flowParser --> |"Preprocesses src"| RemoveWhitespace["Remove trailing whitespace<br/>src.replace(/}\s*\n/g, '}\n')"]
RemoveWhitespace --> flowJison["parser/flow.jison<br/>flowJisonParser.parse(newSrc)"]
flowJison --> ParseGraph["Parse Graph Structure"]
ParseGraph --> ParseVertices["Parse Vertices"]
ParseGraph --> ParseEdges["Parse Edges"]
ParseGraph --> ParseSubgraphs["Parse Subgraphs"]
ParseGraph --> ParseClasses["Parse Classes"]
ParseGraph --> ParseStyles["Parse Styles"]
%% Database Phase - FlowDB Class
Database --> FlowDBClass["flowDb.ts<br/>FlowDB class"]
FlowDBClass --> DBInit["constructor()<br/>- Initialize counters<br/>- Bind methods<br/>- Setup toolTips<br/>- Call clear()"]
DBInit --> DBMethods{FlowDB Methods}
DBMethods --> addVertex["addVertex(id, textObj, type, style,<br/>classes, dir, props, metadata)"]
DBMethods --> addLink["addLink(_start[], _end[], linkData)"]
DBMethods --> addSingleLink["addSingleLink(_start, _end, type, id)"]
DBMethods --> setDirection["setDirection(dir)"]
DBMethods --> addSubGraph["addSubGraph(nodes[], id, title)"]
DBMethods --> addClass["addClass(id, style)"]
DBMethods --> setClass["setClass(ids, className)"]
DBMethods --> setTooltip["setTooltip(ids, tooltip)"]
DBMethods --> setClickEvent["setClickEvent(id, functionName, args)"]
DBMethods --> setClickFun["setClickFun(id, functionName, args)"]
%% Vertex Processing
addVertex --> VertexProcess{Vertex Processing}
VertexProcess --> CreateVertex["Create FlowVertex object<br/>- id, labelType, domId<br/>- styles[], classes[]"]
VertexProcess --> SanitizeText["sanitizeText(textObj.text)"]
VertexProcess --> ParseMetadata["Parse YAML metadata<br/>yaml.load(yamlData)"]
VertexProcess --> SetVertexProps["Set vertex properties<br/>- shape, label, icon, form<br/>- pos, img, constraint, w, h"]
%% Edge Processing
addSingleLink --> EdgeProcess{Edge Processing}
EdgeProcess --> CreateEdge["Create FlowEdge object<br/>- start, end, type, text<br/>- labelType, classes[]"]
EdgeProcess --> ProcessLinkText["Process link text<br/>- sanitizeText()<br/>- strip quotes"]
EdgeProcess --> SetEdgeProps["Set edge properties<br/>- type, stroke, length"]
EdgeProcess --> GenerateEdgeId["Generate edge ID<br/>getEdgeId(start, end, counter)"]
EdgeProcess --> ValidateEdgeLimit["Validate edge limit<br/>maxEdges check"]
%% Data Collection
DBMethods --> GetData["getData()"]
GetData --> CollectNodes["Collect nodes[] from vertices"]
GetData --> CollectEdges["Collect edges[] from edges"]
GetData --> ProcessSubGraphs["Process subgraphs<br/>- parentDB Map<br/>- subGraphDB Map"]
GetData --> AddNodeFromVertex["addNodeFromVertex()<br/>for each vertex"]
GetData --> ProcessEdgeTypes["destructEdgeType()<br/>arrowTypeStart, arrowTypeEnd"]
%% Node Creation
AddNodeFromVertex --> NodeCreation{Node Creation}
NodeCreation --> FindExistingNode["findNode(nodes, vertex.id)"]
NodeCreation --> CreateBaseNode["Create base node<br/>- id, label, parentId<br/>- cssStyles, cssClasses<br/>- shape, domId, tooltip"]
NodeCreation --> GetCompiledStyles["getCompiledStyles(classDefs)"]
NodeCreation --> GetTypeFromVertex["getTypeFromVertex(vertex)"]
%% Rendering Phase
Renderer --> flowRendererV3["flowRenderer-v3-unified.ts<br/>draw(text, id, version, diag)"]
flowRendererV3 --> RenderInit["Initialize rendering<br/>- getConfig()<br/>- handle securityLevel<br/>- getDiagramElement()"]
RenderInit --> GetLayoutData["diag.db.getData()<br/>as LayoutData"]
GetLayoutData --> SetupLayoutData["Setup layout data<br/>- type, layoutAlgorithm<br/>- direction, spacing<br/>- markers, diagramId"]
SetupLayoutData --> CallRender["render(data4Layout, svg)"]
CallRender --> SetupViewPort["setupViewPortForSVG(svg, padding)"]
SetupViewPort --> ProcessLinks["Process vertex links<br/>- create anchor elements<br/>- handle click events"]
%% Shape Rendering
CallRender --> ShapeSystem["flowChartShapes.js<br/>Shape Functions"]
ShapeSystem --> ShapeFunctions{Shape Functions}
ShapeFunctions --> question["question(parent, bbox, node)"]
ShapeFunctions --> hexagon["hexagon(parent, bbox, node)"]
ShapeFunctions --> rect_left_inv_arrow["rect_left_inv_arrow(parent, bbox, node)"]
ShapeFunctions --> lean_right["lean_right(parent, bbox, node)"]
ShapeFunctions --> lean_left["lean_left(parent, bbox, node)"]
ShapeFunctions --> insertPolygonShape["insertPolygonShape(parent, w, h, points)"]
ShapeFunctions --> intersectPolygon["intersectPolygon(node, points, point)"]
ShapeFunctions --> intersectRect["intersectRect(node, point)"]
%% Styling System
Styles --> stylesTS["styles.ts<br/>getStyles(options)"]
stylesTS --> StyleOptions["FlowChartStyleOptions<br/>- arrowheadColor, border2<br/>- clusterBkg, mainBkg<br/>- fontFamily, textColor"]
StyleOptions --> GenerateCSS["Generate CSS styles<br/>- .label, .cluster-label<br/>- .node, .edgePath<br/>- .flowchart-link, .edgeLabel"]
GenerateCSS --> GetIconStyles["getIconStyles()"]
%% Type System
Parser --> TypeSystem["types.ts<br/>Type Definitions"]
TypeSystem --> FlowVertex["FlowVertex interface"]
TypeSystem --> FlowEdge["FlowEdge interface"]
TypeSystem --> FlowClass["FlowClass interface"]
TypeSystem --> FlowSubGraph["FlowSubGraph interface"]
TypeSystem --> FlowVertexTypeParam["FlowVertexTypeParam<br/>Shape types"]
%% Utility Functions
DBMethods --> UtilityFunctions{Utility Functions}
UtilityFunctions --> lookUpDomId["lookUpDomId(id)"]
UtilityFunctions --> getClasses["getClasses()"]
UtilityFunctions --> getDirection["getDirection()"]
UtilityFunctions --> getVertices["getVertices()"]
UtilityFunctions --> getEdges["getEdges()"]
UtilityFunctions --> getSubGraphs["getSubGraphs()"]
UtilityFunctions --> clear["clear()"]
UtilityFunctions --> defaultConfig["defaultConfig()"]
%% Event Handling
ProcessLinks --> EventHandling{Event Handling}
EventHandling --> setupToolTips["setupToolTips(element)"]
EventHandling --> bindFunctions["bindFunctions(element)"]
EventHandling --> runFunc["utils.runFunc(functionName, args)"]
%% Common Database Functions
DBMethods --> CommonDB["commonDb.js functions"]
CommonDB --> setAccTitle["setAccTitle()"]
CommonDB --> getAccTitle["getAccTitle()"]
CommonDB --> setAccDescription["setAccDescription()"]
CommonDB --> getAccDescription["getAccDescription()"]
CommonDB --> setDiagramTitle["setDiagramTitle()"]
CommonDB --> getDiagramTitle["getDiagramTitle()"]
CommonDB --> commonClear["clear()"]
%% Final Output
ProcessLinks --> FinalSVG["Final SVG Output"]
%% Layout Algorithm Selection
SetupLayoutData --> LayoutAlgorithm{Layout Algorithm}
LayoutAlgorithm --> Dagre["dagre<br/>(default)"]
LayoutAlgorithm --> DagreWrapper["dagre-wrapper<br/>(v2 renderer)"]
LayoutAlgorithm --> ELK["elk<br/>(external package)"]
%% Testing Components
FlowDBClass --> TestFiles["Test Files"]
TestFiles --> flowDbSpec["flowDb.spec.ts"]
TestFiles --> flowChartShapesSpec["flowChartShapes.spec.js"]
TestFiles --> ParserTests["parser/*.spec.js files<br/>- flow-text.spec.js<br/>- flow-edges.spec.js<br/>- flow-style.spec.js<br/>- subgraph.spec.js"]
%% Configuration
Init --> ConfigSetup["Configuration Setup"]
ConfigSetup --> FlowchartConfig["cnf.flowchart config"]
ConfigSetup --> ArrowMarkers["arrowMarkerAbsolute"]
ConfigSetup --> LayoutConfig["layout config"]
ConfigSetup --> SetConfig["setConfig() calls"]

View File

@@ -1,307 +0,0 @@
---
references:
- "File: /packages/mermaid/src/mermaidAPI.ts"
- "File: /packages/mermaid/src/mermaid.ts"
generationTime: 2025-01-28T16:30:00.000Z
---
sequenceDiagram
participant User as User/Browser
participant Mermaid as mermaid.ts
participant Queue as executionQueue
participant API as mermaidAPI.ts
participant Config as configApi
participant Preprocessor as preprocessDiagram
participant DiagramAPI as diagram-api
participant Diagram as Diagram.fromText
participant Renderer as diagram.renderer
participant Styles as Styling System
participant DOM as DOM/SVG
Note over User, DOM: Mermaid Complete API Flow
%% Initialization Phase
User->>+Mermaid: mermaid.initialize(config)
Mermaid->>+API: mermaidAPI.initialize(config)
API->>API: assignWithDepth({}, userOptions)
API->>API: handle legacy fontFamily config
API->>Config: saveConfigFromInitialize(options)
alt Theme Configuration Available
API->>API: check if theme in theme object
API->>API: set themeVariables from theme
else Default Theme
API->>API: use default theme variables
end
API->>Config: setSiteConfig(options) or getSiteConfig()
API->>API: setLogLevel(config.logLevel)
API->>DiagramAPI: addDiagrams()
API-->>-Mermaid: initialization complete
Mermaid-->>-User: ready to render
%% Content Loaded Event
User->>DOM: document.load event
DOM->>+Mermaid: contentLoaded()
opt startOnLoad is true
Mermaid->>Config: getConfig()
Config-->>Mermaid: { startOnLoad: true }
Mermaid->>Mermaid: run()
end
Mermaid-->>-DOM: event handling complete
%% Main Run Function
User->>+Mermaid: mermaid.run(options)
Mermaid->>Mermaid: runThrowsErrors(options)
Mermaid->>Config: getConfig()
Config-->>Mermaid: configuration object
alt nodes provided
Mermaid->>Mermaid: use provided nodes
else querySelector provided
Mermaid->>DOM: document.querySelectorAll(querySelector)
DOM-->>Mermaid: nodesToProcess
end
Mermaid->>Mermaid: new InitIDGenerator(deterministicIds, seed)
loop For each diagram element
Mermaid->>DOM: check element.getAttribute('data-processed')
opt not processed
Mermaid->>DOM: element.setAttribute('data-processed', 'true')
Mermaid->>Mermaid: generate unique id
Mermaid->>DOM: get element.innerHTML
Mermaid->>Mermaid: entityDecode and clean text
Mermaid->>Mermaid: detectInit(txt)
Mermaid->>Queue: render(id, txt, element)
end
end
Mermaid-->>-User: processing initiated
%% Render Function (Queued)
activate Queue
Queue->>+API: mermaidAPI.render(id, text, container)
API->>DiagramAPI: addDiagrams()
API->>+Preprocessor: processAndSetConfigs(text)
Preprocessor->>Preprocessor: preprocessDiagram(text)
Preprocessor->>Config: reset()
Preprocessor->>Config: addDirective(processed.config)
Preprocessor-->>-API: { code, config }
API->>Config: getConfig()
Config-->>API: current configuration
opt text length > maxTextSize
API->>API: text = MAX_TEXTLENGTH_EXCEEDED_MSG
end
API->>API: setup id selectors and element IDs
API->>API: determine security level (sandbox/loose)
%% DOM Setup
alt svgContainingElement provided
alt isSandboxed
API->>DOM: sandboxedIframe(select(svgContainingElement), iFrameID)
API->>DOM: select iframe contentDocument body
else
API->>DOM: select(svgContainingElement)
end
else no container
API->>API: removeExistingElements(document, id, divId, iFrameId)
alt isSandboxed
API->>DOM: sandboxedIframe(select('body'), iFrameID)
else
API->>DOM: select('body')
end
end
API->>DOM: appendDivSvgG(root, id, enclosingDivID, fontFamily, XMLNS_XLINK_STD)
%% Diagram Creation
API->>+Diagram: Diagram.fromText(text, { title: processed.title })
Diagram->>DiagramAPI: detect diagram type
Diagram->>DiagramAPI: load appropriate diagram
Diagram-->>-API: diagram instance
opt parsing error occurred
API->>+Diagram: Diagram.fromText('error')
Diagram-->>-API: error diagram
API->>API: store parseEncounteredException
end
%% Style Generation
API->>DOM: get svg element and firstChild
API->>Renderer: diag.renderer.getClasses(text, diag)
Renderer-->>API: diagramClassDefs
API->>+Styles: createUserStyles(config, diagramType, diagramClassDefs, idSelector)
Styles->>Styles: createCssStyles(config, classDefs)
opt config.themeCSS defined
Styles->>Styles: append themeCSS
end
opt fontFamily configured
Styles->>Styles: add CSS variables for fonts
end
opt classDefs exist
loop For each styleClassDef
opt has styles
Styles->>Styles: cssImportantStyles for each CSS element
end
opt has textStyles
Styles->>Styles: cssImportantStyles for tspan elements
end
end
end
Styles->>Styles: getStyles(graphType, userCSSstyles, themeVariables)
Styles->>Styles: serialize(compile(svgId{allStyles}), stringify)
Styles-->>-API: compiled CSS rules
API->>DOM: create style element
API->>DOM: style.innerHTML = rules
API->>DOM: svg.insertBefore(style, firstChild)
%% Diagram Rendering
API->>+Renderer: diag.renderer.draw(text, id, version, diag)
Renderer->>Renderer: diagram-specific rendering logic
Renderer->>DOM: create SVG elements
Renderer->>DOM: apply positioning and styling
Renderer-->>-API: rendered diagram
opt rendering error
alt suppressErrorRendering
API->>API: removeTempElements()
API->>Mermaid: throw error
else
API->>Renderer: errorRenderer.draw(text, id, version)
end
end
%% Accessibility and Cleanup
API->>DOM: select svg element
API->>Diagram: diag.db.getAccTitle()
API->>Diagram: diag.db.getAccDescription()
API->>API: addA11yInfo(diagramType, svgNode, a11yTitle, a11yDescr)
API->>DOM: set xmlns for foreignobject elements
API->>DOM: get innerHTML from enclosing div
API->>+API: cleanUpSvgCode(svgCode, isSandboxed, arrowMarkerAbsolute)
opt not useArrowMarkerUrls and not sandboxed
API->>API: replace marker-end URLs with anchors
end
API->>API: decodeEntities(svgCode)
API->>API: replace <br> with <br/>
API-->>-API: cleaned SVG code
alt isSandboxed
API->>+API: putIntoIFrame(svgCode, svgEl)
API->>API: calculate iframe height
API->>API: toBase64 encode SVG content
API->>API: create iframe with sandbox attributes
API-->>-API: iframe HTML
else not loose security
API->>API: DOMPurify.sanitize(svgCode, options)
end
API->>API: attachFunctions()
API->>API: removeTempElements()
opt parseEncounteredException
API->>Mermaid: throw parseEncounteredException
end
API-->>-Queue: { diagramType, svg: svgCode, bindFunctions }
%% Return to Web Integration
activate Mermaid
Queue-->>Mermaid: render result
Mermaid->>DOM: element.innerHTML = svg
opt postRenderCallback
Mermaid->>User: postRenderCallback(id)
end
opt bindFunctions exist
Mermaid->>DOM: bindFunctions(element)
end
deactivate Mermaid
%% Parse Function Flow
User->>+Mermaid: mermaid.parse(text, parseOptions)
activate Queue
Queue->>+API: mermaidAPI.parse(text, parseOptions)
API->>DiagramAPI: addDiagrams()
API->>Preprocessor: processAndSetConfigs(text)
Preprocessor-->>API: { code, config }
API->>Diagram: getDiagramFromText(code)
Diagram-->>API: diagram instance
API-->>-Queue: { diagramType: diagram.type, config }
Queue-->>-Mermaid: parse result
Mermaid-->>-User: ParseResult or false
%% External Diagram Registration
User->>+Mermaid: registerExternalDiagrams(diagrams, options)
Mermaid->>DiagramAPI: addDiagrams()
Mermaid->>DiagramAPI: registerLazyLoadedDiagrams(...diagrams)
opt lazyLoad is false
Mermaid->>DiagramAPI: loadRegisteredDiagrams()
end
Mermaid-->>-User: registration complete
%% Error Handling
Note over Mermaid, API: Error Handling Throughout
alt Error occurs
API->>Mermaid: throw error
Mermaid->>+Mermaid: handleError(error, errors, parseError)
Mermaid->>Mermaid: log.warn(error)
alt isDetailedError
Mermaid->>User: parseError(error.str, error.hash)
else
Mermaid->>User: parseError(error)
end
opt not suppressErrors
Mermaid->>User: throw error
end
Mermaid-->>-User: error handled
end
%% Configuration Details
Note over Config: Configuration Functions
Note right of Config: Functions:<br/>- reset()<br/>- getConfig()<br/>- setConfig()<br/>- getSiteConfig()<br/>- updateSiteConfig()<br/>- saveConfigFromInitialize()
Note over Styles: CSS Generation
Note right of Styles: Features:<br/>- createCssStyles()<br/>- createUserStyles()<br/>- cssImportantStyles()<br/>- Theme integration<br/>- Class definitions
Note over API: Security Levels
Note right of API: Modes:<br/>- sandbox: iframe isolation<br/>- loose: minimal sanitization<br/>- default: DOMPurify sanitization
Note over API: Helper Functions
Note right of API: Utilities:<br/>- cleanUpSvgCode()<br/>- putIntoIFrame()<br/>- appendDivSvgG()<br/>- removeExistingElements()

View File

@@ -1,180 +0,0 @@
---
references:
- "File: /packages/mermaid/src/diagrams/mindmap/mindmap-definition.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/mindmapDb.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/detector.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/styles.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/svgDraw.ts"
generationTime: 2025-01-28T16:00:00.000Z
---
sequenceDiagram
participant User as User Input Text
participant Detector as detector.ts
participant Loader as DiagramLoader
participant Definition as mindmap-definition.ts
participant Parser as parser/mindmap.jison
participant DB as MindmapDB
participant Renderer as mindmapRenderer.ts
participant Cytoscape as cytoscape.js
participant SVGDraw as svgDraw.ts
participant Styles as styles.ts
participant Output as Final SVG
Note over User, Output: Mindmap Implementation Flow
%% Detection Phase
User->>Detector: /^\s*mindmap/ text input
activate Detector
Detector->>Detector: detector(txt) validates pattern
Detector->>Loader: loader() function called
deactivate Detector
activate Loader
Loader->>Definition: import mindmap-definition.js
deactivate Loader
%% Core Structure Setup
activate Definition
Definition->>DB: get db() → new MindmapDB()
Definition->>Renderer: setup renderer
Definition->>Parser: setup parser
Definition->>Styles: setup styles
deactivate Definition
%% Database Initialization
activate DB
Note over DB: MindmapDB Constructor
DB->>DB: initialize nodes array
DB->>DB: setup nodeType constants
DB->>DB: bind methods
DB->>DB: clear() state
%% Parsing Phase
activate Parser
User->>Parser: mindmap syntax text
loop For each node in hierarchy
Parser->>DB: addNode(level, id, descr, type)
activate DB
DB->>DB: sanitizeText(id, descr)
DB->>DB: getType(startStr, endStr)
Note right of DB: Shape Detection:<br/>[ → RECT<br/>( → ROUNDED_RECT<br/>(( → CIRCLE<br/>)) → BANG<br/>{{ → HEXAGON
DB->>DB: getParent(level)
DB->>DB: create MindmapNode
DB->>DB: add to hierarchy
deactivate DB
end
opt Icon/Class Decoration
Parser->>DB: decorateNode(decoration)
DB->>DB: set icon/class properties
end
deactivate Parser
%% Data Preparation
Renderer->>DB: getData() for layout
activate DB
DB->>DB: collect all nodes
DB->>DB: build parent-child relationships
DB-->>Renderer: return node hierarchy
deactivate DB
%% Rendering Pipeline
activate Renderer
Note over Renderer: Rendering Phase
Renderer->>Cytoscape: initialize cytoscape
activate Cytoscape
loop For each node in mindmap
Renderer->>Cytoscape: addNodes(mindmap, cy, conf, level)
Cytoscape->>Cytoscape: create node data
Cytoscape->>Cytoscape: set position (x, y)
end
loop For parent-child relationships
Renderer->>Cytoscape: add edges
Cytoscape->>Cytoscape: create edge data
end
Renderer->>Cytoscape: configure cose-bilkent layout
Cytoscape->>Cytoscape: calculate optimal positions
Cytoscape-->>Renderer: return positioned graph
deactivate Cytoscape
%% SVG Generation
Renderer->>SVGDraw: drawNodes(db, svg, mindmap, section, conf)
activate SVGDraw
loop For each node recursively
SVGDraw->>SVGDraw: select shape function
alt Default Shape
SVGDraw->>SVGDraw: defaultBkg() - rounded rectangle
else Rectangle Shape
SVGDraw->>SVGDraw: rectBkg() - sharp corners
else Circle Shape
SVGDraw->>SVGDraw: circleBkg() - perfect circle
else Cloud Shape
SVGDraw->>SVGDraw: cloudBkg() - organic curves
else Bang Shape
SVGDraw->>SVGDraw: bangBkg() - explosion style
else Hexagon Shape
SVGDraw->>SVGDraw: hexagonBkg() - six sides
end
SVGDraw->>SVGDraw: create SVG elements
SVGDraw->>SVGDraw: add text labels
SVGDraw->>SVGDraw: position node
opt Node has children
SVGDraw->>SVGDraw: drawNodes() recursive call
end
end
deactivate SVGDraw
%% Edge Rendering
Renderer->>Renderer: drawEdges(edgesEl, cy)
loop For each edge
Renderer->>Renderer: extract edge bounds
Renderer->>Renderer: draw SVG path
end
%% Styling Application
Renderer->>Styles: getStyles(options)
activate Styles
Styles->>Styles: genSections(options)
loop For THEME_COLOR_LIMIT sections
Styles->>Styles: generate color scale
Styles->>Styles: create CSS rules
Note right of Styles: .section-X fills<br/>.edge-depth-X widths<br/>.node-icon-X colors
end
Styles->>Styles: apply theme integration
Styles-->>Renderer: return compiled CSS
deactivate Styles
%% Final Assembly
Renderer->>Output: selectSvgElement()
Renderer->>Output: setupGraphViewbox()
Renderer->>Output: apply styles
Renderer->>Output: add interactive elements
deactivate Renderer
activate Output
Note over Output: Final Mindmap Features
Output->>Output: responsive layout
Output->>Output: accessibility attributes
Output->>Output: hover effects
Output->>Output: click handling
Output-->>User: rendered mindmap
deactivate Output
%% Configuration Details
Note over DB, Styles: Configuration Options
Note right of DB: Padding Calculations:<br/>Base padding from config<br/>RECT: ×2 padding<br/>ROUNDED_RECT: ×2 padding<br/>HEXAGON: ×2 padding
Note right of Styles: Section Management:<br/>MAX_SECTIONS = 12<br/>Dynamic color generation<br/>Git theme integration
Note right of Renderer: Layout Parameters:<br/>Cytoscape configuration<br/>coseBilkent settings<br/>Node spacing rules

View File

@@ -84,7 +84,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
LLM integrations to create mermaid diagrams using AI from text descriptions. LLM integrations to create mermaid diagrams using AI from text descriptions.
- [HueHive - Create mermaid diagrams with text](https://huehive.co/tools/diagrams) - [HueHive - Create mermaid diagrams with text](https://huehive.co/tools/diagrams)
- [MCP Server Mermaid](https://github.com/hustcc/mcp-mermaid) - Generate mermaid diagram and chart with AI MCP dynamically.
### CRM/ERP ### CRM/ERP
@@ -104,7 +103,6 @@ Blogging frameworks and platforms
- [Mermaid](https://nextra.site/docs/guide/mermaid) - [Mermaid](https://nextra.site/docs/guide/mermaid)
- [WordPress](https://wordpress.org) - [WordPress](https://wordpress.org)
- [MerPRess](https://wordpress.org/plugins/merpress/) - [MerPRess](https://wordpress.org/plugins/merpress/)
- [WP Documentation](https://wordpress.org/themes/wp-documentation/)
### CMS/ECM ### CMS/ECM

View File

@@ -16,7 +16,9 @@ Applications that support Mermaid files [SHOULD](https://datatracker.ietf.org/do
### MIME Type ### MIME Type
The recommended [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) for Mermaid media is [`text/vnd.mermaid`](https://www.iana.org/assignments/media-types/application/vnd.mermaid). The recommended [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) for Mermaid media is `text/vnd.mermaid`.
Currently pending [IANA](https://www.iana.org/) recognition.
## Showcase ## Showcase

View File

@@ -9,7 +9,7 @@
## Introduction to Block Diagrams ## Introduction to Block Diagrams
```mermaid-example ```mermaid-example
block block-beta
columns 1 columns 1
db(("DB")) db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down) blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -26,7 +26,7 @@ columns 1
``` ```
```mermaid ```mermaid
block block-beta
columns 1 columns 1
db(("DB")) db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down) blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -80,12 +80,12 @@ At its core, a block diagram consists of blocks representing different entities
To create a simple block diagram with three blocks labeled 'a', 'b', and 'c', the syntax is as follows: To create a simple block diagram with three blocks labeled 'a', 'b', and 'c', the syntax is as follows:
```mermaid-example ```mermaid-example
block block-beta
a b c a b c
``` ```
```mermaid ```mermaid
block block-beta
a b c a b c
``` ```
@@ -101,13 +101,13 @@ While simple block diagrams are linear and straightforward, more complex systems
In scenarios where you need to distribute blocks across multiple columns, you can specify the number of columns and arrange the blocks accordingly. Here's how to create a block diagram with three columns and four blocks, where the fourth block appears in a second row: In scenarios where you need to distribute blocks across multiple columns, you can specify the number of columns and arrange the blocks accordingly. Here's how to create a block diagram with three columns and four blocks, where the fourth block appears in a second row:
```mermaid-example ```mermaid-example
block block-beta
columns 3 columns 3
a b c d a b c d
``` ```
```mermaid ```mermaid
block block-beta
columns 3 columns 3
a b c d a b c d
``` ```
@@ -130,13 +130,13 @@ In more complex diagrams, you may need blocks that span multiple columns to emph
To create a block diagram where one block spans across two columns, you can specify the desired width for each block: To create a block diagram where one block spans across two columns, you can specify the desired width for each block:
```mermaid-example ```mermaid-example
block block-beta
columns 3 columns 3
a["A label"] b:2 c:2 d a["A label"] b:2 c:2 d
``` ```
```mermaid ```mermaid
block block-beta
columns 3 columns 3
a["A label"] b:2 c:2 d a["A label"] b:2 c:2 d
``` ```
@@ -153,7 +153,7 @@ Composite blocks, or blocks within blocks, are an advanced feature in Mermaid's
Creating a composite block involves defining a parent block and then nesting other blocks within it. Here's how to define a composite block with nested elements: Creating a composite block involves defining a parent block and then nesting other blocks within it. Here's how to define a composite block with nested elements:
```mermaid-example ```mermaid-example
block block-beta
block block
D D
end end
@@ -161,7 +161,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
block block
D D
end end
@@ -180,7 +180,7 @@ Mermaid also allows for dynamic adjustment of column widths based on the content
In diagrams with varying block sizes, Mermaid automatically adjusts the column widths to fit the largest block in each column. Here's an example: In diagrams with varying block sizes, Mermaid automatically adjusts the column widths to fit the largest block in each column. Here's an example:
```mermaid-example ```mermaid-example
block block-beta
columns 3 columns 3
a:3 a:3
block:group1:2 block:group1:2
@@ -195,7 +195,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
columns 3 columns 3
a:3 a:3
block:group1:2 block:group1:2
@@ -215,7 +215,7 @@ This example demonstrates how Mermaid dynamically adjusts the width of the colum
In scenarios where you need to stack blocks horizontally, you can use column width to accomplish the task. Blocks can be arranged vertically by putting them in a single column. Here is how you can create a block diagram in which 4 blocks are stacked on top of each other: In scenarios where you need to stack blocks horizontally, you can use column width to accomplish the task. Blocks can be arranged vertically by putting them in a single column. Here is how you can create a block diagram in which 4 blocks are stacked on top of each other:
```mermaid-example ```mermaid-example
block block-beta
block block
columns 1 columns 1
a["A label"] b c d a["A label"] b c d
@@ -223,7 +223,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
block block
columns 1 columns 1
a["A label"] b c d a["A label"] b c d
@@ -247,12 +247,12 @@ Mermaid supports a range of block shapes to suit different diagramming needs, fr
To create a block with round edges, which can be used to represent a softer or more flexible component: To create a block with round edges, which can be used to represent a softer or more flexible component:
```mermaid-example ```mermaid-example
block block-beta
id1("This is the text in the box") id1("This is the text in the box")
``` ```
```mermaid ```mermaid
block block-beta
id1("This is the text in the box") id1("This is the text in the box")
``` ```
@@ -261,12 +261,12 @@ block
A stadium-shaped block, resembling an elongated circle, can be used for components that are process-oriented: A stadium-shaped block, resembling an elongated circle, can be used for components that are process-oriented:
```mermaid-example ```mermaid-example
block block-beta
id1(["This is the text in the box"]) id1(["This is the text in the box"])
``` ```
```mermaid ```mermaid
block block-beta
id1(["This is the text in the box"]) id1(["This is the text in the box"])
``` ```
@@ -275,12 +275,12 @@ block
For representing subroutines or contained processes, a block with double vertical lines is useful: For representing subroutines or contained processes, a block with double vertical lines is useful:
```mermaid-example ```mermaid-example
block block-beta
id1[["This is the text in the box"]] id1[["This is the text in the box"]]
``` ```
```mermaid ```mermaid
block block-beta
id1[["This is the text in the box"]] id1[["This is the text in the box"]]
``` ```
@@ -289,12 +289,12 @@ block
The cylindrical shape is ideal for representing databases or storage components: The cylindrical shape is ideal for representing databases or storage components:
```mermaid-example ```mermaid-example
block block-beta
id1[("Database")] id1[("Database")]
``` ```
```mermaid ```mermaid
block block-beta
id1[("Database")] id1[("Database")]
``` ```
@@ -303,12 +303,12 @@ block
A circle can be used for centralized or pivotal components: A circle can be used for centralized or pivotal components:
```mermaid-example ```mermaid-example
block block-beta
id1(("This is the text in the circle")) id1(("This is the text in the circle"))
``` ```
```mermaid ```mermaid
block block-beta
id1(("This is the text in the circle")) id1(("This is the text in the circle"))
``` ```
@@ -319,36 +319,36 @@ For decision points, use a rhombus, and for unique or specialized processes, asy
**Asymmetric** **Asymmetric**
```mermaid-example ```mermaid-example
block block-beta
id1>"This is the text in the box"] id1>"This is the text in the box"]
``` ```
```mermaid ```mermaid
block block-beta
id1>"This is the text in the box"] id1>"This is the text in the box"]
``` ```
**Rhombus** **Rhombus**
```mermaid-example ```mermaid-example
block block-beta
id1{"This is the text in the box"} id1{"This is the text in the box"}
``` ```
```mermaid ```mermaid
block block-beta
id1{"This is the text in the box"} id1{"This is the text in the box"}
``` ```
**Hexagon** **Hexagon**
```mermaid-example ```mermaid-example
block block-beta
id1{{"This is the text in the box"}} id1{{"This is the text in the box"}}
``` ```
```mermaid ```mermaid
block block-beta
id1{{"This is the text in the box"}} id1{{"This is the text in the box"}}
``` ```
@@ -357,7 +357,7 @@ block
Parallelogram and trapezoid shapes are perfect for inputs/outputs and transitional processes: Parallelogram and trapezoid shapes are perfect for inputs/outputs and transitional processes:
```mermaid-example ```mermaid-example
block block-beta
id1[/"This is the text in the box"/] id1[/"This is the text in the box"/]
id2[\"This is the text in the box"\] id2[\"This is the text in the box"\]
A[/"Christmas"\] A[/"Christmas"\]
@@ -365,7 +365,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
id1[/"This is the text in the box"/] id1[/"This is the text in the box"/]
id2[\"This is the text in the box"\] id2[\"This is the text in the box"\]
A[/"Christmas"\] A[/"Christmas"\]
@@ -377,12 +377,12 @@ block
For highlighting critical or high-priority components, a double circle can be effective: For highlighting critical or high-priority components, a double circle can be effective:
```mermaid-example ```mermaid-example
block block-beta
id1((("This is the text in the circle"))) id1((("This is the text in the circle")))
``` ```
```mermaid ```mermaid
block block-beta
id1((("This is the text in the circle"))) id1((("This is the text in the circle")))
``` ```
@@ -395,7 +395,7 @@ Mermaid also offers unique shapes like block arrows and space blocks for directi
Block arrows can visually indicate direction or flow within a process: Block arrows can visually indicate direction or flow within a process:
```mermaid-example ```mermaid-example
block block-beta
blockArrowId<["Label"]>(right) blockArrowId<["Label"]>(right)
blockArrowId2<["Label"]>(left) blockArrowId2<["Label"]>(left)
blockArrowId3<["Label"]>(up) blockArrowId3<["Label"]>(up)
@@ -406,7 +406,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
blockArrowId<["Label"]>(right) blockArrowId<["Label"]>(right)
blockArrowId2<["Label"]>(left) blockArrowId2<["Label"]>(left)
blockArrowId3<["Label"]>(up) blockArrowId3<["Label"]>(up)
@@ -421,14 +421,14 @@ block
Space blocks can be used to create intentional empty spaces in the diagram, which is useful for layout and readability: Space blocks can be used to create intentional empty spaces in the diagram, which is useful for layout and readability:
```mermaid-example ```mermaid-example
block block-beta
columns 3 columns 3
a space b a space b
c d e c d e
``` ```
```mermaid ```mermaid
block block-beta
columns 3 columns 3
a space b a space b
c d e c d e
@@ -437,12 +437,12 @@ block
or or
```mermaid-example ```mermaid-example
block block-beta
ida space:3 idb idc ida space:3 idb idc
``` ```
```mermaid ```mermaid
block block-beta
ida space:3 idb idc ida space:3 idb idc
``` ```
@@ -467,13 +467,13 @@ The most fundamental aspect of connecting blocks is the use of arrows or links.
A simple link with an arrow can be created to show direction or flow from one block to another: A simple link with an arrow can be created to show direction or flow from one block to another:
```mermaid-example ```mermaid-example
block block-beta
A space B A space B
A-->B A-->B
``` ```
```mermaid ```mermaid
block block-beta
A space B A space B
A-->B A-->B
``` ```
@@ -490,13 +490,13 @@ Example - Text with Links
To add text to a link, the syntax includes the text within the link definition: To add text to a link, the syntax includes the text within the link definition:
```mermaid-example ```mermaid-example
block block-beta
A space:2 B A space:2 B
A-- "X" -->B A-- "X" -->B
``` ```
```mermaid ```mermaid
block block-beta
A space:2 B A space:2 B
A-- "X" -->B A-- "X" -->B
``` ```
@@ -506,7 +506,7 @@ This example show how to add descriptive text to the links, enhancing the inform
Example - Edges and Styles: Example - Edges and Styles:
```mermaid-example ```mermaid-example
block block-beta
columns 1 columns 1
db(("DB")) db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down) blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -523,7 +523,7 @@ columns 1
``` ```
```mermaid ```mermaid
block block-beta
columns 1 columns 1
db(("DB")) db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down) blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -552,7 +552,7 @@ Mermaid enables detailed styling of individual blocks, allowing you to apply var
To apply custom styles to a block, you can use the `style` keyword followed by the block identifier and the desired CSS properties: To apply custom styles to a block, you can use the `style` keyword followed by the block identifier and the desired CSS properties:
```mermaid-example ```mermaid-example
block block-beta
id1 space id2 id1 space id2
id1("Start")-->id2("Stop") id1("Start")-->id2("Stop")
style id1 fill:#636,stroke:#333,stroke-width:4px style id1 fill:#636,stroke:#333,stroke-width:4px
@@ -560,7 +560,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
id1 space id2 id1 space id2
id1("Start")-->id2("Stop") id1("Start")-->id2("Stop")
style id1 fill:#636,stroke:#333,stroke-width:4px style id1 fill:#636,stroke:#333,stroke-width:4px
@@ -574,7 +574,7 @@ Mermaid enables applying styling to classes, which could make styling easier if
#### Example - Styling a Single Class #### Example - Styling a Single Class
```mermaid-example ```mermaid-example
block block-beta
A space B A space B
A-->B A-->B
classDef blue fill:#6e6ce6,stroke:#333,stroke-width:4px; classDef blue fill:#6e6ce6,stroke:#333,stroke-width:4px;
@@ -583,7 +583,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
A space B A space B
A-->B A-->B
classDef blue fill:#6e6ce6,stroke:#333,stroke-width:4px; classDef blue fill:#6e6ce6,stroke:#333,stroke-width:4px;
@@ -608,7 +608,7 @@ Combining the elements of structure, linking, and styling, we can create compreh
Illustrating a simple software system architecture with interconnected components: Illustrating a simple software system architecture with interconnected components:
```mermaid-example ```mermaid-example
block block-beta
columns 3 columns 3
Frontend blockArrowId6<[" "]>(right) Backend Frontend blockArrowId6<[" "]>(right) Backend
space:2 down<[" "]>(down) space:2 down<[" "]>(down)
@@ -621,7 +621,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
columns 3 columns 3
Frontend blockArrowId6<[" "]>(right) Backend Frontend blockArrowId6<[" "]>(right) Backend
space:2 down<[" "]>(down) space:2 down<[" "]>(down)
@@ -640,7 +640,7 @@ This example shows a basic architecture with a frontend, backend, and database.
Representing a business process flow with decision points and multiple stages: Representing a business process flow with decision points and multiple stages:
```mermaid-example ```mermaid-example
block block-beta
columns 3 columns 3
Start(("Start")) space:2 Start(("Start")) space:2
down<[" "]>(down) space:2 down<[" "]>(down) space:2
@@ -653,7 +653,7 @@ block
``` ```
```mermaid ```mermaid
block block-beta
columns 3 columns 3
Start(("Start")) space:2 Start(("Start")) space:2
down<[" "]>(down) space:2 down<[" "]>(down) space:2
@@ -682,7 +682,7 @@ Understanding and avoiding common syntax errors is key to a smooth experience wi
A common mistake is incorrect linking syntax, which can lead to unexpected results or broken diagrams: A common mistake is incorrect linking syntax, which can lead to unexpected results or broken diagrams:
``` ```
block block-beta
A - B A - B
``` ```
@@ -690,13 +690,13 @@ block
Ensure that links between blocks are correctly specified with arrows (--> or ---) to define the direction and type of connection. Also remember that one of the fundamentals for block diagram is to give the author full control of where the boxes are positioned so in the example you need to add a space between the boxes: Ensure that links between blocks are correctly specified with arrows (--> or ---) to define the direction and type of connection. Also remember that one of the fundamentals for block diagram is to give the author full control of where the boxes are positioned so in the example you need to add a space between the boxes:
```mermaid-example ```mermaid-example
block block-beta
A space B A space B
A --> B A --> B
``` ```
```mermaid ```mermaid
block block-beta
A space B A space B
A --> B A --> B
``` ```
@@ -706,13 +706,13 @@ block
Applying styles in the wrong context or with incorrect syntax can lead to blocks not being styled as intended: Applying styles in the wrong context or with incorrect syntax can lead to blocks not being styled as intended:
```mermaid-example ```mermaid-example
block block-beta
A A
style A fill#969; style A fill#969;
``` ```
```mermaid ```mermaid
block block-beta
A A
style A fill#969; style A fill#969;
``` ```
@@ -721,14 +721,14 @@ Applying styles in the wrong context or with incorrect syntax can lead to blocks
Correct the syntax by ensuring proper separation of style properties with commas and using the correct CSS property format: Correct the syntax by ensuring proper separation of style properties with commas and using the correct CSS property format:
```mermaid-example ```mermaid-example
block block-beta
A A
style A fill:#969,stroke:#333; style A fill:#969,stroke:#333;
``` ```
```mermaid ```mermaid
block block-beta
A A
style A fill:#969,stroke:#333; style A fill:#969,stroke:#333;

View File

@@ -1795,54 +1795,15 @@ It is possible to style the type of curve used for lines between items, if the d
Available curve styles include `basis`, `bumpX`, `bumpY`, `cardinal`, `catmullRom`, `linear`, `monotoneX`, `monotoneY`, Available curve styles include `basis`, `bumpX`, `bumpY`, `cardinal`, `catmullRom`, `linear`, `monotoneX`, `monotoneY`,
`natural`, `step`, `stepAfter`, and `stepBefore`. `natural`, `step`, `stepAfter`, and `stepBefore`.
For a full list of available curves, including an explanation of custom curves, refer to
the [Shapes](https://d3js.org/d3-shape/curve) documentation in the [d3-shape](https://github.com/d3/d3-shape/) project.
Line styling can be achieved in two ways:
1. Change the curve style of all the lines
2. Change the curve style of a particular line
#### Diagram level curve style
In this example, a left-to-right graph uses the `stepBefore` curve style: In this example, a left-to-right graph uses the `stepBefore` curve style:
``` ```
--- %%{ init: { 'flowchart': { 'curve': 'stepBefore' } } }%%
config:
flowchart:
curve: stepBefore
---
graph LR graph LR
``` ```
#### Edge level curve style using Edge IDs (v\<MERMAID_RELEASE_VERSION>+) For a full list of available curves, including an explanation of custom curves, refer to
the [Shapes](https://d3js.org/d3-shape/curve) documentation in the [d3-shape](https://github.com/d3/d3-shape/) project.
You can assign IDs to [edges](#attaching-an-id-to-edges). After assigning an ID you can modify the line style by modifying the edge's `curve` property using the following syntax:
```mermaid-example
flowchart LR
A e1@==> B
A e2@--> C
e1@{ curve: linear }
e2@{ curve: natural }
```
```mermaid
flowchart LR
A e1@==> B
A e2@--> C
e1@{ curve: linear }
e2@{ curve: natural }
```
```info
Any edge curve style modified at the edge level overrides the diagram level style.
```
```info
If the same edge is modified multiple times the last modification will be rendered.
```
### Styling a node ### Styling a node

View File

@@ -37,11 +37,6 @@ Drawing a pie chart is really simple in mermaid.
- Followed by `:` colon as separator - Followed by `:` colon as separator
- Followed by `positive numeric value` (supported up to two decimal places) - Followed by `positive numeric value` (supported up to two decimal places)
**Note:**
> Pie chart values must be **positive numbers greater than zero**.\
> **Negative values are not allowed** and will result in an error.
\[pie] \[showData] (OPTIONAL) \[pie] \[showData] (OPTIONAL)
\[title] \[titlevalue] (OPTIONAL) \[title] \[titlevalue] (OPTIONAL)
"\[datakey1]" : \[dataValue1] "\[datakey1]" : \[dataValue1]

View File

@@ -23,7 +23,7 @@ config:
sankey: sankey:
showValues: false showValues: false
--- ---
sankey sankey-beta
Agricultural 'waste',Bio-conversion,124.729 Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597 Bio-conversion,Liquid,0.597
@@ -101,7 +101,7 @@ config:
sankey: sankey:
showValues: false showValues: false
--- ---
sankey sankey-beta
Agricultural 'waste',Bio-conversion,124.729 Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597 Bio-conversion,Liquid,0.597
@@ -175,7 +175,7 @@ Wind,Electricity grid,289.366
## Syntax ## Syntax
The idea behind syntax is that a user types `sankey` keyword first, then pastes raw CSV below and get the result. The idea behind syntax is that a user types `sankey-beta` keyword first, then pastes raw CSV below and get the result.
It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**: It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**:
@@ -187,7 +187,7 @@ It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.
It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly: It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly:
```mermaid-example ```mermaid-example
sankey sankey-beta
%% source,target,value %% source,target,value
Electricity grid,Over generation / exports,104.453 Electricity grid,Over generation / exports,104.453
@@ -196,7 +196,7 @@ Electricity grid,H2 conversion,27.14
``` ```
```mermaid ```mermaid
sankey sankey-beta
%% source,target,value %% source,target,value
Electricity grid,Over generation / exports,104.453 Electricity grid,Over generation / exports,104.453
@@ -209,7 +209,7 @@ Electricity grid,H2 conversion,27.14
CSV does not support empty lines without comma delimiters by default. But you can add them if needed: CSV does not support empty lines without comma delimiters by default. But you can add them if needed:
```mermaid-example ```mermaid-example
sankey sankey-beta
Bio-conversion,Losses,26.862 Bio-conversion,Losses,26.862
@@ -219,7 +219,7 @@ Bio-conversion,Gas,81.144
``` ```
```mermaid ```mermaid
sankey sankey-beta
Bio-conversion,Losses,26.862 Bio-conversion,Losses,26.862
@@ -233,14 +233,14 @@ Bio-conversion,Gas,81.144
If you need to have a comma, wrap it in double quotes: If you need to have a comma, wrap it in double quotes:
```mermaid-example ```mermaid-example
sankey sankey-beta
Pumped heat,"Heating and cooling, homes",193.026 Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672 Pumped heat,"Heating and cooling, commercial",70.672
``` ```
```mermaid ```mermaid
sankey sankey-beta
Pumped heat,"Heating and cooling, homes",193.026 Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672 Pumped heat,"Heating and cooling, commercial",70.672
@@ -251,14 +251,14 @@ Pumped heat,"Heating and cooling, commercial",70.672
If you need to have double quote, put a pair of them inside quoted string: If you need to have double quote, put a pair of them inside quoted string:
```mermaid-example ```mermaid-example
sankey sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026 Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672 Pumped heat,"Heating and cooling, ""commercial""",70.672
``` ```
```mermaid ```mermaid
sankey sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026 Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672 Pumped heat,"Heating and cooling, ""commercial""",70.672

View File

@@ -13,7 +13,7 @@
## Example ## Example
```mermaid-example ```mermaid-example
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -22,7 +22,7 @@ xychart
``` ```
```mermaid ```mermaid
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -40,7 +40,7 @@ xychart
The chart can be drawn horizontal or vertical, default value is vertical. The chart can be drawn horizontal or vertical, default value is vertical.
``` ```
xychart horizontal xychart-beta horizontal
... ...
``` ```
@@ -51,7 +51,7 @@ The title is a short description of the chart and it will always render on top o
#### Example #### Example
``` ```
xychart xychart-beta
title "This is a simple example" title "This is a simple example"
... ...
``` ```
@@ -98,10 +98,10 @@ A bar chart offers the capability to graphically depict bars.
#### Simplest example #### Simplest example
The only two things required are the chart name (`xychart`) and one data set. So you will be able to draw a chart with a simple config like The only two things required are the chart name (`xychart-beta`) and one data set. So you will be able to draw a chart with a simple config like
``` ```
xychart xychart-beta
line [+1.3, .6, 2.4, -.34] line [+1.3, .6, 2.4, -.34]
``` ```
@@ -176,7 +176,7 @@ config:
xyChart: xyChart:
titleColor: "#ff0000" titleColor: "#ff0000"
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000
@@ -195,7 +195,7 @@ config:
xyChart: xyChart:
titleColor: "#ff0000" titleColor: "#ff0000"
--- ---
xychart xychart-beta
title "Sales Revenue" title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec] x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000 y-axis "Revenue (in $)" 4000 --> 11000

View File

@@ -67,7 +67,7 @@
"@argos-ci/cypress": "^5.0.2", "@argos-ci/cypress": "^5.0.2",
"@changesets/changelog-github": "^0.5.1", "@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.27.12", "@changesets/cli": "^2.27.12",
"@cspell/eslint-plugin": "^8.19.4", "@cspell/eslint-plugin": "^8.19.3",
"@cypress/code-coverage": "^3.12.49", "@cypress/code-coverage": "^3.12.49",
"@eslint/js": "^9.26.0", "@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.2", "@rollup/plugin-typescript": "^12.1.2",
@@ -94,17 +94,17 @@
"cypress-split": "^1.24.14", "cypress-split": "^1.24.14",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"eslint": "^9.26.0", "eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.1",
"eslint-plugin-cypress": "^4.3.0", "eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-html": "^8.1.3", "eslint-plugin-html": "^8.1.2",
"eslint-plugin-jest": "^28.14.0", "eslint-plugin-jest": "^28.11.0",
"eslint-plugin-jsdoc": "^50.8.0", "eslint-plugin-jsdoc": "^50.6.9",
"eslint-plugin-json": "^4.0.1", "eslint-plugin-json": "^4.0.1",
"eslint-plugin-lodash": "^8.0.0", "eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.1.0", "eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-tsdoc": "^0.4.0", "eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^59.0.1", "eslint-plugin-unicorn": "^59.0.0",
"express": "^5.1.0", "express": "^5.1.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"globby": "^14.0.2", "globby": "^14.0.2",
@@ -126,7 +126,7 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"tsx": "^4.7.3", "tsx": "^4.7.3",
"typescript": "~5.7.3", "typescript": "~5.7.3",
"typescript-eslint": "^8.38.0", "typescript-eslint": "^8.32.0",
"vite": "^7.0.3", "vite": "^7.0.3",
"vite-plugin-istanbul": "^7.0.0", "vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.0.6" "vitest": "^3.0.6"

View File

@@ -1,14 +0,0 @@
# @mermaid-js/examples
## 1.0.0
### Minor Changes
- [#6453](https://github.com/mermaid-js/mermaid/pull/6453) [`4936ef5`](https://github.com/mermaid-js/mermaid/commit/4936ef5c306d2f892cca9a95a5deac4af6d4882b) Thanks [@sidharthv96](https://github.com/sidharthv96)! - feat: Add examples for diagrams in the `@mermaid-js/examples` package
### Patch Changes
- [#6510](https://github.com/mermaid-js/mermaid/pull/6510) [`7a38eb7`](https://github.com/mermaid-js/mermaid/commit/7a38eb715d795cd5c66cb59357d64ec197b432e6) Thanks [@sidharthv96](https://github.com/sidharthv96)! - chore: Move packet diagram out of beta
- Updated dependencies [[`5acbd7e`](https://github.com/mermaid-js/mermaid/commit/5acbd7e762469d9d89a9c77faf6617ee13367f3a), [`d90634b`](https://github.com/mermaid-js/mermaid/commit/d90634bf2b09e586b055729e07e9a1a31b21827c), [`7a38eb7`](https://github.com/mermaid-js/mermaid/commit/7a38eb715d795cd5c66cb59357d64ec197b432e6), [`3e3ae08`](https://github.com/mermaid-js/mermaid/commit/3e3ae089305e1c7b9948b9e149eba6854fe7f2d6), [`d3e2be3`](https://github.com/mermaid-js/mermaid/commit/d3e2be35be066adeb7fd502b4a24c223c3b53947), [`637680d`](https://github.com/mermaid-js/mermaid/commit/637680d4d9e39b4f8cb6f05b4cb261e8f5693ac3)]:
- mermaid@11.9.0

View File

@@ -1,41 +0,0 @@
# @mermaid-js/examples
The `@mermaid-js/examples` package contains a collection of examples used by tools like [mermaid.live](https://mermaid.live) to help users get started with new diagrams.
You can duplicate an existing diagram example file, e.g., `packages/examples/src/examples/flowchart.ts`, and modify it with details specific to your diagram.
Then, import the example in the `packages/examples/src/index.ts` file and add it to the `examples` array.
Each diagram should have at least one example, which should be marked as the default. It's a good idea to add more examples to showcase different features of the diagram.
## Usage
```bash
pnpm add @mermaid-js/examples
```
A sample usage of the package in mermaid.live, to get the default example for every diagram type:
```ts
import { diagramData } from '@mermaid-js/examples';
type DiagramDefinition = (typeof diagramData)[number];
const isValidDiagram = (diagram: DiagramDefinition): diagram is Required<DiagramDefinition> => {
return Boolean(diagram.name && diagram.examples && diagram.examples.length > 0);
};
export const getSampleDiagrams = () => {
const diagrams = diagramData
.filter((d) => isValidDiagram(d))
.map(({ examples, ...rest }) => ({
...rest,
example: examples?.filter(({ isDefault }) => isDefault)[0],
}));
const examples: Record<string, string> = {};
for (const diagram of diagrams) {
examples[diagram.name.replace(/ (Diagram|Chart|Graph)/, '')] = diagram.example.code;
}
return examples;
};
```

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mermaid-js/examples", "name": "@mermaid-js/examples",
"version": "1.0.0", "version": "0.0.1-beta.1",
"description": "Mermaid examples package", "description": "Mermaid examples package",
"author": "Sidharth Vinod", "author": "Sidharth Vinod",
"type": "module", "type": "module",
@@ -16,10 +16,6 @@
"files": [ "files": [
"dist" "dist"
], ],
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"
},
"scripts": { "scripts": {
"clean": "rimraf dist" "clean": "rimraf dist"
}, },

View File

@@ -1,384 +0,0 @@
# @mermaid-js/mermaid-zenuml
MermaidJS plugin for ZenUML integration - A powerful sequence diagram rendering engine.
> A Sequence diagram is an interaction diagram that shows how processes operate with one another and in what order.
Mermaid can render sequence diagrams with [ZenUML](https://zenuml.com). Note that ZenUML uses a different
syntax than the original Sequence Diagram in mermaid.
```mermaid
zenuml
BookLibService.Borrow(id) {
User = Session.GetUser()
if(User.isActive) {
try {
BookRepository.Update(id, onLoan, User)
receipt = new Receipt(id, dueDate)
} catch (BookNotFoundException) {
ErrorService.onException(BookNotFoundException)
} finally {
Connection.close()
}
}
return receipt
}
```
## Installation
### With bundlers
```sh
npm install @mermaid-js/mermaid-zenuml
```
```ts
import mermaid from 'mermaid';
import zenuml from '@mermaid-js/mermaid-zenuml';
await mermaid.registerExternalDiagrams([zenuml]);
```
### With CDN
```html
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
import zenuml from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@0.2.0/dist/mermaid-zenuml.core.mjs';
await mermaid.registerExternalDiagrams([zenuml]);
</script>
```
> [!NOTE]
> ZenUML uses experimental lazy loading & async rendering features which could change in the future.
## Basic Usage
Once the plugin is registered, you can create ZenUML diagrams using the `zenuml` syntax:
```mermaid
zenuml
Controller.Get(id) {
Service.Get(id) {
item = Repository.Get(id)
if(item) {
return item
} else {
return null
}
}
return result
}
```
## ZenUML Syntax Reference
### Participants
The participants can be defined implicitly as in the first example on this page. The participants or actors are
rendered in order of appearance in the diagram source text. Sometimes you might want to show the participants in a
different order than how they appear in the first message. It is possible to specify the actor's order of
appearance by doing the following:
```mermaid
zenuml
title Declare participant (optional)
Bob
Alice
Alice->Bob: Hi Bob
Bob->Alice: Hi Alice
```
### Annotators
If you specifically want to use symbols instead of just rectangles with text you can do so by using the annotator syntax to declare participants as per below.
```mermaid
zenuml
title Annotators
@Actor Alice
@Database Bob
Alice->Bob: Hi Bob
Bob->Alice: Hi Alice
```
Available annotators include:
- `@Actor` - Human figure
- `@Database` - Database symbol
- `@Boundary` - Boundary symbol
- `@Control` - Control symbol
- `@Entity` - Entity symbol
- `@Queue` - Queue symbol
### Aliases
The participants can have a convenient identifier and a descriptive label.
```mermaid
zenuml
title Aliases
A as Alice
J as John
A->J: Hello John, how are you?
J->A: Great!
```
## Messages
Messages can be one of:
1. Sync message
2. Async message
3. Creation message
4. Reply message
### Sync message
You can think of a sync (blocking) method in a programming language.
```mermaid
zenuml
title Sync message
A.SyncMessage
A.SyncMessage(with, parameters) {
B.nestedSyncMessage()
}
```
### Async message
You can think of an async (non-blocking) method in a programming language. Fire an event and forget about it.
```mermaid
zenuml
title Async message
Alice->Bob: How are you?
```
### Creation message
We use `new` keyword to create an object.
```mermaid
zenuml
new A1
new A2(with, parameters)
```
### Reply message
There are three ways to express a reply message:
```mermaid
zenuml
// 1. assign a variable from a sync message.
a = A.SyncMessage()
// 1.1. optionally give the variable a type
SomeType a = A.SyncMessage()
// 2. use return keyword
A.SyncMessage() {
return result
}
// 3. use @return or @reply annotator on an async message
@return
A->B: result
```
The third way `@return` is rarely used, but it is useful when you want to return to one level up.
```mermaid
zenuml
title Reply message
Client->A.method() {
B.method() {
if(condition) {
return x1
// return early
@return
A->Client: x11
}
}
return x2
}
```
## Advanced Features
### Nesting
Sync messages and Creation messages are naturally nestable with `{}`.
```mermaid
zenuml
A.method() {
B.nested_sync_method()
B->C: nested async message
}
```
### Comments
It is possible to add comments to a sequence diagram with `// comment` syntax.
Comments will be rendered above the messages or fragments. Comments on other places
are ignored. Markdown is supported.
```mermaid
zenuml
// a comment on a participant will not be rendered
BookService
// a comment on a message.
// **Markdown** is supported.
BookService.getBook()
```
### Loops
It is possible to express loops in a ZenUML diagram. This is done by any of the
following notations:
1. while
2. for
3. forEach, foreach
4. loop
```zenuml
while(condition) {
...statements...
}
```
Example:
```mermaid
zenuml
Alice->John: Hello John, how are you?
while(true) {
John->Alice: Great!
}
```
### Alt (Alternative paths)
It is possible to express alternative paths in a sequence diagram. This is done by the notation
```zenuml
if(condition1) {
...statements...
} else if(condition2) {
...statements...
} else {
...statements...
}
```
Example:
```mermaid
zenuml
Alice->Bob: Hello Bob, how are you?
if(is_sick) {
Bob->Alice: Not so good :(
} else {
Bob->Alice: Feeling fresh like a daisy
}
```
### Opt (Optional)
It is possible to render an `opt` fragment. This is done by the notation
```zenuml
opt {
...statements...
}
```
Example:
```mermaid
zenuml
Alice->Bob: Hello Bob, how are you?
Bob->Alice: Not so good :(
opt {
Bob->Alice: Thanks for asking
}
```
### Parallel
It is possible to show actions that are happening in parallel.
This is done by the notation
```zenuml
par {
statement1
statement2
statement3
}
```
Example:
```mermaid
zenuml
par {
Alice->Bob: Hello guys!
Alice->John: Hello guys!
}
```
### Try/Catch/Finally (Break)
It is possible to indicate a stop of the sequence within the flow (usually used to model exceptions).
This is done by the notation
```
try {
...statements...
} catch {
...statements...
} finally {
...statements...
}
```
Example:
```mermaid
zenuml
try {
Consumer->API: Book something
API->BookingService: Start booking process
} catch {
API->Consumer: show failure
} finally {
API->BookingService: rollback status
}
```
## Contributing
This package is part of the [Mermaid](https://github.com/mermaid-js/mermaid) project. See the main repository for contributing guidelines.
## Contributors
- [Peng Xiao](https://github.com/MrCoder)
- [Sidharth Vinod](https://sidharth.dev)
- [Dong Cai](https://github.com/dontry)
## License
MIT
## Links
- [ZenUML Official Website](https://zenuml.com)
- [Mermaid Documentation](https://mermaid.js.org)
- [GitHub Repository](https://github.com/mermaid-js/mermaid)

View File

@@ -0,0 +1 @@
../mermaid/src/docs/syntax/zenuml.md

View File

@@ -33,7 +33,7 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@zenuml/core": "^3.35.2" "@zenuml/core": "^3.31.1"
}, },
"devDependencies": { "devDependencies": {
"mermaid": "workspace:^" "mermaid": "workspace:^"

View File

@@ -1,11 +0,0 @@
declare module '@zenuml/core' {
interface RenderOptions {
theme?: string;
mode?: string;
}
export default class ZenUml {
constructor(container: Element);
render(text: string, options?: RenderOptions): Promise<void>;
}
}

View File

@@ -53,6 +53,7 @@ export const draw = async function (text: string, id: string) {
const { foreignObject, container, app } = createForeignObject(id); const { foreignObject, container, app } = createForeignObject(id);
svgContainer.appendChild(foreignObject); svgContainer.appendChild(foreignObject);
// @ts-expect-error @zenuml/core@3.0.0 exports the wrong type for ZenUml
const zenuml = new ZenUml(app); const zenuml = new ZenUml(app);
// default is a theme name. More themes to be added and will be configurable in the future // default is a theme name. More themes to be added and will be configurable in the future
await zenuml.render(text, { theme: 'default', mode: 'static' }); await zenuml.render(text, { theme: 'default', mode: 'static' });

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "11.9.0", "version": "11.8.1",
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.", "description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
"type": "module", "type": "module",
"module": "./dist/mermaid.core.mjs", "module": "./dist/mermaid.core.mjs",
@@ -79,7 +79,7 @@
"dagre-d3-es": "7.0.11", "dagre-d3-es": "7.0.11",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dompurify": "^3.2.5", "dompurify": "^3.2.5",
"katex": "^0.16.22", "katex": "^0.16.9",
"khroma": "^2.1.0", "khroma": "^2.1.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"marked": "^16.0.0", "marked": "^16.0.0",

View File

@@ -78,41 +78,5 @@ describe('diagram-orchestration', () => {
flowchart: 1 "pie" pie: 2 "pie"`) flowchart: 1 "pie" pie: 2 "pie"`)
).toBe('pie'); ).toBe('pie');
}); });
it('should detect proper diagram when defaultRenderer is elk for flowchart', () => {
expect(
detectType('mindmap\n root\n Photograph\n Waterfall', {
flowchart: { defaultRenderer: 'elk' },
})
).toBe('mindmap');
expect(
detectType(
`
classDiagram
class Person {
+String name
-Int id
#double age
+Text demographicProfile
}
`,
{ flowchart: { defaultRenderer: 'elk' } }
)
).toBe('class');
expect(
detectType(
`
erDiagram
p[Photograph] {
varchar(12) jobId
date dateCreated
}
`,
{
flowchart: { defaultRenderer: 'elk' },
}
)
).toBe('er');
});
}); });
}); });

View File

@@ -1,12 +1,21 @@
import { it, describe, expect } from 'vitest'; import { it, describe, expect } from 'vitest';
import { db } from './architectureDb.js';
import { parser } from './architectureParser.js'; import { parser } from './architectureParser.js';
import { ArchitectureDB } from './architectureDb.js';
const {
clear,
getDiagramTitle,
getAccTitle,
getAccDescription,
getServices,
getGroups,
getEdges,
getJunctions,
} = db;
describe('architecture diagrams', () => { describe('architecture diagrams', () => {
let db: ArchitectureDB;
beforeEach(() => { beforeEach(() => {
db = new ArchitectureDB(); clear();
// @ts-expect-error since type is set to undefined we will have error
parser.parser?.yy = db;
}); });
describe('architecture diagram definitions', () => { describe('architecture diagram definitions', () => {
@@ -27,7 +36,7 @@ describe('architecture diagrams', () => {
it('should handle title on the first line', async () => { it('should handle title on the first line', async () => {
const str = `architecture-beta title Simple Architecture Diagram`; const str = `architecture-beta title Simple Architecture Diagram`;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getDiagramTitle()).toBe('Simple Architecture Diagram'); expect(getDiagramTitle()).toBe('Simple Architecture Diagram');
}); });
it('should handle title on another line', async () => { it('should handle title on another line', async () => {
@@ -35,7 +44,7 @@ describe('architecture diagrams', () => {
title Simple Architecture Diagram title Simple Architecture Diagram
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getDiagramTitle()).toBe('Simple Architecture Diagram'); expect(getDiagramTitle()).toBe('Simple Architecture Diagram');
}); });
it('should handle accessibility title and description', async () => { it('should handle accessibility title and description', async () => {
@@ -44,8 +53,8 @@ describe('architecture diagrams', () => {
accDescr: Accessibility Description accDescr: Accessibility Description
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getAccTitle()).toBe('Accessibility Title'); expect(getAccTitle()).toBe('Accessibility Title');
expect(db.getAccDescription()).toBe('Accessibility Description'); expect(getAccDescription()).toBe('Accessibility Description');
}); });
it('should handle multiline accessibility description', async () => { it('should handle multiline accessibility description', async () => {
@@ -55,7 +64,7 @@ describe('architecture diagrams', () => {
} }
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getAccDescription()).toBe('Accessibility Description'); expect(getAccDescription()).toBe('Accessibility Description');
}); });
}); });
}); });

View File

@@ -1,9 +1,8 @@
import { getConfig as commonGetConfig } from '../../config.js';
import type { ArchitectureDiagramConfig } from '../../config.type.js'; import type { ArchitectureDiagramConfig } from '../../config.type.js';
import DEFAULT_CONFIG from '../../defaultConfig.js'; import DEFAULT_CONFIG from '../../defaultConfig.js';
import type { DiagramDB } from '../../diagram-api/types.js'; import { getConfig as commonGetConfig } from '../../config.js';
import type { D3Element } from '../../types.js'; import type { D3Element } from '../../types.js';
import { cleanAndMerge } from '../../utils.js'; import { ImperativeState } from '../../utils/imperativeState.js';
import { import {
clear as commonClear, clear as commonClear,
getAccDescription, getAccDescription,
@@ -15,6 +14,7 @@ import {
} from '../common/commonDb.js'; } from '../common/commonDb.js';
import type { import type {
ArchitectureAlignment, ArchitectureAlignment,
ArchitectureDB,
ArchitectureDirectionPair, ArchitectureDirectionPair,
ArchitectureDirectionPairMap, ArchitectureDirectionPairMap,
ArchitectureEdge, ArchitectureEdge,
@@ -33,333 +33,330 @@ import {
isArchitectureService, isArchitectureService,
shiftPositionByArchitectureDirectionPair, shiftPositionByArchitectureDirectionPair,
} from './architectureTypes.js'; } from './architectureTypes.js';
import { cleanAndMerge } from '../../utils.js';
const DEFAULT_ARCHITECTURE_CONFIG: Required<ArchitectureDiagramConfig> = const DEFAULT_ARCHITECTURE_CONFIG: Required<ArchitectureDiagramConfig> =
DEFAULT_CONFIG.architecture; DEFAULT_CONFIG.architecture;
export class ArchitectureDB implements DiagramDB {
private nodes: Record<string, ArchitectureNode> = {};
private groups: Record<string, ArchitectureGroup> = {};
private edges: ArchitectureEdge[] = [];
private registeredIds: Record<string, 'node' | 'group'> = {};
private dataStructures?: ArchitectureState['dataStructures'];
private elements: Record<string, D3Element> = {};
constructor() { const state = new ImperativeState<ArchitectureState>(() => ({
this.clear(); 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`);
}
} }
public clear(): void { state.records.registeredIds[id] = 'node';
this.nodes = {};
this.groups = {}; state.records.nodes[id] = {
this.edges = []; id,
this.registeredIds = {}; type: 'service',
this.dataStructures = undefined; icon,
this.elements = {}; iconText,
commonClear(); 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`);
}
} }
public addService({ state.records.registeredIds[id] = 'group';
state.records.groups[id] = {
id, id,
icon, icon,
in: parent,
title, title,
iconText, in: parent,
}: Omit<ArchitectureService, 'edges'>): void { };
if (this.registeredIds[id] !== undefined) { };
throw new Error( const getGroups = (): ArchitectureGroup[] => {
`The service id [${id}] is already in use by another ${this.registeredIds[id]}` return Object.values(state.records.groups);
); };
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The service [${id}] cannot be placed within itself`);
}
if (this.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 (this.registeredIds[parent] === 'node') {
throw new Error(`The service [${id}]'s parent is not a group`);
}
}
this.registeredIds[id] = 'node'; const addEdge = function ({
lhsId,
this.nodes[id] = { rhsId,
id, lhsDir,
type: 'service', rhsDir,
icon, lhsInto,
iconText, rhsInto,
title, lhsGroup,
edges: [], rhsGroup,
in: parent, 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}`
);
} }
public getServices(): ArchitectureService[] { if (state.records.nodes[lhsId] === undefined && state.records.groups[lhsId] === undefined) {
return Object.values(this.nodes).filter(isArchitectureService); 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.`
);
} }
public addJunction({ id, in: parent }: Omit<ArchitectureJunction, 'edges'>): void { const lhsGroupId = state.records.nodes[lhsId].in;
this.registeredIds[id] = 'node'; const rhsGroupId = state.records.nodes[rhsId].in;
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
this.nodes[id] = { throw new Error(
id, `The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
type: 'junction', );
edges: [], }
in: parent, 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.`
);
} }
public getJunctions(): ArchitectureJunction[] { const edge = {
return Object.values(this.nodes).filter(isArchitectureJunction);
}
public getNodes(): ArchitectureNode[] {
return Object.values(this.nodes);
}
public getNode(id: string): ArchitectureNode | null {
return this.nodes[id] ?? null;
}
public addGroup({ id, icon, in: parent, title }: ArchitectureGroup): void {
if (this.registeredIds?.[id] !== undefined) {
throw new Error(
`The group id [${id}] is already in use by another ${this.registeredIds[id]}`
);
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The group [${id}] cannot be placed within itself`);
}
if (this.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 (this.registeredIds?.[parent] === 'node') {
throw new Error(`The group [${id}]'s parent is not a group`);
}
}
this.registeredIds[id] = 'group';
this.groups[id] = {
id,
icon,
title,
in: parent,
};
}
public getGroups(): ArchitectureGroup[] {
return Object.values(this.groups);
}
public addEdge({
lhsId, lhsId,
rhsId,
lhsDir, lhsDir,
rhsDir,
lhsInto, lhsInto,
rhsInto,
lhsGroup, lhsGroup,
rhsId,
rhsDir,
rhsInto,
rhsGroup, rhsGroup,
title, title,
}: ArchitectureEdge): void { };
if (!isArchitectureDirection(lhsDir)) {
throw new Error(
`Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${String(lhsDir)}`
);
}
if (!isArchitectureDirection(rhsDir)) {
throw new Error(
`Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${String(rhsDir)}`
);
}
if (this.nodes[lhsId] === undefined && this.groups[lhsId] === undefined) { state.records.edges.push(edge);
throw new Error( if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) {
`The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.` 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]);
}
if (this.nodes[rhsId] === undefined && this.groups[rhsId] === 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 = this.nodes[lhsId].in;
const rhsGroupId = this.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,
};
this.edges.push(edge);
if (this.nodes[lhsId] && this.nodes[rhsId]) {
this.nodes[lhsId].edges.push(this.edges[this.edges.length - 1]);
this.nodes[rhsId].edges.push(this.edges[this.edges.length - 1]);
}
} }
};
public getEdges(): ArchitectureEdge[] { const getEdges = (): ArchitectureEdge[] => state.records.edges;
return this.edges;
}
/** /**
* Returns the current diagram's adjacency list, spatial map, & group alignments. * Returns the current diagram's adjacency list, spatial map, & group alignments.
* If they have not been created, run the algorithms to generate them. * If they have not been created, run the algorithms to generate them.
* @returns * @returns
*/ */
public getDataStructures() { const getDataStructures = () => {
if (this.dataStructures === undefined) { if (state.records.dataStructures === undefined) {
// Tracks how groups are aligned with one another. Generated while creating the adj list // Tracks how groups are aligned with one another. Generated while creating the adj list
const groupAlignments: Record< const groupAlignments: Record<
string, string,
Record<string, Exclude<ArchitectureAlignment, 'bend'>> Record<string, Exclude<ArchitectureAlignment, 'bend'>>
> = {}; > = {};
// Create an adjacency list of the diagram to perform BFS on // Create an adjacency list of the diagram to perform BFS on
// Outer reduce applied on all services // Outer reduce applied on all services
// Inner reduce applied on the edges for a service // Inner reduce applied on the edges for a service
const adjList = Object.entries(this.nodes).reduce< const adjList = Object.entries(state.records.nodes).reduce<
Record<string, ArchitectureDirectionPairMap> Record<string, ArchitectureDirectionPairMap>
>((prevOuter, [id, service]) => { >((prevOuter, [id, service]) => {
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => { prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
// track the direction groups connect to one another // track the direction groups connect to one another
const lhsGroupId = this.getNode(edge.lhsId)?.in; const lhsGroupId = getNode(edge.lhsId)?.in;
const rhsGroupId = this.getNode(edge.rhsId)?.in; const rhsGroupId = getNode(edge.rhsId)?.in;
if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) { if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) {
const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir); const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir);
if (alignment !== 'bend') { if (alignment !== 'bend') {
groupAlignments[lhsGroupId] ??= {}; groupAlignments[lhsGroupId] ??= {};
groupAlignments[lhsGroupId][rhsGroupId] = alignment; groupAlignments[lhsGroupId][rhsGroupId] = alignment;
groupAlignments[rhsGroupId] ??= {}; groupAlignments[rhsGroupId] ??= {};
groupAlignments[rhsGroupId][lhsGroupId] = alignment; groupAlignments[rhsGroupId][lhsGroupId] = alignment;
}
}
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 };
// If a key is present in this object, it has not been visited
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 if (edge.lhsId === id) {
while (Object.keys(notVisited).length > 0) { // source is LHS
spatialMaps.push(BFS(Object.keys(notVisited)[0])); 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 };
// If a key is present in this object, it has not been visited
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);
}
});
}
} }
this.dataStructures = { return spatialMap;
adjList, };
spatialMaps, const spatialMaps = [BFS(firstId)];
groupAlignments,
}; // 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]));
} }
return this.dataStructures; state.records.dataStructures = {
adjList,
spatialMaps,
groupAlignments,
};
} }
return state.records.dataStructures;
};
public setElementForId(id: string, element: D3Element): void { const setElementForId = (id: string, element: D3Element) => {
this.elements[id] = element; state.records.elements[id] = element;
} };
const getElementById = (id: string) => state.records.elements[id];
public getElementById(id: string): D3Element { const getConfig = (): Required<ArchitectureDiagramConfig> => {
return this.elements[id]; const config = cleanAndMerge({
} ...DEFAULT_ARCHITECTURE_CONFIG,
...commonGetConfig().architecture,
});
return config;
};
public getConfig(): Required<ArchitectureDiagramConfig> { export const db: ArchitectureDB = {
return cleanAndMerge({ clear,
...DEFAULT_ARCHITECTURE_CONFIG, setDiagramTitle,
...commonGetConfig().architecture, getDiagramTitle,
}); setAccTitle,
} getAccTitle,
setAccDescription,
getAccDescription,
getConfig,
public getConfigField<T extends keyof ArchitectureDiagramConfig>( addService,
field: T getServices,
): Required<ArchitectureDiagramConfig>[T] { addJunction,
return this.getConfig()[field]; getJunctions,
} getNodes,
getNode,
public setAccTitle = setAccTitle; addGroup,
public getAccTitle = getAccTitle; getGroups,
public setDiagramTitle = setDiagramTitle; addEdge,
public getDiagramTitle = getDiagramTitle; getEdges,
public getAccDescription = getAccDescription; setElementForId,
public setAccDescription = setAccDescription; getElementById,
} getDataStructures,
};
/** /**
* Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined * Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined
* @param field - the config field to access * @param field - the config field to access
* @returns * @returns
*/ */
// export function getConfigField<T extends keyof ArchitectureDiagramConfig>( export function getConfigField<T extends keyof ArchitectureDiagramConfig>(
// field: T field: T
// ): Required<ArchitectureDiagramConfig>[T] { ): Required<ArchitectureDiagramConfig>[T] {
// return db.getConfig()[field]; return getConfig()[field];
// } }

View File

@@ -1,14 +1,12 @@
import type { DiagramDefinition } from '../../diagram-api/types.js'; import type { DiagramDefinition } from '../../diagram-api/types.js';
import { parser } from './architectureParser.js'; import { parser } from './architectureParser.js';
import { ArchitectureDB } from './architectureDb.js'; import { db } from './architectureDb.js';
import styles from './architectureStyles.js'; import styles from './architectureStyles.js';
import { renderer } from './architectureRenderer.js'; import { renderer } from './architectureRenderer.js';
export const diagram: DiagramDefinition = { export const diagram: DiagramDefinition = {
parser, parser,
get db() { db,
return new ArchitectureDB();
},
renderer, renderer,
styles, styles,
}; };

View File

@@ -1,33 +1,24 @@
import type { Architecture } from '@mermaid-js/parser'; import type { Architecture } from '@mermaid-js/parser';
import { parse } from '@mermaid-js/parser'; import { parse } from '@mermaid-js/parser';
import type { ParserDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import type { ParserDefinition } from '../../diagram-api/types.js';
import { populateCommonDb } from '../common/populateCommonDb.js'; import { populateCommonDb } from '../common/populateCommonDb.js';
import { ArchitectureDB } from './architectureDb.js'; import type { ArchitectureDB } from './architectureTypes.js';
import { db } from './architectureDb.js';
const populateDb = (ast: Architecture, db: ArchitectureDB) => { const populateDb = (ast: Architecture, db: ArchitectureDB) => {
populateCommonDb(ast, db); populateCommonDb(ast, db);
ast.groups.map((group) => db.addGroup(group)); ast.groups.map(db.addGroup);
ast.services.map((service) => db.addService({ ...service, type: 'service' })); ast.services.map((service) => db.addService({ ...service, type: 'service' }));
ast.junctions.map((service) => db.addJunction({ ...service, type: 'junction' })); 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? // @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((edge) => db.addEdge(edge)); ast.edges.map(db.addEdge);
}; };
export const parser: ParserDefinition = { export const parser: ParserDefinition = {
parser: {
// @ts-expect-error - ArchitectureDB is not assignable to DiagramDB
yy: undefined,
},
parse: async (input: string): Promise<void> => { parse: async (input: string): Promise<void> => {
const ast: Architecture = await parse('architecture', input); const ast: Architecture = await parse('architecture', input);
log.debug(ast); log.debug(ast);
const db = parser.parser?.yy;
if (!(db instanceof ArchitectureDB)) {
throw new Error(
'parser.parser?.yy was not a ArchitectureDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.'
);
}
populateDb(ast, db); populateDb(ast, db);
}, },
}; };

View File

@@ -1,3 +1,4 @@
import { registerIconPacks } from '../../rendering-util/icons.js';
import type { Position } from 'cytoscape'; import type { Position } from 'cytoscape';
import cytoscape from 'cytoscape'; import cytoscape from 'cytoscape';
import type { FcoseLayoutOptions } from 'cytoscape-fcose'; import type { FcoseLayoutOptions } from 'cytoscape-fcose';
@@ -6,10 +7,9 @@ import { select } from 'd3';
import type { DrawDefinition, SVG } from '../../diagram-api/types.js'; import type { DrawDefinition, SVG } from '../../diagram-api/types.js';
import type { Diagram } from '../../Diagram.js'; import type { Diagram } from '../../Diagram.js';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import { registerIconPacks } from '../../rendering-util/icons.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js'; import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import type { ArchitectureDB } from './architectureDb.js'; import { getConfigField } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js'; import { architectureIcons } from './architectureIcons.js';
import type { import type {
ArchitectureAlignment, ArchitectureAlignment,
@@ -22,6 +22,7 @@ import type {
NodeSingularData, NodeSingularData,
} from './architectureTypes.js'; } from './architectureTypes.js';
import { import {
type ArchitectureDB,
type ArchitectureDirection, type ArchitectureDirection,
type ArchitectureEdge, type ArchitectureEdge,
type ArchitectureGroup, type ArchitectureGroup,
@@ -43,7 +44,7 @@ registerIconPacks([
]); ]);
cytoscape.use(fcose); cytoscape.use(fcose);
function addServices(services: ArchitectureService[], cy: cytoscape.Core, db: ArchitectureDB) { function addServices(services: ArchitectureService[], cy: cytoscape.Core) {
services.forEach((service) => { services.forEach((service) => {
cy.add({ cy.add({
group: 'nodes', group: 'nodes',
@@ -53,15 +54,15 @@ function addServices(services: ArchitectureService[], cy: cytoscape.Core, db: Ar
icon: service.icon, icon: service.icon,
label: service.title, label: service.title,
parent: service.in, parent: service.in,
width: db.getConfigField('iconSize'), width: getConfigField('iconSize'),
height: db.getConfigField('iconSize'), height: getConfigField('iconSize'),
} as NodeSingularData, } as NodeSingularData,
classes: 'node-service', classes: 'node-service',
}); });
}); });
} }
function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core, db: ArchitectureDB) { function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core) {
junctions.forEach((junction) => { junctions.forEach((junction) => {
cy.add({ cy.add({
group: 'nodes', group: 'nodes',
@@ -69,8 +70,8 @@ function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core, db:
type: 'junction', type: 'junction',
id: junction.id, id: junction.id,
parent: junction.in, parent: junction.in,
width: db.getConfigField('iconSize'), width: getConfigField('iconSize'),
height: db.getConfigField('iconSize'), height: getConfigField('iconSize'),
} as NodeSingularData, } as NodeSingularData,
classes: 'node-junction', classes: 'node-junction',
}); });
@@ -256,8 +257,7 @@ function getAlignments(
} }
function getRelativeConstraints( function getRelativeConstraints(
spatialMaps: ArchitectureSpatialMap[], spatialMaps: ArchitectureSpatialMap[]
db: ArchitectureDB
): fcose.FcoseRelativePlacementConstraint[] { ): fcose.FcoseRelativePlacementConstraint[] {
const relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = []; const relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = [];
const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`; const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`;
@@ -296,7 +296,7 @@ function getRelativeConstraints(
[ArchitectureDirectionName[ [ArchitectureDirectionName[
getOppositeArchitectureDirection(dir as ArchitectureDirection) getOppositeArchitectureDirection(dir as ArchitectureDirection)
]]: currId, ]]: currId,
gap: 1.5 * db.getConfigField('iconSize'), gap: 1.5 * getConfigField('iconSize'),
}); });
} }
}); });
@@ -353,7 +353,7 @@ function layoutArchitecture(
style: { style: {
'text-valign': 'bottom', 'text-valign': 'bottom',
'text-halign': 'center', 'text-halign': 'center',
'font-size': `${db.getConfigField('fontSize')}px`, 'font-size': `${getConfigField('fontSize')}px`,
}, },
}, },
{ {
@@ -375,7 +375,7 @@ function layoutArchitecture(
selector: '.node-group', selector: '.node-group',
style: { style: {
// @ts-ignore Incorrect library types // @ts-ignore Incorrect library types
padding: `${db.getConfigField('padding')}px`, padding: `${getConfigField('padding')}px`,
}, },
}, },
], ],
@@ -393,14 +393,14 @@ function layoutArchitecture(
renderEl.remove(); renderEl.remove();
addGroups(groups, cy); addGroups(groups, cy);
addServices(services, cy, db); addServices(services, cy);
addJunctions(junctions, cy, db); addJunctions(junctions, cy);
addEdges(edges, cy); addEdges(edges, cy);
// Use the spatial map to create alignment arrays for fcose // Use the spatial map to create alignment arrays for fcose
const alignmentConstraint = getAlignments(db, spatialMaps, groupAlignments); const alignmentConstraint = getAlignments(db, spatialMaps, groupAlignments);
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it // Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
const relativePlacementConstraint = getRelativeConstraints(spatialMaps, db); const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
const layout = cy.layout({ const layout = cy.layout({
name: 'fcose', name: 'fcose',
@@ -415,9 +415,7 @@ function layoutArchitecture(
const { parent: parentA } = nodeData(nodeA); const { parent: parentA } = nodeData(nodeA);
const { parent: parentB } = nodeData(nodeB); const { parent: parentB } = nodeData(nodeB);
const elasticity = const elasticity =
parentA === parentB parentA === parentB ? 1.5 * getConfigField('iconSize') : 0.5 * getConfigField('iconSize');
? 1.5 * db.getConfigField('iconSize')
: 0.5 * db.getConfigField('iconSize');
return elasticity; return elasticity;
}, },
edgeElasticity(edge: EdgeSingular) { edgeElasticity(edge: EdgeSingular) {
@@ -537,11 +535,11 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
const cy = await layoutArchitecture(services, junctions, groups, edges, db, ds); const cy = await layoutArchitecture(services, junctions, groups, edges, db, ds);
await drawEdges(edgesElem, cy, db); await drawEdges(edgesElem, cy);
await drawGroups(groupElem, cy, db); await drawGroups(groupElem, cy);
positionNodes(db, cy); positionNodes(db, cy);
setupGraphViewbox(undefined, svg, db.getConfigField('padding'), db.getConfigField('useMaxWidth')); setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
}; };
export const renderer = { draw }; export const renderer = { draw };

View File

@@ -1,9 +1,9 @@
import { getIconSVG } from '../../rendering-util/icons.js';
import type cytoscape from 'cytoscape'; import type cytoscape from 'cytoscape';
import { getConfig } from '../../diagram-api/diagramAPI.js'; import { getConfig } from '../../diagram-api/diagramAPI.js';
import { createText } from '../../rendering-util/createText.js'; import { createText } from '../../rendering-util/createText.js';
import { getIconSVG } from '../../rendering-util/icons.js';
import type { D3Element } from '../../types.js'; import type { D3Element } from '../../types.js';
import type { ArchitectureDB } from './architectureDb.js'; import { db, getConfigField } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js'; import { architectureIcons } from './architectureIcons.js';
import { import {
ArchitectureDirectionArrow, ArchitectureDirectionArrow,
@@ -16,17 +16,14 @@ import {
isArchitectureDirectionY, isArchitectureDirectionY,
isArchitecturePairXY, isArchitecturePairXY,
nodeData, nodeData,
type ArchitectureDB,
type ArchitectureJunction, type ArchitectureJunction,
type ArchitectureService, type ArchitectureService,
} from './architectureTypes.js'; } from './architectureTypes.js';
export const drawEdges = async function ( export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) {
edgesEl: D3Element, const padding = getConfigField('padding');
cy: cytoscape.Core, const iconSize = getConfigField('iconSize');
db: ArchitectureDB
) {
const padding = db.getConfigField('padding');
const iconSize = db.getConfigField('iconSize');
const halfIconSize = iconSize / 2; const halfIconSize = iconSize / 2;
const arrowSize = iconSize / 6; const arrowSize = iconSize / 6;
const halfArrowSize = arrowSize / 2; const halfArrowSize = arrowSize / 2;
@@ -186,17 +183,13 @@ export const drawEdges = async function (
); );
}; };
export const drawGroups = async function ( export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) {
groupsEl: D3Element, const padding = getConfigField('padding');
cy: cytoscape.Core,
db: ArchitectureDB
) {
const padding = db.getConfigField('padding');
const groupIconSize = padding * 0.75; const groupIconSize = padding * 0.75;
const fontSize = db.getConfigField('fontSize'); const fontSize = getConfigField('fontSize');
const iconSize = db.getConfigField('iconSize'); const iconSize = getConfigField('iconSize');
const halfIconSize = iconSize / 2; const halfIconSize = iconSize / 2;
await Promise.all( await Promise.all(
@@ -273,7 +266,7 @@ export const drawServices = async function (
): Promise<number> { ): Promise<number> {
for (const service of services) { for (const service of services) {
const serviceElem = elem.append('g'); const serviceElem = elem.append('g');
const iconSize = db.getConfigField('iconSize'); const iconSize = getConfigField('iconSize');
if (service.title) { if (service.title) {
const textElem = serviceElem.append('g'); const textElem = serviceElem.append('g');
@@ -357,7 +350,7 @@ export const drawJunctions = function (
) { ) {
junctions.forEach((junction) => { junctions.forEach((junction) => {
const junctionElem = elem.append('g'); const junctionElem = elem.append('g');
const iconSize = db.getConfigField('iconSize'); const iconSize = getConfigField('iconSize');
const bkgElem = junctionElem.append('g'); const bkgElem = junctionElem.append('g');
bkgElem bkgElem

View File

@@ -92,20 +92,7 @@ export const setCssClass = function (itemIds: string, cssClassName: string) {
const populateBlockDatabase = (_blockList: Block[], parent: Block): void => { const populateBlockDatabase = (_blockList: Block[], parent: Block): void => {
const blockList = _blockList.flat(); const blockList = _blockList.flat();
const children = []; const children = [];
const columnSettingBlock = blockList.find((b) => b?.type === 'column-setting');
const column = columnSettingBlock?.columns ?? -1;
for (const block of blockList) { for (const block of blockList) {
if (
typeof column === 'number' &&
column > 0 &&
block.type !== 'column-setting' &&
typeof block.widthInColumns === 'number' &&
block.widthInColumns > column
) {
log.warn(
`Block ${block.id} width ${block.widthInColumns} exceeds configured column width ${column}`
);
}
if (block.label) { if (block.label) {
block.label = sanitizeText(block.label); block.label = sanitizeText(block.label);
} }
@@ -238,15 +225,13 @@ export function edgeTypeStr2Type(typeStr: string): string {
} }
export function edgeStrToEdgeData(typeStr: string): string { export function edgeStrToEdgeData(typeStr: string): string {
switch (typeStr.replace(/^[\s-]+|[\s-]+$/g, '')) { switch (typeStr.trim()) {
case 'x': case '--x':
return 'arrow_cross'; return 'arrow_cross';
case 'o': case '--o':
return 'arrow_circle'; return 'arrow_circle';
case '>':
return 'arrow_point';
default: default:
return ''; return 'arrow_point';
} }
} }

View File

@@ -3,7 +3,7 @@ import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-a
const id = 'block'; const id = 'block';
const detector: DiagramDetector = (txt) => { const detector: DiagramDetector = (txt) => {
return /^\s*block(-beta)?/.test(txt); return /^\s*block-beta/.test(txt);
}; };
const loader = async () => { const loader = async () => {

View File

@@ -270,12 +270,7 @@ function layoutBlocks(block: Block, db: BlockDB) {
if (child.children) { if (child.children) {
layoutBlocks(child, db); layoutBlocks(child, db);
} }
let columnsFilled = child?.widthInColumns ?? 1; columnPos += child?.widthInColumns ?? 1;
if (columns > 0) {
// Make sure overflowing lines do not affect later lines
columnsFilled = Math.min(columnsFilled, columns - (columnPos % columns));
}
columnPos += columnsFilled;
log.debug('abc88 columnsPos', child, columnPos); log.debug('abc88 columnsPos', child, columnPos);
} }
} }

View File

@@ -36,10 +36,10 @@ CRLF \u000D\u000A
%% %%
"block-beta" { yy.getLogger().debug('Found block-beta'); return 'BLOCK_DIAGRAM_KEY'; } "block-beta" { return 'BLOCK_DIAGRAM_KEY'; }
"block:" { yy.getLogger().debug('Found id-block'); return 'id-block'; } "block"\s+ { yy.getLogger().debug('Found space-block'); return 'block';}
"block" { yy.getLogger().debug('Found block'); return 'BLOCK_DIAGRAM_KEY'; } "block"\n+ { yy.getLogger().debug('Found nl-block'); return 'block';}
"block:" { yy.getLogger().debug('Found space-block'); return 'id-block';}
// \s*\%\%.* { yy.getLogger().debug('Found comment',yytext); } // \s*\%\%.* { yy.getLogger().debug('Found comment',yytext); }
[\s]+ { yy.getLogger().debug('.', yytext); /* skip all whitespace */ } [\s]+ { yy.getLogger().debug('.', yytext); /* skip all whitespace */ }
[\n]+ {yy.getLogger().debug('_', yytext); /* skip all whitespace */ } [\n]+ {yy.getLogger().debug('_', yytext); /* skip all whitespace */ }
@@ -240,7 +240,7 @@ columnsStatement
blockStatement blockStatement
: id-block nodeStatement document end { yy.getLogger().debug('Rule: id-block statement : ', $2, $3); const id2 = yy.generateId(); $$ = { ...$2, type:'composite', children: $3 }; } : id-block nodeStatement document end { yy.getLogger().debug('Rule: id-block statement : ', $2, $3); const id2 = yy.generateId(); $$ = { ...$2, type:'composite', children: $3 }; }
| BLOCK_DIAGRAM_KEY document end { yy.getLogger().debug('Rule: blockStatement : ', $1, $2, $3); const id = yy.generateId(); $$ = { id, type:'composite', label:'', children: $2 }; } | block document end { yy.getLogger().debug('Rule: blockStatement : ', $1, $2, $3); const id = yy.generateId(); $$ = { id, type:'composite', label:'', children: $2 }; }
; ;
node node

View File

@@ -1,7 +1,6 @@
// @ts-ignore: jison doesn't export types // @ts-ignore: jison doesn't export types
import block from './block.jison'; import block from './block.jison';
import db from '../blockDB.js'; import db from '../blockDB.js';
import { log } from '../../../logger.js';
describe('Block diagram', function () { describe('Block diagram', function () {
describe('when parsing a block diagram graph it should handle > ', function () { describe('when parsing a block diagram graph it should handle > ', function () {
@@ -23,7 +22,7 @@ describe('Block diagram', function () {
expect(blocks[0].label).toBe('id'); expect(blocks[0].label).toBe('id');
}); });
it('a node with a square shape and a label', () => { it('a node with a square shape and a label', () => {
const str = `block const str = `block-beta
id["A label"] id["A label"]
`; `;
@@ -35,7 +34,7 @@ describe('Block diagram', function () {
expect(blocks[0].type).toBe('square'); expect(blocks[0].type).toBe('square');
}); });
it('a diagram with multiple nodes', () => { it('a diagram with multiple nodes', () => {
const str = `block const str = `block-beta
id1 id1
id2 id2
`; `;
@@ -51,7 +50,7 @@ describe('Block diagram', function () {
expect(blocks[1].type).toBe('na'); expect(blocks[1].type).toBe('na');
}); });
it('a diagram with multiple nodes', () => { it('a diagram with multiple nodes', () => {
const str = `block const str = `block-beta
id1 id1
id2 id2
id3 id3
@@ -72,7 +71,7 @@ describe('Block diagram', function () {
}); });
it('a node with a square shape and a label', () => { it('a node with a square shape and a label', () => {
const str = `block const str = `block-beta
id["A label"] id["A label"]
id2`; id2`;
@@ -87,7 +86,7 @@ describe('Block diagram', function () {
expect(blocks[1].type).toBe('na'); expect(blocks[1].type).toBe('na');
}); });
it('a diagram with multiple nodes with edges abc123', () => { it('a diagram with multiple nodes with edges abc123', () => {
const str = `block const str = `block-beta
id1["first"] --> id2["second"] id1["first"] --> id2["second"]
`; `;
@@ -101,7 +100,7 @@ describe('Block diagram', function () {
expect(edges[0].arrowTypeEnd).toBe('arrow_point'); expect(edges[0].arrowTypeEnd).toBe('arrow_point');
}); });
it('a diagram with multiple nodes with edges abc123', () => { it('a diagram with multiple nodes with edges abc123', () => {
const str = `block const str = `block-beta
id1["first"] -- "a label" --> id2["second"] id1["first"] -- "a label" --> id2["second"]
`; `;
@@ -116,7 +115,7 @@ describe('Block diagram', function () {
expect(edges[0].label).toBe('a label'); expect(edges[0].label).toBe('a label');
}); });
it('a diagram with column statements', () => { it('a diagram with column statements', () => {
const str = `block const str = `block-beta
columns 2 columns 2
block1["Block 1"] block1["Block 1"]
`; `;
@@ -127,7 +126,7 @@ describe('Block diagram', function () {
expect(blocks.length).toBe(1); expect(blocks.length).toBe(1);
}); });
it('a diagram without column statements', () => { it('a diagram without column statements', () => {
const str = `block const str = `block-beta
block1["Block 1"] block1["Block 1"]
`; `;
@@ -137,7 +136,7 @@ describe('Block diagram', function () {
expect(blocks.length).toBe(1); expect(blocks.length).toBe(1);
}); });
it('a diagram with auto column statements', () => { it('a diagram with auto column statements', () => {
const str = `block const str = `block-beta
columns auto columns auto
block1["Block 1"] block1["Block 1"]
`; `;
@@ -149,7 +148,7 @@ describe('Block diagram', function () {
}); });
it('blocks next to each other', () => { it('blocks next to each other', () => {
const str = `block const str = `block-beta
columns 2 columns 2
block1["Block 1"] block1["Block 1"]
block2["Block 2"] block2["Block 2"]
@@ -163,7 +162,7 @@ describe('Block diagram', function () {
}); });
it('blocks on top of each other', () => { it('blocks on top of each other', () => {
const str = `block const str = `block-beta
columns 1 columns 1
block1["Block 1"] block1["Block 1"]
block2["Block 2"] block2["Block 2"]
@@ -177,7 +176,7 @@ describe('Block diagram', function () {
}); });
it('compound blocks 2', () => { it('compound blocks 2', () => {
const str = `block const str = `block-beta
block block
aBlock["ABlock"] aBlock["ABlock"]
bBlock["BBlock"] bBlock["BBlock"]
@@ -205,7 +204,7 @@ describe('Block diagram', function () {
expect(bBlock.type).toBe('square'); expect(bBlock.type).toBe('square');
}); });
it('compound blocks of compound blocks', () => { it('compound blocks of compound blocks', () => {
const str = `block const str = `block-beta
block block
aBlock["ABlock"] aBlock["ABlock"]
block block
@@ -240,7 +239,7 @@ describe('Block diagram', function () {
expect(bBlock.type).toBe('square'); expect(bBlock.type).toBe('square');
}); });
it('compound blocks with title', () => { it('compound blocks with title', () => {
const str = `block const str = `block-beta
block:compoundBlock["Compound block"] block:compoundBlock["Compound block"]
columns 1 columns 1
block2["Block 2"] block2["Block 2"]
@@ -265,7 +264,7 @@ describe('Block diagram', function () {
expect(block2.type).toBe('square'); expect(block2.type).toBe('square');
}); });
it('blocks mixed with compound blocks', () => { it('blocks mixed with compound blocks', () => {
const str = `block const str = `block-beta
columns 1 columns 1
block1["Block 1"] block1["Block 1"]
@@ -292,7 +291,7 @@ describe('Block diagram', function () {
}); });
it('Arrow blocks', () => { it('Arrow blocks', () => {
const str = `block const str = `block-beta
columns 3 columns 3
block1["Block 1"] block1["Block 1"]
blockArrow<["&nbsp;&nbsp;&nbsp;"]>(right) blockArrow<["&nbsp;&nbsp;&nbsp;"]>(right)
@@ -316,7 +315,7 @@ describe('Block diagram', function () {
expect(blockArrow.directions).toContain('right'); expect(blockArrow.directions).toContain('right');
}); });
it('Arrow blocks with multiple points', () => { it('Arrow blocks with multiple points', () => {
const str = `block const str = `block-beta
columns 1 columns 1
A A
blockArrow<["&nbsp;&nbsp;&nbsp;"]>(up, down) blockArrow<["&nbsp;&nbsp;&nbsp;"]>(up, down)
@@ -339,7 +338,7 @@ describe('Block diagram', function () {
expect(blockArrow.directions).not.toContain('right'); expect(blockArrow.directions).not.toContain('right');
}); });
it('blocks with different widths', () => { it('blocks with different widths', () => {
const str = `block const str = `block-beta
columns 3 columns 3
one["One Slot"] one["One Slot"]
two["Two slots"]:2 two["Two slots"]:2
@@ -354,7 +353,7 @@ describe('Block diagram', function () {
expect(two.widthInColumns).toBe(2); expect(two.widthInColumns).toBe(2);
}); });
it('empty blocks', () => { it('empty blocks', () => {
const str = `block const str = `block-beta
columns 3 columns 3
space space
middle["In the middle"] middle["In the middle"]
@@ -373,7 +372,7 @@ describe('Block diagram', function () {
expect(middle.label).toBe('In the middle'); expect(middle.label).toBe('In the middle');
}); });
it('classDef statements applied to a block', () => { it('classDef statements applied to a block', () => {
const str = `block const str = `block-beta
classDef black color:#ffffff, fill:#000000; classDef black color:#ffffff, fill:#000000;
mc["Memcache"] mc["Memcache"]
@@ -391,7 +390,7 @@ describe('Block diagram', function () {
expect(black.styles[0]).toEqual('color:#ffffff'); expect(black.styles[0]).toEqual('color:#ffffff');
}); });
it('style statements applied to a block', () => { it('style statements applied to a block', () => {
const str = `block const str = `block-beta
columns 1 columns 1
B["A wide one in the middle"] B["A wide one in the middle"]
style B fill:#f9F,stroke:#333,stroke-width:4px style B fill:#f9F,stroke:#333,stroke-width:4px
@@ -403,32 +402,13 @@ columns 1
const B = blocks[0]; const B = blocks[0];
expect(B.styles).toContain('fill:#f9F'); expect(B.styles).toContain('fill:#f9F');
}); });
it('should log a warning when block width exceeds column width', () => {
const str = `block-beta
columns 1
A:1
B:2
C:3
D:4
E:3
F:2
G:1`;
const logWarnSpy = vi.spyOn(log, 'warn').mockImplementation(() => undefined);
block.parse(str);
expect(logWarnSpy).toHaveBeenCalledWith('Block B width 2 exceeds configured column width 1');
logWarnSpy.mockRestore();
});
}); });
describe('prototype properties', function () { describe('prototype properties', function () {
function validateProperty(prop: string) { function validateProperty(prop: string) {
expect(() => block.parse(`block\n${prop}`)).not.toThrow(); expect(() => block.parse(`block-beta\n${prop}`)).not.toThrow();
expect(() => expect(() =>
block.parse(`block\nA; classDef ${prop} color:#ffffff,fill:#000000; class A ${prop}`) block.parse(`block-beta\nA; classDef ${prop} color:#ffffff,fill:#000000; class A ${prop}`)
).not.toThrow(); ).not.toThrow();
} }

View File

@@ -15,12 +15,4 @@ describe('class diagram', function () {
expect(() => parser.parse(`classDiagram\nnamespace ${prop} {\n\tclass A\n}`)).not.toThrow(); expect(() => parser.parse(`classDiagram\nnamespace ${prop} {\n\tclass A\n}`)).not.toThrow();
}); });
}); });
describe('backtick escaping', function () {
it('should handle backtick-quoted namespace names', function () {
expect(() =>
parser.parse(`classDiagram\nnamespace \`A::B\` {\n\tclass \`IPC::Sender\`\n}`)
).not.toThrow();
});
});
}); });

View File

@@ -242,7 +242,6 @@ classLabel
namespaceName namespaceName
: alphaNumToken { $$=$1; } : alphaNumToken { $$=$1; }
| classLiteralName { $$=$1; }
| alphaNumToken DOT namespaceName { $$=$1+'.'+$3; } | alphaNumToken DOT namespaceName { $$=$1+'.'+$3; }
| alphaNumToken namespaceName { $$=$1+$2; } | alphaNumToken namespaceName { $$=$1+$2; }
; ;

View File

@@ -33,13 +33,13 @@ function setupDompurifyHooks() {
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target'; const TEMPORARY_ATTRIBUTE = 'data-temp-href-target';
DOMPurify.addHook('beforeSanitizeAttributes', (node) => { DOMPurify.addHook('beforeSanitizeAttributes', (node) => {
if (node.tagName === 'A' && node.hasAttribute('target')) { if (node instanceof Element && node.tagName === 'A' && node.hasAttribute('target')) {
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? ''); node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? '');
} }
}); });
DOMPurify.addHook('afterSanitizeAttributes', (node) => { DOMPurify.addHook('afterSanitizeAttributes', (node) => {
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) { if (node instanceof Element && node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? ''); node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? '');
node.removeAttribute(TEMPORARY_ATTRIBUTE); node.removeAttribute(TEMPORARY_ATTRIBUTE);
if (node.getAttribute('target') === '_blank') { if (node.getAttribute('target') === '_blank') {

View File

@@ -11,7 +11,7 @@ const detector: DiagramDetector = (txt, config = {}): boolean => {
// If diagram explicitly states flowchart-elk // If diagram explicitly states flowchart-elk
/^\s*flowchart-elk/.test(txt) || /^\s*flowchart-elk/.test(txt) ||
// If a flowchart/graph diagram has their default renderer set to elk // If a flowchart/graph diagram has their default renderer set to elk
(/^\s*(flowchart|graph)/.test(txt) && config?.flowchart?.defaultRenderer === 'elk') (/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
) { ) {
config.layout = 'elk'; config.layout = 'elk';
return true; return true;

View File

@@ -125,60 +125,4 @@ describe('flow db getData', () => {
const { edges } = flowDb.getData(); const { edges } = flowDb.getData();
expect(edges[0].curve).toBe('basis'); expect(edges[0].curve).toBe('basis');
}); });
it('should support modifying interpolate using edge id syntax', () => {
flowDb.addVertex('A', { text: 'A', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addVertex('B', { text: 'B', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addVertex('C', { text: 'C', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addVertex('D', { text: 'D', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addLink(['A'], ['B'], {});
flowDb.addLink(['A'], ['C'], { id: 'e2' });
flowDb.addLink(['B'], ['D'], { id: 'e3' });
flowDb.addLink(['C'], ['D'], {});
flowDb.updateLinkInterpolate(['default'], 'stepBefore');
flowDb.updateLinkInterpolate([0], 'basis');
flowDb.addVertex(
'e2',
{ text: 'Shouldnt be used', type: 'text' },
undefined,
[],
[],
'',
{},
' curve: monotoneX '
);
flowDb.addVertex(
'e3',
{ text: 'Shouldnt be used', type: 'text' },
undefined,
[],
[],
'',
{},
' curve: catmullRom '
);
const { edges } = flowDb.getData();
expect(edges[0].curve).toBe('basis');
expect(edges[1].curve).toBe('monotoneX');
expect(edges[2].curve).toBe('catmullRom');
expect(edges[3].curve).toBe('stepBefore');
});
});
describe('flow db direction', () => {
let flowDb: FlowDB;
beforeEach(() => {
flowDb = new FlowDB();
});
it('should set direction to TB when TD is set', () => {
flowDb.setDirection('TD');
expect(flowDb.getDirection()).toBe('TB');
});
it('should correctly set direction irrespective of leading spaces', () => {
flowDb.setDirection(' TD');
expect(flowDb.getDirection()).toBe('TB');
});
}); });

View File

@@ -139,9 +139,6 @@ export class FlowDB implements DiagramDB {
if (edgeDoc?.animation !== undefined) { if (edgeDoc?.animation !== undefined) {
edge.animation = edgeDoc.animation; edge.animation = edgeDoc.animation;
} }
if (edgeDoc?.curve !== undefined) {
edge.interpolate = edgeDoc.curve;
}
return; return;
} }
@@ -406,8 +403,7 @@ You have to call mermaid.initialize.`
* *
*/ */
public setDirection(dir: string) { public setDirection(dir: string) {
this.direction = dir.trim(); this.direction = dir;
if (/.*</.exec(this.direction)) { if (/.*</.exec(this.direction)) {
this.direction = 'RL'; this.direction = 'RL';
} }

View File

@@ -37,59 +37,6 @@ describe('[Lines] when parsing', () => {
expect(edges[1].interpolate).toBe('cardinal'); expect(edges[1].interpolate).toBe('cardinal');
}); });
it('should handle edge curve properties using edge ID', function () {
const res = flow.parser.parse(
'graph TD\n' +
'A e1@-->B\n' +
'A uniqueName@-->C\n' +
'e1@{curve: basis}\n' +
'uniqueName@{curve: cardinal}'
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle edge curve properties using edge ID but without overriding default', function () {
const res = flow.parser.parse(
'graph TD\n' +
'A e1@-->B\n' +
'A-->C\n' +
'linkStyle default interpolate linear\n' +
'e1@{curve: stepAfter}'
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('stepAfter');
expect(edges.defaultInterpolate).toBe('linear');
});
it('should handle edge curve properties using edge ID mixed with line interpolation', function () {
const res = flow.parser.parse(
'graph TD\n' +
'A e1@-->B-->D\n' +
'A-->C e4@-->D-->E\n' +
'linkStyle default interpolate linear\n' +
'linkStyle 1 interpolate basis\n' +
'e1@{curve: monotoneX}\n' +
'e4@{curve: stepBefore}'
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('monotoneX');
expect(edges[1].interpolate).toBe('basis');
expect(edges.defaultInterpolate).toBe('linear');
expect(edges[3].interpolate).toBe('stepBefore');
expect(edges.defaultInterpolate).toBe('linear');
});
it('should handle line interpolation multi-numbered definitions', function () { it('should handle line interpolation multi-numbered definitions', function () {
const res = flow.parser.parse( const res = flow.parser.parse(
'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis' 'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis'

View File

@@ -167,10 +167,7 @@ export const getTasks = function () {
}; };
export const isInvalidDate = function (date, dateFormat, excludes, includes) { export const isInvalidDate = function (date, dateFormat, excludes, includes) {
const formattedDate = date.format(dateFormat.trim()); if (includes.includes(date.format(dateFormat.trim()))) {
const dateOnly = date.format('YYYY-MM-DD');
if (includes.includes(formattedDate) || includes.includes(dateOnly)) {
return false; return false;
} }
if ( if (
@@ -183,7 +180,7 @@ export const isInvalidDate = function (date, dateFormat, excludes, includes) {
if (excludes.includes(date.format('dddd').toLowerCase())) { if (excludes.includes(date.format('dddd').toLowerCase())) {
return true; return true;
} }
return excludes.includes(formattedDate) || excludes.includes(dateOnly); return excludes.includes(date.format(dateFormat.trim()));
}; };
export const setWeekday = function (txt) { export const setWeekday = function (txt) {

View File

@@ -581,11 +581,17 @@ export const draw = function (text, id, version, diagObj) {
rectangles rectangles
.append('rect') .append('rect')
.attr('id', (d) => 'exclude-' + d.start.format('YYYY-MM-DD')) .attr('id', function (d) {
.attr('x', (d) => timeScale(d.start.startOf('day')) + theSidePad) return 'exclude-' + d.start.format('YYYY-MM-DD');
})
.attr('x', function (d) {
return timeScale(d.start) + theSidePad;
})
.attr('y', conf.gridLineStartPadding) .attr('y', conf.gridLineStartPadding)
.attr('width', (d) => timeScale(d.end.endOf('day')) - timeScale(d.start.startOf('day'))) .attr('width', function (d) {
const renderEnd = d.end.add(1, 'day');
return timeScale(renderEnd) - timeScale(d.start);
})
.attr('height', h - theTopPad - conf.gridLineStartPadding) .attr('height', h - theTopPad - conf.gridLineStartPadding)
.attr('transform-origin', function (d, i) { .attr('transform-origin', function (d, i) {
return ( return (
@@ -609,20 +615,9 @@ export const draw = function (text, id, version, diagObj) {
* @param h * @param h
*/ */
function makeGrid(theSidePad, theTopPad, w, h) { function makeGrid(theSidePad, theTopPad, w, h) {
const dateFormat = diagObj.db.getDateFormat();
const userAxisFormat = diagObj.db.getAxisFormat();
let axisFormat;
if (userAxisFormat) {
axisFormat = userAxisFormat;
} else if (dateFormat === 'D') {
axisFormat = '%d';
} else {
axisFormat = conf.axisFormat ?? '%Y-%m-%d';
}
let bottomXAxis = axisBottom(timeScale) let bottomXAxis = axisBottom(timeScale)
.tickSize(-h + theTopPad + conf.gridLineStartPadding) .tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(timeFormat(axisFormat)); .tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
const reTickInterval = /^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/; const reTickInterval = /^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/;
const resultTickInterval = reTickInterval.exec( const resultTickInterval = reTickInterval.exec(
@@ -674,7 +669,7 @@ export const draw = function (text, id, version, diagObj) {
if (diagObj.db.topAxisEnabled() || conf.topAxis) { if (diagObj.db.topAxisEnabled() || conf.topAxis) {
let topXAxis = axisTop(timeScale) let topXAxis = axisTop(timeScale)
.tickSize(-h + theTopPad + conf.gridLineStartPadding) .tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(timeFormat(axisFormat)); .tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
if (resultTickInterval !== null) { if (resultTickInterval !== null) {
const every = resultTickInterval[1]; const every = resultTickInterval[1];

View File

@@ -1,14 +1,12 @@
// @ts-ignore: JISON doesn't support types // @ts-ignore: JISON doesn't support types
import parser from './parser/mindmap.jison'; import parser from './parser/mindmap.jison';
import { MindmapDB } from './mindmapDb.js'; import db from './mindmapDb.js';
import renderer from './mindmapRenderer.js'; import renderer from './mindmapRenderer.js';
import styles from './styles.js'; import styles from './styles.js';
import type { DiagramDefinition } from '../../diagram-api/types.js'; import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = { export const diagram: DiagramDefinition = {
get db() { db,
return new MindmapDB();
},
renderer, renderer,
parser, parser,
styles, styles,

View File

@@ -1,12 +1,12 @@
// @ts-expect-error No types available for JISON // @ts-expect-error No types available for JISON
import { parser as mindmap } from './parser/mindmap.jison'; import { parser as mindmap } from './parser/mindmap.jison';
import { MindmapDB } from './mindmapDb.js'; import mindmapDB from './mindmapDb.js';
// Todo fix utils functions for tests // Todo fix utils functions for tests
import { setLogLevel } from '../../diagram-api/diagramAPI.js'; import { setLogLevel } from '../../diagram-api/diagramAPI.js';
describe('when parsing a mindmap ', function () { describe('when parsing a mindmap ', function () {
beforeEach(function () { beforeEach(function () {
mindmap.yy = new MindmapDB(); mindmap.yy = mindmapDB;
mindmap.yy.clear(); mindmap.yy.clear();
setLogLevel('trace'); setLogLevel('trace');
}); });

View File

@@ -5,6 +5,70 @@ import { log } from '../../logger.js';
import type { MindmapNode } from './mindmapTypes.js'; import type { MindmapNode } from './mindmapTypes.js';
import defaultConfig from '../../defaultConfig.js'; import defaultConfig from '../../defaultConfig.js';
let nodes: MindmapNode[] = [];
let cnt = 0;
let elements: Record<number, D3Element> = {};
const clear = () => {
nodes = [];
cnt = 0;
elements = {};
};
const getParent = function (level: number) {
for (let i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].level < level) {
return nodes[i];
}
}
// No parent found
return null;
};
const getMindmap = () => {
return nodes.length > 0 ? nodes[0] : null;
};
const addNode = (level: number, id: string, descr: string, type: number) => {
log.info('addNode', level, id, descr, type);
const conf = getConfig();
let padding: number = conf.mindmap?.padding ?? defaultConfig.mindmap.padding;
switch (type) {
case nodeType.ROUNDED_RECT:
case nodeType.RECT:
case nodeType.HEXAGON:
padding *= 2;
}
const node = {
id: cnt++,
nodeId: sanitizeText(id, conf),
level,
descr: sanitizeText(descr, conf),
type,
children: [],
width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth,
padding,
} satisfies MindmapNode;
const parent = getParent(level);
if (parent) {
parent.children.push(node);
// Keep all nodes in the list
nodes.push(node);
} else {
if (nodes.length === 0) {
// First node, the root
nodes.push(node);
} else {
// Syntax error ... there can only bee one root
throw new Error(
'There can be only one root. No parent could be found for ("' + node.descr + '")'
);
}
}
};
const nodeType = { const nodeType = {
DEFAULT: 0, DEFAULT: 0,
NO_BORDER: 0, NO_BORDER: 0,
@@ -14,149 +78,82 @@ const nodeType = {
CLOUD: 4, CLOUD: 4,
BANG: 5, BANG: 5,
HEXAGON: 6, HEXAGON: 6,
};
const getType = (startStr: string, endStr: string): number => {
log.debug('In get type', startStr, endStr);
switch (startStr) {
case '[':
return nodeType.RECT;
case '(':
return endStr === ')' ? nodeType.ROUNDED_RECT : nodeType.CLOUD;
case '((':
return nodeType.CIRCLE;
case ')':
return nodeType.CLOUD;
case '))':
return nodeType.BANG;
case '{{':
return nodeType.HEXAGON;
default:
return nodeType.DEFAULT;
}
};
const setElementForId = (id: number, element: D3Element) => {
elements[id] = element;
};
const decorateNode = (decoration?: { class?: string; icon?: string }) => {
if (!decoration) {
return;
}
const config = getConfig();
const node = nodes[nodes.length - 1];
if (decoration.icon) {
node.icon = sanitizeText(decoration.icon, config);
}
if (decoration.class) {
node.class = sanitizeText(decoration.class, config);
}
};
const type2Str = (type: number) => {
switch (type) {
case nodeType.DEFAULT:
return 'no-border';
case nodeType.RECT:
return 'rect';
case nodeType.ROUNDED_RECT:
return 'rounded-rect';
case nodeType.CIRCLE:
return 'circle';
case nodeType.CLOUD:
return 'cloud';
case nodeType.BANG:
return 'bang';
case nodeType.HEXAGON:
return 'hexgon'; // cspell: disable-line
default:
return 'no-border';
}
};
// Expose logger to grammar
const getLogger = () => log;
const getElementById = (id: number) => elements[id];
const db = {
clear,
addNode,
getMindmap,
nodeType,
getType,
setElementForId,
decorateNode,
type2Str,
getLogger,
getElementById,
} as const; } as const;
export class MindmapDB { export default db;
private nodes: MindmapNode[] = [];
private count = 0;
private elements: Record<number, D3Element> = {};
public readonly nodeType: typeof nodeType;
constructor() {
this.getLogger = this.getLogger.bind(this);
this.nodeType = nodeType;
this.clear();
this.getType = this.getType.bind(this);
this.getMindmap = this.getMindmap.bind(this);
this.getElementById = this.getElementById.bind(this);
this.getParent = this.getParent.bind(this);
this.getMindmap = this.getMindmap.bind(this);
this.addNode = this.addNode.bind(this);
this.decorateNode = this.decorateNode.bind(this);
}
public clear() {
this.nodes = [];
this.count = 0;
this.elements = {};
}
public getParent(level: number): MindmapNode | null {
for (let i = this.nodes.length - 1; i >= 0; i--) {
if (this.nodes[i].level < level) {
return this.nodes[i];
}
}
return null;
}
public getMindmap(): MindmapNode | null {
return this.nodes.length > 0 ? this.nodes[0] : null;
}
public addNode(level: number, id: string, descr: string, type: number): void {
log.info('addNode', level, id, descr, type);
const conf = getConfig();
let padding = conf.mindmap?.padding ?? defaultConfig.mindmap.padding;
switch (type) {
case this.nodeType.ROUNDED_RECT:
case this.nodeType.RECT:
case this.nodeType.HEXAGON:
padding *= 2;
break;
}
const node: MindmapNode = {
id: this.count++,
nodeId: sanitizeText(id, conf),
level,
descr: sanitizeText(descr, conf),
type,
children: [],
width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth,
padding,
};
const parent = this.getParent(level);
if (parent) {
parent.children.push(node);
this.nodes.push(node);
} else {
if (this.nodes.length === 0) {
this.nodes.push(node);
} else {
throw new Error(
`There can be only one root. No parent could be found for ("${node.descr}")`
);
}
}
}
public getType(startStr: string, endStr: string) {
log.debug('In get type', startStr, endStr);
switch (startStr) {
case '[':
return this.nodeType.RECT;
case '(':
return endStr === ')' ? this.nodeType.ROUNDED_RECT : this.nodeType.CLOUD;
case '((':
return this.nodeType.CIRCLE;
case ')':
return this.nodeType.CLOUD;
case '))':
return this.nodeType.BANG;
case '{{':
return this.nodeType.HEXAGON;
default:
return this.nodeType.DEFAULT;
}
}
public setElementForId(id: number, element: D3Element): void {
this.elements[id] = element;
}
public getElementById(id: number) {
return this.elements[id];
}
public decorateNode(decoration?: { class?: string; icon?: string }): void {
if (!decoration) {
return;
}
const config = getConfig();
const node = this.nodes[this.nodes.length - 1];
if (decoration.icon) {
node.icon = sanitizeText(decoration.icon, config);
}
if (decoration.class) {
node.class = sanitizeText(decoration.class, config);
}
}
type2Str(type: number): string {
switch (type) {
case this.nodeType.DEFAULT:
return 'no-border';
case this.nodeType.RECT:
return 'rect';
case this.nodeType.ROUNDED_RECT:
return 'rounded-rect';
case this.nodeType.CIRCLE:
return 'circle';
case this.nodeType.CLOUD:
return 'cloud';
case this.nodeType.BANG:
return 'bang';
case this.nodeType.HEXAGON:
return 'hexgon'; // cspell: disable-line
default:
return 'no-border';
}
}
public getLogger() {
return log;
}
}

View File

@@ -9,10 +9,10 @@ import { log } from '../../logger.js';
import type { D3Element } from '../../types.js'; import type { D3Element } from '../../types.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js'; import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import type { FilledMindMapNode, MindmapNode } from './mindmapTypes.js'; import type { FilledMindMapNode, MindmapDB, MindmapNode } from './mindmapTypes.js';
import { drawNode, positionNode } from './svgDraw.js'; import { drawNode, positionNode } from './svgDraw.js';
import defaultConfig from '../../defaultConfig.js'; import defaultConfig from '../../defaultConfig.js';
import type { MindmapDB } from './mindmapDb.js';
// Inject the layout algorithm into cytoscape // Inject the layout algorithm into cytoscape
cytoscape.use(coseBilkent); cytoscape.use(coseBilkent);

View File

@@ -1,4 +1,5 @@
import type { RequiredDeep } from 'type-fest'; import type { RequiredDeep } from 'type-fest';
import type mindmapDb from './mindmapDb.js';
export interface MindmapNode { export interface MindmapNode {
id: number; id: number;
@@ -18,3 +19,4 @@ export interface MindmapNode {
} }
export type FilledMindMapNode = RequiredDeep<MindmapNode>; export type FilledMindMapNode = RequiredDeep<MindmapNode>;
export type MindmapDB = typeof mindmapDb;

View File

@@ -1,9 +1,8 @@
import { createText } from '../../rendering-util/createText.js'; import { createText } from '../../rendering-util/createText.js';
import type { FilledMindMapNode } from './mindmapTypes.js'; import type { FilledMindMapNode, MindmapDB } from './mindmapTypes.js';
import type { Point, D3Element } from '../../types.js'; import type { Point, D3Element } from '../../types.js';
import { parseFontSize } from '../../utils.js'; import { parseFontSize } from '../../utils.js';
import type { MermaidConfig } from '../../config.type.js'; import type { MermaidConfig } from '../../config.type.js';
import type { MindmapDB } from './mindmapDb.js';
const MAX_SECTIONS = 12; const MAX_SECTIONS = 12;

View File

@@ -1,7 +1,6 @@
import { getConfig as commonGetConfig } from '../../config.js'; import { getConfig as commonGetConfig } from '../../config.js';
import type { PacketDiagramConfig } from '../../config.type.js'; import type { PacketDiagramConfig } from '../../config.type.js';
import DEFAULT_CONFIG from '../../defaultConfig.js'; import DEFAULT_CONFIG from '../../defaultConfig.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import { cleanAndMerge } from '../../utils.js'; import { cleanAndMerge } from '../../utils.js';
import { import {
clear as commonClear, clear as commonClear,
@@ -12,42 +11,49 @@ import {
setAccTitle, setAccTitle,
setDiagramTitle, setDiagramTitle,
} from '../common/commonDb.js'; } from '../common/commonDb.js';
import type { PacketWord } from './types.js'; import type { PacketDB, PacketData, PacketWord } from './types.js';
const defaultPacketData: PacketData = {
packet: [],
};
let data: PacketData = structuredClone(defaultPacketData);
const DEFAULT_PACKET_CONFIG: Required<PacketDiagramConfig> = DEFAULT_CONFIG.packet; const DEFAULT_PACKET_CONFIG: Required<PacketDiagramConfig> = DEFAULT_CONFIG.packet;
export class PacketDB implements DiagramDB { const getConfig = (): Required<PacketDiagramConfig> => {
private packet: PacketWord[] = []; const config = cleanAndMerge({
...DEFAULT_PACKET_CONFIG,
public getConfig() { ...commonGetConfig().packet,
const config = cleanAndMerge({ });
...DEFAULT_PACKET_CONFIG, if (config.showBits) {
...commonGetConfig().packet, config.paddingY += 10;
});
if (config.showBits) {
config.paddingY += 10;
}
return config;
} }
return config;
};
public getPacket() { const getPacket = (): PacketWord[] => data.packet;
return this.packet;
const pushWord = (word: PacketWord) => {
if (word.length > 0) {
data.packet.push(word);
} }
};
public pushWord(word: PacketWord) { const clear = () => {
if (word.length > 0) { commonClear();
this.packet.push(word); data = structuredClone(defaultPacketData);
} };
}
public clear() { export const db: PacketDB = {
commonClear(); pushWord,
this.packet = []; getPacket,
} getConfig,
clear,
public setAccTitle = setAccTitle; setAccTitle,
public getAccTitle = getAccTitle; getAccTitle,
public setDiagramTitle = setDiagramTitle; setDiagramTitle,
public getDiagramTitle = getDiagramTitle; getDiagramTitle,
public getAccDescription = getAccDescription; getAccDescription,
public setAccDescription = setAccDescription; setAccDescription,
} };

View File

@@ -1,14 +1,12 @@
import type { DiagramDefinition } from '../../diagram-api/types.js'; import type { DiagramDefinition } from '../../diagram-api/types.js';
import { PacketDB } from './db.js'; import { db } from './db.js';
import { parser } from './parser.js'; import { parser } from './parser.js';
import { renderer } from './renderer.js'; import { renderer } from './renderer.js';
import { styles } from './styles.js'; import { styles } from './styles.js';
export const diagram: DiagramDefinition = { export const diagram: DiagramDefinition = {
parser, parser,
get db() { db,
return new PacketDB();
},
renderer, renderer,
styles, styles,
}; };

View File

@@ -1,26 +1,24 @@
import { it, describe, expect } from 'vitest'; import { it, describe, expect } from 'vitest';
import { PacketDB } from './db.js'; import { db } from './db.js';
import { parser } from './parser.js'; import { parser } from './parser.js';
const { clear, getPacket, getDiagramTitle, getAccTitle, getAccDescription } = db;
describe('packet diagrams', () => { describe('packet diagrams', () => {
let db: PacketDB;
beforeEach(() => { beforeEach(() => {
db = new PacketDB(); clear();
if (parser.parser) {
parser.parser.yy = db;
}
}); });
it('should handle a packet-beta definition', async () => { it('should handle a packet-beta definition', async () => {
const str = `packet-beta`; const str = `packet-beta`;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot('[]'); expect(getPacket()).toMatchInlineSnapshot('[]');
}); });
it('should handle a packet definition', async () => { it('should handle a packet definition', async () => {
const str = `packet`; const str = `packet`;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot('[]'); expect(getPacket()).toMatchInlineSnapshot('[]');
}); });
it('should handle diagram with data and title', async () => { it('should handle diagram with data and title', async () => {
@@ -31,10 +29,10 @@ describe('packet diagrams', () => {
0-10: "test" 0-10: "test"
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getDiagramTitle()).toMatchInlineSnapshot('"Packet diagram"'); expect(getDiagramTitle()).toMatchInlineSnapshot('"Packet diagram"');
expect(db.getAccTitle()).toMatchInlineSnapshot('"Packet accTitle"'); expect(getAccTitle()).toMatchInlineSnapshot('"Packet accTitle"');
expect(db.getAccDescription()).toMatchInlineSnapshot('"Packet accDescription"'); expect(getAccDescription()).toMatchInlineSnapshot('"Packet accDescription"');
expect(db.getPacket()).toMatchInlineSnapshot(` expect(getPacket()).toMatchInlineSnapshot(`
[ [
[ [
{ {
@@ -54,7 +52,7 @@ describe('packet diagrams', () => {
11: "single" 11: "single"
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot(` expect(getPacket()).toMatchInlineSnapshot(`
[ [
[ [
{ {
@@ -80,7 +78,7 @@ describe('packet diagrams', () => {
+16: "word" +16: "word"
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot(` expect(getPacket()).toMatchInlineSnapshot(`
[ [
[ [
{ {
@@ -106,7 +104,7 @@ describe('packet diagrams', () => {
+16: "word" +16: "word"
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot(` expect(getPacket()).toMatchInlineSnapshot(`
[ [
[ [
{ {
@@ -132,7 +130,7 @@ describe('packet diagrams', () => {
11-90: "multiple" 11-90: "multiple"
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot(` expect(getPacket()).toMatchInlineSnapshot(`
[ [
[ [
{ {
@@ -174,7 +172,7 @@ describe('packet diagrams', () => {
17-63: "multiple" 17-63: "multiple"
`; `;
await expect(parser.parse(str)).resolves.not.toThrow(); await expect(parser.parse(str)).resolves.not.toThrow();
expect(db.getPacket()).toMatchInlineSnapshot(` expect(getPacket()).toMatchInlineSnapshot(`
[ [
[ [
{ {

View File

@@ -3,12 +3,12 @@ import { parse } from '@mermaid-js/parser';
import type { ParserDefinition } from '../../diagram-api/types.js'; import type { ParserDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js'; import { log } from '../../logger.js';
import { populateCommonDb } from '../common/populateCommonDb.js'; import { populateCommonDb } from '../common/populateCommonDb.js';
import { PacketDB } from './db.js'; import { db } from './db.js';
import type { PacketBlock, PacketWord } from './types.js'; import type { PacketBlock, PacketWord } from './types.js';
const maxPacketSize = 10_000; const maxPacketSize = 10_000;
const populate = (ast: Packet, db: PacketDB) => { const populate = (ast: Packet) => {
populateCommonDb(ast, db); populateCommonDb(ast, db);
let lastBit = -1; let lastBit = -1;
let word: PacketWord = []; let word: PacketWord = [];
@@ -91,17 +91,9 @@ const getNextFittingBlock = (
}; };
export const parser: ParserDefinition = { export const parser: ParserDefinition = {
// @ts-expect-error - PacketDB is not assignable to DiagramDB
parser: { yy: undefined },
parse: async (input: string): Promise<void> => { parse: async (input: string): Promise<void> => {
const ast: Packet = await parse('packet', input); const ast: Packet = await parse('packet', input);
const db = parser.parser?.yy;
if (!(db instanceof PacketDB)) {
throw new Error(
'parser.parser?.yy was not a PacketDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.'
);
}
log.debug(ast); log.debug(ast);
populate(ast, db); populate(ast);
}, },
}; };

View File

@@ -139,32 +139,6 @@ describe('pie', () => {
}).rejects.toThrowError(); }).rejects.toThrowError();
}); });
it('should handle simple pie with zero slice value', async () => {
await parser.parse(`pie title Default text position: Animal adoption
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"dogs" : 0
"rats" : 40.12
`);
const sections = db.getSections();
expect(sections.get('dogs')).toBe(0);
expect(sections.get('rats')).toBe(40.12);
});
it('should handle simple pie with negative slice value', async () => {
await expect(async () => {
await parser.parse(`pie title Default text position: Animal adoption
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"dogs" : -60.67
"rats" : 40.12
`);
}).rejects.toThrowError(
'"dogs" has invalid value: -60.67. Negative values are not allowed in pie charts. All slice values must be >= 0.'
);
});
it('should handle unsafe properties', async () => { it('should handle unsafe properties', async () => {
await expect( await expect(
parser.parse(`pie title Unsafe props test parser.parse(`pie title Unsafe props test

Some files were not shown because too many files have changed in this diff Show More