mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-23 04:36:59 +01:00
Compare commits
989 Commits
@mermaid-j
...
dev-tool
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f91ed32bc | ||
|
|
d5bc07dc0c | ||
|
|
80a686be03 | ||
|
|
d26f2c6043 | ||
|
|
8ca7a28bf3 | ||
|
|
6b77c9c4c7 | ||
|
|
7b167cf331 | ||
|
|
d435ac6fe1 | ||
|
|
ed96d067fc | ||
|
|
09c60be450 | ||
|
|
39d070fdea | ||
|
|
4f6f627e75 | ||
|
|
53570ee815 | ||
|
|
a3a5040d79 | ||
|
|
7ff9bf1a50 | ||
|
|
ffd38716d0 | ||
|
|
4313f233d2 | ||
|
|
56564f3807 | ||
|
|
7e1a1de5e9 | ||
|
|
b4b90417cf | ||
|
|
6bf2b2108c | ||
|
|
614129ca31 | ||
|
|
b115ad3cd7 | ||
|
|
73b8626ab0 | ||
|
|
88e8ad6f5b | ||
|
|
ac5fdb8121 | ||
|
|
a30b3bb3f8 | ||
|
|
87c561615e | ||
|
|
0843a2fa7a | ||
|
|
1aa4cd9847 | ||
|
|
8d1cdc41c2 | ||
|
|
bf50ce5237 | ||
|
|
8bfd47758a | ||
|
|
88fd141276 | ||
|
|
454238867b | ||
|
|
6025ec663c | ||
|
|
2346801c30 | ||
|
|
d7eb94dc89 | ||
|
|
db9c683316 | ||
|
|
c9b5c5fed6 | ||
|
|
324cf05afd | ||
|
|
a357c1079f | ||
|
|
e20b079707 | ||
|
|
b1fe4ffe97 | ||
|
|
61c18b99a0 | ||
|
|
04ac594f5b | ||
|
|
a6c2b1d85e | ||
|
|
96ca7c090f | ||
|
|
3c752421a2 | ||
|
|
bf7c532e43 | ||
|
|
8add133bbe | ||
|
|
8cf0d3373d | ||
|
|
ff3b194925 | ||
|
|
6e869ff8dc | ||
|
|
9df18da01c | ||
|
|
608d623641 | ||
|
|
ecf9ea1134 | ||
|
|
b33ce14932 | ||
|
|
283aef54e4 | ||
|
|
96f87fd597 | ||
|
|
03e8589818 | ||
|
|
6b9f26dac8 | ||
|
|
ea590cdafe | ||
|
|
f3769c70bc | ||
|
|
4cf4d15197 | ||
|
|
c02cf92656 | ||
|
|
3a1266892d | ||
|
|
67e81de557 | ||
|
|
847b3aa24e | ||
|
|
85a13da40f | ||
|
|
9ec0e8f932 | ||
|
|
9585ee7533 | ||
|
|
1269486124 | ||
|
|
f45ea0c60e | ||
|
|
d20955a56a | ||
|
|
fb66b3fbe3 | ||
|
|
82ea5d63bb | ||
|
|
881e74087a | ||
|
|
09920c0497 | ||
|
|
8065d65cd7 | ||
|
|
6bc6617ca6 | ||
|
|
29ed57ffec | ||
|
|
9fdc4b8005 | ||
|
|
09b841f781 | ||
|
|
d0f9dc0c9b | ||
|
|
15e2824d53 | ||
|
|
7eb582e860 | ||
|
|
6ca928f31f | ||
|
|
983120d945 | ||
|
|
61f74ffc5e | ||
|
|
74318f9337 | ||
|
|
dfd59470dc | ||
|
|
4aac6fa448 | ||
|
|
5f96f80efb | ||
|
|
545801e144 | ||
|
|
0bd74759cc | ||
|
|
e4cf266c1d | ||
|
|
c0e1662e50 | ||
|
|
6546aed482 | ||
|
|
b76ccae065 | ||
|
|
287a9a3fcb | ||
|
|
7f5160fa4d | ||
|
|
7b0763f262 | ||
|
|
38c289818c | ||
|
|
57530076aa | ||
|
|
f2d7877c7a | ||
|
|
62d2c6505e | ||
|
|
974236bbb8 | ||
|
|
cf0d1248a4 | ||
|
|
ebefbd87a8 | ||
|
|
1e7b71a085 | ||
|
|
f28f3c25aa | ||
|
|
58137aa631 | ||
|
|
e7719f14c5 | ||
|
|
35d9cead8a | ||
|
|
13baf51b35 | ||
|
|
9af985ba9b | ||
|
|
c7f8a11ded | ||
|
|
762b44cf33 | ||
|
|
02c0091106 | ||
|
|
16359adc33 | ||
|
|
061632c580 | ||
|
|
cbf89462ac | ||
|
|
700aa100f2 | ||
|
|
49103ea654 | ||
|
|
3f46c94ab2 | ||
|
|
b136acdc67 | ||
|
|
bba5e5938e | ||
|
|
fed8a523a4 | ||
|
|
33b4946e21 | ||
|
|
3d768f3adf | ||
|
|
76e17ffd20 | ||
|
|
835de0012d | ||
|
|
60f633101c | ||
|
|
18f51eb14e | ||
|
|
2bb57bf7d2 | ||
|
|
a6276daffd | ||
|
|
7def6eecbf | ||
|
|
96a766dcdb | ||
|
|
39d7ebd32e | ||
|
|
34f40f0794 | ||
|
|
32ac2c689d | ||
|
|
dbcadc1d0b | ||
|
|
ac411a7d7e | ||
|
|
d80a638e55 | ||
|
|
7a869c08a2 | ||
|
|
44e8cbb1de | ||
|
|
efe38b8425 | ||
|
|
6fecb985e8 | ||
|
|
69b338d8af | ||
|
|
fa15ce8502 | ||
|
|
6d0650918f | ||
|
|
c728d864c8 | ||
|
|
99f17bea3a | ||
|
|
ad82448084 | ||
|
|
9498619d3c | ||
|
|
7a8557a1a2 | ||
|
|
74863c94fb | ||
|
|
63df702146 | ||
|
|
421f8d4633 | ||
|
|
bf6e1a594c | ||
|
|
c1c14e401a | ||
|
|
8b3057f27c | ||
|
|
717d3b3bb2 | ||
|
|
2f8d9ba958 | ||
|
|
ace0367afd | ||
|
|
b983626587 | ||
|
|
7effdc147b | ||
|
|
6e67515f41 | ||
|
|
1a9d45abf0 | ||
|
|
09b74f1c29 | ||
|
|
880da21908 | ||
|
|
38191243be | ||
|
|
b75dcb8a82 | ||
|
|
4c1e170f4a | ||
|
|
d5c4eff251 | ||
|
|
5324fd8dfd | ||
|
|
bd25b88a01 | ||
|
|
0116b272b4 | ||
|
|
634f3367da | ||
|
|
37e3a6951b | ||
|
|
0de0b063e4 | ||
|
|
619515e5a9 | ||
|
|
59c8b07509 | ||
|
|
9e72bbf62d | ||
|
|
2a2c46f1e2 | ||
|
|
f25df353d4 | ||
|
|
398345a8bc | ||
|
|
7fd2d94ef7 | ||
|
|
bcc1472b9d | ||
|
|
ddc1cfe6c8 | ||
|
|
d3de3ecbbb | ||
|
|
18e9c1174d | ||
|
|
789018abf6 | ||
|
|
16569b295b | ||
|
|
11a35c11ee | ||
|
|
216be22801 | ||
|
|
e87f77a865 | ||
|
|
a9579083bf | ||
|
|
6fd78d0856 | ||
|
|
994f7df29a | ||
|
|
531f5e9380 | ||
|
|
dc11b8645c | ||
|
|
ad4c227477 | ||
|
|
3964ce0a0f | ||
|
|
181af8167b | ||
|
|
799d2ed547 | ||
|
|
a44e3e992c | ||
|
|
ca5b370ffb | ||
|
|
08160a74b4 | ||
|
|
6d221fb3ca | ||
|
|
8b20907141 | ||
|
|
4dbabba8e8 | ||
|
|
d318f1a13c | ||
|
|
525a7de8ae | ||
|
|
a459c436c9 | ||
|
|
bbb93b263d | ||
|
|
1c2a0020bd | ||
|
|
141c6b3808 | ||
|
|
8d4ffdf808 | ||
|
|
32106e259c | ||
|
|
450754221e | ||
|
|
7a4f5b62c9 | ||
|
|
e3ef5e4208 | ||
|
|
daeb85bac2 | ||
|
|
4240340a18 | ||
|
|
b36edd557e | ||
|
|
5e3b5e8f36 | ||
|
|
764b315dc1 | ||
|
|
47c0d2d040 | ||
|
|
ac3b777bf6 | ||
|
|
cf08ba0ef8 | ||
|
|
166782cd38 | ||
|
|
b37eb6d0d1 | ||
|
|
f759f5dcf7 | ||
|
|
80bcefe321 | ||
|
|
70cbbe69d8 | ||
|
|
baf4093e8d | ||
|
|
fd185f7694 | ||
|
|
027d7b6368 | ||
|
|
7986b66a88 | ||
|
|
edb0edc451 | ||
|
|
b511a2e9be | ||
|
|
4829dfa8c5 | ||
|
|
b80ea26a2b | ||
|
|
f88986a87d | ||
|
|
e16f0848ab | ||
|
|
e7811886c3 | ||
|
|
32eda8565c | ||
|
|
2812a0d12a | ||
|
|
25fa26d915 | ||
|
|
62915183b1 | ||
|
|
6874ab3fb6 | ||
|
|
040af4f545 | ||
|
|
65ca3eabfd | ||
|
|
8b9bbad842 | ||
|
|
d2773db7dc | ||
|
|
ca10a259fa | ||
|
|
3840451fda | ||
|
|
cfe9238882 | ||
|
|
0ed9c65572 | ||
|
|
56cc12690f | ||
|
|
2cdaf03ada | ||
|
|
9c85521e16 | ||
|
|
8a565bce92 | ||
|
|
baf510b935 | ||
|
|
c1f2d052be | ||
|
|
bce40e180a | ||
|
|
0dd46a3543 | ||
|
|
f81e63663c | ||
|
|
7109e3a17f | ||
|
|
e0bd51941e | ||
|
|
38f4e67ca7 | ||
|
|
681d829227 | ||
|
|
164e44c3d9 | ||
|
|
f6fa0260e7 | ||
|
|
f47dec3680 | ||
|
|
29aad6d23c | ||
|
|
88dc4beade | ||
|
|
e9232088c0 | ||
|
|
e96614ab86 | ||
|
|
73115cb416 | ||
|
|
480438bd52 | ||
|
|
34fc8bddc4 | ||
|
|
4dd89e439f | ||
|
|
150177c449 | ||
|
|
bf58ed2b53 | ||
|
|
827ced0014 | ||
|
|
133d46bde2 | ||
|
|
e1017266ac | ||
|
|
404fdaf2ff | ||
|
|
82ef7b5fdb | ||
|
|
11cd3f1262 | ||
|
|
2e1d156d66 | ||
|
|
e863ad1547 | ||
|
|
e231b692fd | ||
|
|
68c365f906 | ||
|
|
494c7294cb | ||
|
|
ac4aa94e78 | ||
|
|
fb20ee99eb | ||
|
|
1a22154a3a | ||
|
|
2972bf25bf | ||
|
|
6b1a7a9e1a | ||
|
|
33bc4a0b4e | ||
|
|
c6f25167a2 | ||
|
|
a150f92fb0 | ||
|
|
5d31ded7a0 | ||
|
|
0ed31bfa2c | ||
|
|
51b9185a6b | ||
|
|
b219497847 | ||
|
|
7e96c89be5 | ||
|
|
16a8d0e794 | ||
|
|
7bb9981d8a | ||
|
|
ea3d38bf64 | ||
|
|
8f628b85e5 | ||
|
|
defc922acd | ||
|
|
88ae8d1f2b | ||
|
|
8c7c9ac38a | ||
|
|
0e146d50f7 | ||
|
|
c40faac80d | ||
|
|
454e1e3927 | ||
|
|
4f9875fd4e | ||
|
|
82800a2c84 | ||
|
|
27e700debd | ||
|
|
01e47333d5 | ||
|
|
d47ba7c2d1 | ||
|
|
b1c4eb3f5c | ||
|
|
869709a75f | ||
|
|
310fcd2292 | ||
|
|
85e9ca2a0f | ||
|
|
65d225cb2c | ||
|
|
04b6fc1280 | ||
|
|
c530baed3f | ||
|
|
21eddc3f23 | ||
|
|
f46a151075 | ||
|
|
b7e9d02b7c | ||
|
|
0ef3130510 | ||
|
|
862d40cc3a | ||
|
|
4b63214a72 | ||
|
|
4937ebc058 | ||
|
|
00f5700320 | ||
|
|
e32dc8513f | ||
|
|
50127f3ffe | ||
|
|
29bb0e3dca | ||
|
|
1221de4c2d | ||
|
|
c41e08cb7a | ||
|
|
4760ed8893 | ||
|
|
31ecf31c2e | ||
|
|
b52766653c | ||
|
|
6d9fad01a9 | ||
|
|
8322a63598 | ||
|
|
075e1b5e1f | ||
|
|
045699de10 | ||
|
|
3c9bd7be29 | ||
|
|
6995248443 | ||
|
|
93467a6fce | ||
|
|
95d48e3497 | ||
|
|
202172135d | ||
|
|
b94ab243a8 | ||
|
|
11c8848e1f | ||
|
|
231fcc700f | ||
|
|
8ba7520acc | ||
|
|
e0a5a2489d | ||
|
|
bd400a5130 | ||
|
|
d35f84f337 | ||
|
|
af3bbdc591 | ||
|
|
1988d24227 | ||
|
|
8813cf2c94 | ||
|
|
d145c0e910 | ||
|
|
39f90debe7 | ||
|
|
8dadb853a0 | ||
|
|
29886b8dd4 | ||
|
|
e438e035bc | ||
|
|
2bc5b6d2fa | ||
|
|
e0b45c2d2b | ||
|
|
d4c76968e9 | ||
|
|
a700e8bf97 | ||
|
|
73e9849f99 | ||
|
|
5a05540a5f | ||
|
|
7091792694 | ||
|
|
efd94b705d | ||
|
|
2cfebef122 | ||
|
|
9ec989e633 | ||
|
|
61d9143acb | ||
|
|
c88f74a6ee | ||
|
|
6377d6f64d | ||
|
|
1b0bc05fc2 | ||
|
|
45edeb9307 | ||
|
|
211974b2b7 | ||
|
|
1f5ad3e315 | ||
|
|
d7848e8a3d | ||
|
|
c0e2d4a23b | ||
|
|
89b9f0df70 | ||
|
|
e9011567bd | ||
|
|
7171237b96 | ||
|
|
0429970d58 | ||
|
|
ecad9cee6c | ||
|
|
066883f4cd | ||
|
|
1e8a9f76a9 | ||
|
|
2b58df9665 | ||
|
|
e42fdf1c54 | ||
|
|
c75566ddc3 | ||
|
|
7bdcf93412 | ||
|
|
d86e46b705 | ||
|
|
96778f7789 | ||
|
|
d4c058bd56 | ||
|
|
b638a0a9c1 | ||
|
|
fd9aa36c77 | ||
|
|
46a9f1b31e | ||
|
|
83c6224cc0 | ||
|
|
d8161b1923 | ||
|
|
8223141af9 | ||
|
|
99f98a6876 | ||
|
|
ef28f548df | ||
|
|
e448c53b53 | ||
|
|
71e09bcaef | ||
|
|
cad144734d | ||
|
|
5e57f22e23 | ||
|
|
fc2c32603d | ||
|
|
8c7da5af56 | ||
|
|
4b89af3aca | ||
|
|
c534d3d364 | ||
|
|
4db72f5357 | ||
|
|
2aa8330279 | ||
|
|
803e2e14be | ||
|
|
685516a85e | ||
|
|
880f7454a3 | ||
|
|
7e9577dffd | ||
|
|
cba659d097 | ||
|
|
f7a0844a31 | ||
|
|
2817383714 | ||
|
|
1f46c9e9bb | ||
|
|
ec7099dc27 | ||
|
|
180dc7bdff | ||
|
|
cc9581842d | ||
|
|
a716a525c3 | ||
|
|
e6ee145edf | ||
|
|
d782e4bb17 | ||
|
|
5cc264feb7 | ||
|
|
690cc73259 | ||
|
|
ba9ad9385b | ||
|
|
b9ef683fb6 | ||
|
|
6a6289f2aa | ||
|
|
accb4c6369 | ||
|
|
52be254ad3 | ||
|
|
9c071a9064 | ||
|
|
91d7229f1b | ||
|
|
60feec465b | ||
|
|
d1ae687d1e | ||
|
|
7af6723ac0 | ||
|
|
da90f6760b | ||
|
|
91edfa40f7 | ||
|
|
c8b00bb929 | ||
|
|
d74013c642 | ||
|
|
ce996346f8 | ||
|
|
29edfa7f56 | ||
|
|
14a4ab81c9 | ||
|
|
13d72262d9 | ||
|
|
62dee0bad4 | ||
|
|
9e81e1146a | ||
|
|
57eadbf6af | ||
|
|
2260948b7b | ||
|
|
186429ae32 | ||
|
|
a906adce26 | ||
|
|
657a9ef785 | ||
|
|
4f24489d81 | ||
|
|
11abfc9ae5 | ||
|
|
227cef05b3 | ||
|
|
a6d26ef6c3 | ||
|
|
2a514fa69e | ||
|
|
80c6faf4d5 | ||
|
|
2b3f94eb7d | ||
|
|
81b0ffb92a | ||
|
|
9f6ee53382 | ||
|
|
3248bf3da4 | ||
|
|
bf3ca9d1ef | ||
|
|
e6fb4a84da | ||
|
|
32723b2de1 | ||
|
|
18703782ee | ||
|
|
0b42bdba07 | ||
|
|
dd36046e23 | ||
|
|
1507435e15 | ||
|
|
e7a7ff8a2a | ||
|
|
68fc68c239 | ||
|
|
e53c17a012 | ||
|
|
bb2d6973ba | ||
|
|
769b362005 | ||
|
|
e4d3aa4610 | ||
|
|
716548548a | ||
|
|
aeaf626bb5 | ||
|
|
9322771b5c | ||
|
|
2fe3063bf5 | ||
|
|
4e55a45b1b | ||
|
|
4bece53a3c | ||
|
|
3d319824a6 | ||
|
|
aa5d443a46 | ||
|
|
356da0b4d7 | ||
|
|
22530a8bdf | ||
|
|
e6574ef40c | ||
|
|
c4eb526162 | ||
|
|
68c01b76bf | ||
|
|
4d62d59632 | ||
|
|
5af489d8dd | ||
|
|
28717e108d | ||
|
|
297be4a868 | ||
|
|
fb6ace73b5 | ||
|
|
688d9b383d | ||
|
|
096fbe933e | ||
|
|
e539909e87 | ||
|
|
074701e316 | ||
|
|
e68424d748 | ||
|
|
cfc76ef1cb | ||
|
|
bf362673fc | ||
|
|
d042b21b12 | ||
|
|
677ff82d13 | ||
|
|
4b31361506 | ||
|
|
204a9a338f | ||
|
|
981829a426 | ||
|
|
327a5aa9fd | ||
|
|
6a6a39ff33 | ||
|
|
b296db9a33 | ||
|
|
01ce84d8ee | ||
|
|
f48e663d4c | ||
|
|
a4aa2bd355 | ||
|
|
20a18971ea | ||
|
|
e1e36dfcb3 | ||
|
|
b51b9d50c2 | ||
|
|
74c96db3e2 | ||
|
|
165ffefad5 | ||
|
|
9258b2933b | ||
|
|
f5445b266e | ||
|
|
7a1530d911 | ||
|
|
bd47c57eaf | ||
|
|
3e5d2db514 | ||
|
|
848f69a75c | ||
|
|
99dbeba407 | ||
|
|
d525acc05b | ||
|
|
b61780f735 | ||
|
|
d93d9a521d | ||
|
|
1d3681053b | ||
|
|
93df13898f | ||
|
|
e32e80ea7f | ||
|
|
c99bce6bab | ||
|
|
3256807d25 | ||
|
|
0133f1c0c5 | ||
|
|
12e01bdb5c | ||
|
|
a776b4f0ab | ||
|
|
8d79bc9b19 | ||
|
|
ecf7ab4355 | ||
|
|
c61a431e2d | ||
|
|
526b35c602 | ||
|
|
8090580c67 | ||
|
|
9d685178d2 | ||
|
|
074f18dfb8 | ||
|
|
b451c66d7c | ||
|
|
d7308b0f43 | ||
|
|
1dd11705d9 | ||
|
|
2f1860386a | ||
|
|
17142ef8d7 | ||
|
|
f0bca7da55 | ||
|
|
6fcdf5bfcc | ||
|
|
e2ce0450c1 | ||
|
|
29de40478f | ||
|
|
c95c64139d | ||
|
|
4ab98c2ec7 | ||
|
|
aeb51e56e2 | ||
|
|
a7f12f1baa | ||
|
|
2a8653de2b | ||
|
|
a92c3bb251 | ||
|
|
3677abe9e5 | ||
|
|
ddcd8a5e73 | ||
|
|
e464d080ef | ||
|
|
1a9b94ca2d | ||
|
|
95847ad236 | ||
|
|
e0152fb873 | ||
|
|
e4b33a1d99 | ||
|
|
2298b96d8e | ||
|
|
5db83365b6 | ||
|
|
e27a9da61d | ||
|
|
03cf10003f | ||
|
|
40990bb096 | ||
|
|
8e31fdb611 | ||
|
|
5dd748148f | ||
|
|
895f9d43ff | ||
|
|
4915545429 | ||
|
|
fb890a2be8 | ||
|
|
5986189a52 | ||
|
|
1988dfc956 | ||
|
|
e48b0ba61d | ||
|
|
1a4b8662cf | ||
|
|
6083463c8e | ||
|
|
1a14e331ea | ||
|
|
ebb6680eba | ||
|
|
6d1d46f88a | ||
|
|
435790f931 | ||
|
|
ffe9c1090e | ||
|
|
a476e99d4c | ||
|
|
0cc0b63e52 | ||
|
|
694844050a | ||
|
|
1be1620000 | ||
|
|
c36cd05c45 | ||
|
|
341a81a113 | ||
|
|
8a62b4cace | ||
|
|
b7a591b8d3 | ||
|
|
8bb29fc879 | ||
|
|
e073c80019 | ||
|
|
01aaef39b4 | ||
|
|
3d640fc620 | ||
|
|
7ca0665764 | ||
|
|
334fe87bc6 | ||
|
|
283e7810d2 | ||
|
|
81a6a361ab | ||
|
|
237d01d510 | ||
|
|
62faacdeeb | ||
|
|
0e40d8e8a8 | ||
|
|
e8d6daf4f6 | ||
|
|
724197c910 | ||
|
|
6180c5f2ff | ||
|
|
a9f7a94ae3 | ||
|
|
3ffe9618ae | ||
|
|
da539c1fa1 | ||
|
|
5e8aa2dccf | ||
|
|
ac04172cf8 | ||
|
|
cf5b4b89a8 | ||
|
|
1c269e0432 | ||
|
|
ccafc20917 | ||
|
|
999b836508 | ||
|
|
326e4e3693 | ||
|
|
9c92da487f | ||
|
|
10752f1357 | ||
|
|
1a80854242 | ||
|
|
fc9c600a31 | ||
|
|
da8ce0b93e | ||
|
|
00a79353fc | ||
|
|
1ceeca1ef1 | ||
|
|
94890390ef | ||
|
|
d5cb4eaa59 | ||
|
|
adfeb093cb | ||
|
|
425fb7ee33 | ||
|
|
cb4ed605b2 | ||
|
|
366d217928 | ||
|
|
b94f1336ab | ||
|
|
020c6d66e0 | ||
|
|
ba9db26bfa | ||
|
|
252b1837f7 | ||
|
|
6b9c15d7f0 | ||
|
|
fda640c90c | ||
|
|
cd6f8e5a24 | ||
|
|
584a789183 | ||
|
|
cfc2551bdc | ||
|
|
000308c8f5 | ||
|
|
6039a8b930 | ||
|
|
cd282f2245 | ||
|
|
a27d90fe9c | ||
|
|
64bf34b9ab | ||
|
|
bcd3e33243 | ||
|
|
9faf2f9fb2 | ||
|
|
f683b03645 | ||
|
|
9cef40d164 | ||
|
|
b2754bc553 | ||
|
|
04612e078a | ||
|
|
af585bdcc7 | ||
|
|
37bfa2aa75 | ||
|
|
54640ce476 | ||
|
|
47b4c56b2b | ||
|
|
6b1b0bf151 | ||
|
|
7ba332ad4a | ||
|
|
b65a73f432 | ||
|
|
412d2a09d3 | ||
|
|
7886fed8b2 | ||
|
|
af3d5b6528 | ||
|
|
404286a90d | ||
|
|
827a9af790 | ||
|
|
a14cd0e2a1 | ||
|
|
0823e08a54 | ||
|
|
95733b6295 | ||
|
|
5cafe241d0 | ||
|
|
c847817a54 | ||
|
|
8b86d617e7 | ||
|
|
3ab0961bdc | ||
|
|
cc2112c7aa | ||
|
|
0892870b5d | ||
|
|
500f90a105 | ||
|
|
14983158a2 | ||
|
|
767754f4fb | ||
|
|
cff59c58b4 | ||
|
|
3fea9e8759 | ||
|
|
e5ea2ed0b1 | ||
|
|
34e7f9704b | ||
|
|
ac976245ad | ||
|
|
1176d30668 | ||
|
|
5b241bbb97 | ||
|
|
7e5e47843b | ||
|
|
6728852b7a | ||
|
|
15e7c890ed | ||
|
|
9e2cd1a926 | ||
|
|
abf2227faf | ||
|
|
7db942b0e1 | ||
|
|
70041c806f | ||
|
|
77e2703f72 | ||
|
|
771801b366 | ||
|
|
a9a0a9b2de | ||
|
|
1e5e835c41 | ||
|
|
1f3f8da0f7 | ||
|
|
b11f40e8ce | ||
|
|
1fe045e638 | ||
|
|
1c750ffc70 | ||
|
|
b0ec93f29c | ||
|
|
6b071c135a | ||
|
|
42a3c3487f | ||
|
|
d7b8ed2c5a | ||
|
|
96c21c7e54 | ||
|
|
e95e4d155a | ||
|
|
688170558c | ||
|
|
832f012e10 | ||
|
|
2e2e8c4152 | ||
|
|
6ff6e08c4b | ||
|
|
d7f1f12549 | ||
|
|
6e56869566 | ||
|
|
d3e2be35be | ||
|
|
852cb35f0a | ||
|
|
260a045da0 | ||
|
|
a28965064d | ||
|
|
355eeeb9cc | ||
|
|
5449d6a447 | ||
|
|
34e91f8b65 | ||
|
|
627ee1f34d | ||
|
|
28840ebd84 | ||
|
|
4145879003 | ||
|
|
0451e343ef | ||
|
|
a2dbc8e4b3 | ||
|
|
e097b480d5 | ||
|
|
f76e27db70 | ||
|
|
3e3ae08930 | ||
|
|
966c112eb1 | ||
|
|
55527e70c2 | ||
|
|
447d1cf988 | ||
|
|
003d1c7a70 | ||
|
|
daf8d8d3be | ||
|
|
d7a55b422b | ||
|
|
12e3d31437 | ||
|
|
ad024b01d6 | ||
|
|
c12aea588c | ||
|
|
9dfbf1166d | ||
|
|
98bf9b4cb4 | ||
|
|
e9ce8cf4da | ||
|
|
d90634bf2b | ||
|
|
90707e8062 | ||
|
|
7e23f984e6 | ||
|
|
b3a12237c0 | ||
|
|
aea16eaf7e | ||
|
|
bb7cd70034 | ||
|
|
fad6676d18 | ||
|
|
637680d4d9 | ||
|
|
35a92efcdc | ||
|
|
5f6f5110fd | ||
|
|
af47269342 | ||
|
|
d3c0893937 | ||
|
|
75ef9bc681 | ||
|
|
45edc91591 | ||
|
|
f4edd19371 | ||
|
|
b611a13e04 | ||
|
|
7a38eb715d | ||
|
|
2715ddb338 | ||
|
|
ca2eca58c9 | ||
|
|
fce7cabb71 | ||
|
|
38e6dc497a | ||
|
|
5f0c53c8a7 | ||
|
|
f9da4433ff | ||
|
|
fc07f0d8ab | ||
|
|
b4fae2d096 | ||
|
|
ce7a487dfc | ||
|
|
6dd9af0dd4 | ||
|
|
3151241559 | ||
|
|
8c0d12027d | ||
|
|
afeb761296 | ||
|
|
3abcfbb8d2 | ||
|
|
ee82694645 | ||
|
|
dda9c9b46e | ||
|
|
cdd1a70b67 | ||
|
|
12c94a177b | ||
|
|
d8bd4dea93 | ||
|
|
6deb476182 | ||
|
|
ed297ee235 | ||
|
|
03c1201fcb | ||
|
|
3ca317c5a0 | ||
|
|
4d83263388 | ||
|
|
254e5cbd51 | ||
|
|
a58dd3c6ce | ||
|
|
10b7bb568f | ||
|
|
1aa2870224 | ||
|
|
8fbcbb6dc9 | ||
|
|
3e545d7925 | ||
|
|
75d2a259ed | ||
|
|
746280b3e2 | ||
|
|
fa4e30bb15 | ||
|
|
950b107dd4 | ||
|
|
35b84761a9 | ||
|
|
0da2922ee7 | ||
|
|
85eba01663 | ||
|
|
ea72740d1f | ||
|
|
79ba50216a | ||
|
|
71b04f93b0 | ||
|
|
27185f62e4 | ||
|
|
9655d07adf | ||
|
|
2b3cb6a362 | ||
|
|
427dc38857 | ||
|
|
ec21042c53 | ||
|
|
89ac2932c4 | ||
|
|
e828609749 | ||
|
|
91f141f772 | ||
|
|
c77b968f1e | ||
|
|
ba13981905 | ||
|
|
a7a94b95e1 | ||
|
|
31f141c61a | ||
|
|
fb017bebfd | ||
|
|
648698a43a | ||
|
|
a5e4729c76 | ||
|
|
c884def5fc | ||
|
|
4a86b68e01 | ||
|
|
592c5bb880 | ||
|
|
da0c6c6c32 | ||
|
|
012530e98e | ||
|
|
a4a27611dd | ||
|
|
5055ade44e | ||
|
|
b61bec8faf | ||
|
|
1414380181 | ||
|
|
6686ee9253 | ||
|
|
7f109c7b94 | ||
|
|
b9c3375be3 | ||
|
|
4254bdd473 | ||
|
|
4012cbf013 | ||
|
|
939da082b2 | ||
|
|
d9396eedd6 | ||
|
|
76d073b027 | ||
|
|
5f9601b6a8 | ||
|
|
378f8ece0c | ||
|
|
8693be56ee | ||
|
|
148a42a31a | ||
|
|
8980ca4526 | ||
|
|
814b68b4a9 | ||
|
|
bfa0eefa32 | ||
|
|
caa04aad8b | ||
|
|
cc476d59d1 | ||
|
|
3dc06ea9bd | ||
|
|
c311c1ba5d | ||
|
|
11a86d9c06 | ||
|
|
037bec189c | ||
|
|
9b164dd185 | ||
|
|
4ee124bf91 | ||
|
|
e70be4f155 | ||
|
|
8314554eb5 | ||
|
|
b7c03dc27e | ||
|
|
6621f6ddb2 | ||
|
|
c7f2f609a9 | ||
|
|
579c22cf5d | ||
|
|
33e08daf17 | ||
|
|
24257de8a6 | ||
|
|
4c3de3a1ec | ||
|
|
42ac1848dd | ||
|
|
3920ad442d | ||
|
|
e7970c66ee | ||
|
|
c7b96be683 | ||
|
|
0623a87d70 | ||
|
|
c5f89eaa9a | ||
|
|
1fbe550285 | ||
|
|
b3724f04c4 | ||
|
|
2f58a0e188 | ||
|
|
6f1e0e4d17 | ||
|
|
7dd8ddc3eb | ||
|
|
222d7170f7 | ||
|
|
3137cff4a5 | ||
|
|
b45b1d7795 | ||
|
|
e1030b186e | ||
|
|
334c8c2962 | ||
|
|
bdaa3f693a | ||
|
|
be13fb0391 | ||
|
|
c120901744 | ||
|
|
badb019d38 | ||
|
|
d85a393bcd | ||
|
|
53d27b771d | ||
|
|
2c0931da46 | ||
|
|
8224a81ab6 | ||
|
|
98442294ed | ||
|
|
a3d164fde8 | ||
|
|
9dc987b28b | ||
|
|
72c0d9df26 | ||
|
|
cfbce54638 | ||
|
|
6979aa1013 | ||
|
|
ea60525988 | ||
|
|
9da6fb39ae | ||
|
|
83b9a17277 | ||
|
|
f0445b74d1 | ||
|
|
ba52eef257 | ||
|
|
c13ce2a5c0 | ||
|
|
d2463f41b5 | ||
|
|
eadb343292 | ||
|
|
e7208622f7 | ||
|
|
fbae611406 | ||
|
|
34027bc589 | ||
|
|
f2eef37599 | ||
|
|
dc3ed65c18 | ||
|
|
43092e6e11 | ||
|
|
878e77acab | ||
|
|
63827db60d | ||
|
|
41108358f6 | ||
|
|
f970fc8bea | ||
|
|
db461a4c6c | ||
|
|
fe33d5fb53 | ||
|
|
64c4d4197b | ||
|
|
ed7bab76f2 | ||
|
|
f338802642 | ||
|
|
445595f942 | ||
|
|
66ce617bea | ||
|
|
3e07a5acff | ||
|
|
df1e739194 | ||
|
|
2def5a0768 | ||
|
|
2746bccef3 | ||
|
|
f0c3dfe3b3 | ||
|
|
680d65114c | ||
|
|
4f8f929340 | ||
|
|
3629e8e480 | ||
|
|
ff48c2e1da | ||
|
|
1bd13b50f1 | ||
|
|
e0a075ecca | ||
|
|
40eb0cc240 | ||
|
|
954f6cc8fc | ||
|
|
19884294bc | ||
|
|
e6b63fd70a | ||
|
|
8a84ede164 | ||
|
|
d79b7b2d97 | ||
|
|
844f879f63 | ||
|
|
cf789d2c91 | ||
|
|
4936ef5c30 | ||
|
|
f006718e56 | ||
|
|
d720776918 | ||
|
|
f43398dd44 | ||
|
|
43ad451940 | ||
|
|
94c099caa1 | ||
|
|
3e6f680df2 | ||
|
|
2e5d955e77 | ||
|
|
7733faf6c4 | ||
|
|
b053a88993 | ||
|
|
7c77c46ede | ||
|
|
da6937f474 | ||
|
|
cbb496da79 | ||
|
|
43e66a6089 | ||
|
|
5acbd7e762 | ||
|
|
febae345fc | ||
|
|
28bdbbca1a | ||
|
|
4b896fa22e | ||
|
|
a25ee49edd | ||
|
|
695b5b2fb2 | ||
|
|
9b77af540b | ||
|
|
7829138fb2 | ||
|
|
7e7a4fc665 | ||
|
|
34e6112fea | ||
|
|
0c759d0075 | ||
|
|
5b7c1aad9e | ||
|
|
865c453547 | ||
|
|
c0b14021b7 | ||
|
|
6cc192680a | ||
|
|
3dd6107e76 | ||
|
|
47297f7c26 | ||
|
|
967aa0629e | ||
|
|
04b20a79b9 | ||
|
|
d60b09cafc | ||
|
|
1e3ea13323 | ||
|
|
4c8c48cde9 | ||
|
|
c8e50276e8 | ||
|
|
1e6419a63f | ||
|
|
875827f59b | ||
|
|
c4e08261b5 | ||
|
|
70f679d2fa | ||
|
|
25c43fa439 | ||
|
|
ec1c6325d4 | ||
|
|
309ff6be38 | ||
|
|
02d368df05 | ||
|
|
4ee1fe2ca4 | ||
|
|
4ff2ae9f4e | ||
|
|
7a729e8f16 | ||
|
|
3c7fd95617 | ||
|
|
2dd29bee25 | ||
|
|
259a508d8a | ||
|
|
1963064369 | ||
|
|
a101ce803c | ||
|
|
fc0c7936d1 | ||
|
|
2d2add5b44 | ||
|
|
58fd5ddbaf |
@@ -33,4 +33,14 @@ export const packageOptions = {
|
||||
packageName: 'mermaid-layout-elk',
|
||||
file: 'layouts.ts',
|
||||
},
|
||||
'mermaid-layout-tidy-tree': {
|
||||
name: 'mermaid-layout-tidy-tree',
|
||||
packageName: 'mermaid-layout-tidy-tree',
|
||||
file: 'index.ts',
|
||||
},
|
||||
examples: {
|
||||
name: 'mermaid-examples',
|
||||
packageName: 'examples',
|
||||
file: 'index.ts',
|
||||
},
|
||||
} as const satisfies Record<string, PackageOptions>;
|
||||
|
||||
@@ -10,13 +10,16 @@ const buildType = (packageName: string) => {
|
||||
console.log(out.toString());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e.stdout.length > 0) {
|
||||
console.error(e.stdout.toString());
|
||||
}
|
||||
if (e.stderr.length > 0) {
|
||||
console.error(e.stderr.toString());
|
||||
}
|
||||
// Exit the build process if we are in CI
|
||||
if (process.env.CI) {
|
||||
throw new Error(`Failed to build types for ${packageName}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
5
.changeset/brave-baths-behave.md
Normal file
5
.changeset/brave-baths-behave.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Prevent HTML tags from being escaped in sandbox label rendering
|
||||
5
.changeset/brave-memes-flash.md
Normal file
5
.changeset/brave-memes-flash.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Support edge animation in hand drawn look
|
||||
5
.changeset/busy-mirrors-try.md
Normal file
5
.changeset/busy-mirrors-try.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Resolved parsing error where direction TD was not recognized within subgraphs
|
||||
5
.changeset/chatty-insects-dream.md
Normal file
5
.changeset/chatty-insects-dream.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(treemap): Fixed treemap classDef style application to properly apply user-defined styles
|
||||
5
.changeset/chilly-words-march.md
Normal file
5
.changeset/chilly-words-march.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Correct viewBox casing and make SVGs responsive
|
||||
5
.changeset/curly-apes-prove.md
Normal file
5
.changeset/curly-apes-prove.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Improve participant parsing and prevent recursive loops on invalid syntax
|
||||
5
.changeset/lazy-brooms-battle.md
Normal file
5
.changeset/lazy-brooms-battle.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
feat: add alias support for new participant syntax of sequence diagrams
|
||||
5
.changeset/loud-results-melt.md
Normal file
5
.changeset/loud-results-melt.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Add half-arrowheads (solid & stick) and central connection support
|
||||
5
.changeset/short-seals-sort.md
Normal file
5
.changeset/short-seals-sort.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: allow to put notes in namespaces on classDiagram
|
||||
5
.changeset/slow-lemons-know.md
Normal file
5
.changeset/slow-lemons-know.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@mermaid': patch
|
||||
---
|
||||
|
||||
fix: Mindmap breaking in ELK layout
|
||||
5
.changeset/sweet-games-build.md
Normal file
5
.changeset/sweet-games-build.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names
|
||||
5
.changeset/ten-plums-bet.md
Normal file
5
.changeset/ten-plums-bet.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Support ComponentQueue_Ext to prevent parsing error
|
||||
5
.changeset/tricky-rivers-stand.md
Normal file
5
.changeset/tricky-rivers-stand.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: validate dates and tick interval to prevent UI freeze/crash in gantt diagramtype
|
||||
5
.changeset/wide-lines-trade.md
Normal file
5
.changeset/wide-lines-trade.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Mindmap rendering issue when the number of Level 2 nodes exceeds 11
|
||||
@@ -1,3 +1,5 @@
|
||||
!viewbox
|
||||
# It should be viewBox
|
||||
# This file contains coding related terms
|
||||
ALPHANUM
|
||||
antiscript
|
||||
@@ -88,6 +90,7 @@ NODIR
|
||||
NSTR
|
||||
outdir
|
||||
Qcontrolx
|
||||
QSTR
|
||||
reinit
|
||||
rels
|
||||
reqs
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Ashish Jain
|
||||
cpettitt
|
||||
Dong Cai
|
||||
fourcube
|
||||
knsv
|
||||
Knut Sveidqvist
|
||||
Nikolay Rozhkov
|
||||
|
||||
@@ -64,6 +64,7 @@ rscratch
|
||||
shiki
|
||||
Slidev
|
||||
sparkline
|
||||
speccharts
|
||||
sphinxcontrib
|
||||
ssim
|
||||
stylis
|
||||
|
||||
@@ -5,8 +5,10 @@ bmatrix
|
||||
braintree
|
||||
catmull
|
||||
compositTitleSize
|
||||
cose
|
||||
curv
|
||||
doublecircle
|
||||
elem
|
||||
elems
|
||||
gantt
|
||||
gitgraph
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
BRANDES
|
||||
Buzan
|
||||
circo
|
||||
handDrawn
|
||||
KOEPF
|
||||
|
||||
53
.esbuild/dev-explorer/ARCHITECTURE.md
Normal file
53
.esbuild/dev-explorer/ARCHITECTURE.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Dev Explorer frontend: architecture + debugging notes
|
||||
|
||||
## Root cause of the “CSS changes do nothing” problem
|
||||
|
||||
The page loads `/dev/styles.css`, but **document-level CSS does not apply through a shadow DOM boundary**.
|
||||
|
||||
Historically, `dev-explorer-app` was a `LitElement` using Lit’s default `shadowRoot`, while the rest of the UI used light DOM. That meant:
|
||||
|
||||
- The browser showed the _right classes_ (`card`, `card-folder`, `card-file`) in Elements panel.
|
||||
- `/dev/styles.css` was clearly being served/updated.
|
||||
- Yet computed styles for `.card` looked like UA defaults because the selector never matched across the shadow root.
|
||||
|
||||
Fix: make `dev-explorer-app` light DOM too (`createRenderRoot() { return this; }`), so `/dev/styles.css` reliably styles the whole UI.
|
||||
|
||||
## Debugging traps (and fast detection)
|
||||
|
||||
- **Shadow DOM trap**
|
||||
- Symptom: “CSS is loaded but doesn’t apply”, especially for simple class selectors.
|
||||
- Fast check:
|
||||
- DevTools console: `document.querySelector('dev-explorer-app')?.shadowRoot`
|
||||
- If non-null, global CSS won’t style inside it.
|
||||
- Or: right-click an element you expect styled → “Reveal in Elements” → see if it’s under `#shadow-root`.
|
||||
|
||||
- **“Light DOM child inside shadow DOM parent” trap**
|
||||
- Even if a child component uses `createRenderRoot() { return this; }`,
|
||||
if it’s _rendered inside the parent’s shadow root_, it’s still effectively in shadow for document styles.
|
||||
|
||||
- **Dev loop trap (CSS-only changes don’t trigger reload)**
|
||||
- The server watches TypeScript bundle inputs + `.mmd` files; static `/dev/styles.css` previously didn’t emit SSE reload events.
|
||||
- That makes CSS changes look flaky unless you manually refresh.
|
||||
- Fix: watch `.esbuild/dev-explorer/public/**/*` and emit SSE on changes.
|
||||
|
||||
- **Caching trap (less common here, but real)**
|
||||
- If a query param is constant (`?v=3`) and you don’t reload, the browser can keep a cached stylesheet.
|
||||
- Fast check: DevTools → Network → disable cache + hard reload; or check “(from disk cache)” on the CSS request.
|
||||
|
||||
## Styling strategy recommendation (pragmatic)
|
||||
|
||||
For a dev-only explorer, keep it simple:
|
||||
|
||||
- **Light DOM everywhere**
|
||||
- **One stylesheet**: `.esbuild/dev-explorer/public/styles.css` served as `/dev/styles.css`
|
||||
- **Scoped selectors** under `dev-explorer-app` to avoid generic class collisions (`.header`, `.content`, etc.)
|
||||
|
||||
If you later _want_ Shadow DOM isolation, do it deliberately:
|
||||
|
||||
- Put UI styles in Lit `static styles` or adopt a `CSSStyleSheet` into `this.renderRoot.adoptedStyleSheets`.
|
||||
- Avoid relying on document CSS selectors for component internals.
|
||||
|
||||
## Shoelace integration notes
|
||||
|
||||
- Current setup is correct for dev: `setBasePath('/dev/vendor/shoelace')` and `registerIconLibrary(...)`.
|
||||
- Prefer theming via CSS variables (Shoelace tokens) rather than overriding internal parts everywhere.
|
||||
187
.esbuild/dev-explorer/console-panel.ts
Normal file
187
.esbuild/dev-explorer/console-panel.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import { LitElement, html } from 'lit';
|
||||
|
||||
import '@shoelace-style/shoelace/dist/components/input/input.js';
|
||||
|
||||
export type LogLevel = 'info' | 'warn' | 'error';
|
||||
|
||||
export type LogEntry = {
|
||||
ts: number;
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
};
|
||||
|
||||
function formatTs(ts: number) {
|
||||
const d = new Date(ts);
|
||||
return (
|
||||
d.toLocaleTimeString(undefined, { hour12: false }) +
|
||||
'.' +
|
||||
String(d.getMilliseconds()).padStart(3, '0')
|
||||
);
|
||||
}
|
||||
|
||||
function levelVariant(level: LogLevel) {
|
||||
switch (level) {
|
||||
case 'error':
|
||||
return 'danger';
|
||||
case 'warn':
|
||||
return 'warning';
|
||||
default:
|
||||
return 'neutral';
|
||||
}
|
||||
}
|
||||
|
||||
type DisplayLevel = 'debug' | LogLevel;
|
||||
|
||||
function displayLevel(entry: LogEntry): DisplayLevel {
|
||||
// Mermaid often emits debug lines through console.log/info with a marker.
|
||||
if (entry.message.includes(': DEBUG :')) return 'debug';
|
||||
return entry.level;
|
||||
}
|
||||
|
||||
function displayVariant(level: DisplayLevel) {
|
||||
switch (level) {
|
||||
case 'error':
|
||||
return 'danger';
|
||||
case 'warn':
|
||||
return 'warning';
|
||||
case 'debug':
|
||||
return 'success';
|
||||
default:
|
||||
return 'neutral';
|
||||
}
|
||||
}
|
||||
|
||||
export class DevConsolePanel extends LitElement {
|
||||
static properties = {
|
||||
logs: { state: true },
|
||||
showInfo: { state: true },
|
||||
showWarn: { state: true },
|
||||
showError: { state: true },
|
||||
showDebug: { state: true },
|
||||
filterText: { state: true },
|
||||
};
|
||||
|
||||
declare logs: LogEntry[];
|
||||
declare showInfo: boolean;
|
||||
declare showWarn: boolean;
|
||||
declare showError: boolean;
|
||||
declare showDebug: boolean;
|
||||
declare filterText: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.logs = [];
|
||||
this.showInfo = true;
|
||||
this.showWarn = true;
|
||||
this.showError = true;
|
||||
this.showDebug = true;
|
||||
this.filterText = '';
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.logs = [];
|
||||
}
|
||||
|
||||
append(entry: LogEntry) {
|
||||
this.logs = [...this.logs, entry];
|
||||
}
|
||||
|
||||
async copyVisible() {
|
||||
const visible = this.filteredLogs();
|
||||
const text = visible
|
||||
.map((l) => `[${formatTs(l.ts)}] ${l.level.toUpperCase()} ${l.message}`)
|
||||
.join('\n');
|
||||
await navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
filteredLogs() {
|
||||
const q = this.filterText.trim().toLowerCase();
|
||||
return this.logs.filter((l) => {
|
||||
const isDebugLine = l.message.includes(': DEBUG :');
|
||||
// Treat debug-marked lines as their own independent toggle, since Mermaid often routes them through
|
||||
// console.log/info with a marker rather than a distinct "debug" level.
|
||||
if (isDebugLine && !this.showDebug) return false;
|
||||
|
||||
if (!isDebugLine) {
|
||||
const levelOk =
|
||||
l.level === 'info' ? this.showInfo : l.level === 'warn' ? this.showWarn : this.showError;
|
||||
if (!levelOk) return false;
|
||||
}
|
||||
|
||||
if (!q) return true;
|
||||
return l.message.toLowerCase().includes(q);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const visible = this.filteredLogs();
|
||||
return html`
|
||||
<div class="console">
|
||||
<div class="console-toolbar">
|
||||
<div class="spacer"></div>
|
||||
<sl-input
|
||||
size="small"
|
||||
placeholder="filter…"
|
||||
clearable
|
||||
value=${this.filterText}
|
||||
@sl-input=${(e: any) => (this.filterText = e.target.value ?? '')}
|
||||
></sl-input>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showDebug}
|
||||
@sl-change=${(e: any) => (this.showDebug = e.target.checked)}
|
||||
>debug</sl-checkbox
|
||||
>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showInfo}
|
||||
@sl-change=${(e: any) => (this.showInfo = e.target.checked)}
|
||||
>info</sl-checkbox
|
||||
>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showWarn}
|
||||
@sl-change=${(e: any) => (this.showWarn = e.target.checked)}
|
||||
>warn</sl-checkbox
|
||||
>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showError}
|
||||
@sl-change=${(e: any) => (this.showError = e.target.checked)}
|
||||
>error</sl-checkbox
|
||||
>
|
||||
<sl-button size="small" variant="default" @click=${() => void this.copyVisible()}>
|
||||
<sl-icon slot="prefix" name="clipboard"></sl-icon>
|
||||
Copy
|
||||
</sl-button>
|
||||
<sl-button size="small" variant="default" @click=${() => this.clear()}>
|
||||
<sl-icon slot="prefix" name="trash"></sl-icon>
|
||||
Clear
|
||||
</sl-button>
|
||||
</div>
|
||||
<div class="console-body">
|
||||
${visible.length === 0
|
||||
? html`<div class="empty">No logs yet.</div>`
|
||||
: visible.map((l) => {
|
||||
const lvl = displayLevel(l);
|
||||
return html`
|
||||
<div class="logline">
|
||||
<div class="logmeta">
|
||||
<sl-badge variant=${displayVariant(lvl)}>${lvl}</sl-badge>
|
||||
<span class="path">${formatTs(l.ts)}</span>
|
||||
</div>
|
||||
<div>${l.message}</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-console-panel', DevConsolePanel);
|
||||
551
.esbuild/dev-explorer/diagram-viewer.ts
Normal file
551
.esbuild/dev-explorer/diagram-viewer.ts
Normal file
@@ -0,0 +1,551 @@
|
||||
import { LitElement, html, nothing } from 'lit';
|
||||
|
||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
|
||||
import '@shoelace-style/shoelace/dist/components/select/select.js';
|
||||
import '@shoelace-style/shoelace/dist/components/option/option.js';
|
||||
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
|
||||
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
|
||||
|
||||
import './console-panel';
|
||||
import type { LogEntry, LogLevel } from './console-panel';
|
||||
|
||||
type MermaidIife = {
|
||||
initialize: (config: Record<string, unknown>) => void | Promise<void>;
|
||||
render: (
|
||||
id: string,
|
||||
text: string,
|
||||
container?: Element
|
||||
) => Promise<{ svg: string; bindFunctions?: (el: Element) => void }>;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
mermaid?: MermaidIife;
|
||||
mermaidReady?: Promise<MermaidIife>;
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyArgs(args: unknown[]) {
|
||||
// Mermaid's internal logger frequently uses console formatting like:
|
||||
// console.log('%c...message...', 'color: lightgreen', ...)
|
||||
// For the log panel we want the human text, not the formatting tokens/styles.
|
||||
let normalized = [...args];
|
||||
if (typeof normalized[0] === 'string') {
|
||||
const fmt = normalized[0];
|
||||
const cssCount = (fmt.match(/%c/g) ?? []).length;
|
||||
if (cssCount > 0) {
|
||||
normalized[0] = fmt.replaceAll('%c', '');
|
||||
// Drop the corresponding CSS args that follow the format string.
|
||||
normalized.splice(1, cssCount);
|
||||
}
|
||||
}
|
||||
|
||||
return normalized
|
||||
.map((a) => {
|
||||
if (typeof a === 'string') return a;
|
||||
if (a instanceof Error) return a.stack ?? a.message;
|
||||
try {
|
||||
return JSON.stringify(a);
|
||||
} catch {
|
||||
return String(a);
|
||||
}
|
||||
})
|
||||
.join(' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
type MermaidTheme = 'default' | 'dark' | 'forest' | 'neutral' | 'base';
|
||||
type MermaidLayout = 'dagre' | 'elk';
|
||||
type MermaidLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
|
||||
const DEFAULT_THEME: MermaidTheme = 'default';
|
||||
const DEFAULT_LAYOUT: MermaidLayout = 'dagre';
|
||||
const DEFAULT_MERMAID_LOG_LEVEL: MermaidLogLevel = 'warn';
|
||||
|
||||
function readUrlParam(name: string) {
|
||||
try {
|
||||
return new URL(window.location.href).searchParams.get(name);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setUrlParams(pairs: Record<string, string | null | undefined>) {
|
||||
const url = new URL(window.location.href);
|
||||
for (const [k, v] of Object.entries(pairs)) {
|
||||
if (!v) url.searchParams.delete(k);
|
||||
else url.searchParams.set(k, v);
|
||||
}
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
function readStorage(key: string) {
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeStorage(key: string, value: string) {
|
||||
try {
|
||||
localStorage.setItem(key, value);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function isTheme(v: unknown): v is MermaidTheme {
|
||||
return v === 'default' || v === 'dark' || v === 'forest' || v === 'neutral' || v === 'base';
|
||||
}
|
||||
|
||||
function isLayout(v: unknown): v is MermaidLayout {
|
||||
return v === 'dagre' || v === 'elk';
|
||||
}
|
||||
|
||||
function isMermaidLogLevel(v: unknown): v is MermaidLogLevel {
|
||||
return (
|
||||
v === 'trace' || v === 'debug' || v === 'info' || v === 'warn' || v === 'error' || v === 'fatal'
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeLayout(v: unknown): MermaidLayout | null {
|
||||
// Back-compat:
|
||||
// - older UI used `renderer=dagre-d3|dagre-wrapper|elk`
|
||||
// - new UI uses `layout=dagre|elk`
|
||||
if (v === 'dagre' || v === 'elk') return v;
|
||||
if (v === 'dagre-d3' || v === 'dagre-wrapper') return 'dagre';
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseBoolean(v: unknown): boolean | null {
|
||||
if (typeof v !== 'string') return null;
|
||||
const s = v.trim().toLowerCase();
|
||||
if (['1', 'true', 'yes', 'on'].includes(s)) return true;
|
||||
if (['0', 'false', 'no', 'off'].includes(s)) return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
export class DevDiagramViewer extends LitElement {
|
||||
static properties = {
|
||||
filePath: { type: String },
|
||||
sseToken: { type: Number },
|
||||
theme: { state: true },
|
||||
layout: { state: true },
|
||||
mermaidLogLevel: { state: true },
|
||||
useMaxWidth: { state: true },
|
||||
loading: { state: true },
|
||||
error: { state: true },
|
||||
source: { state: true },
|
||||
svg: { state: true },
|
||||
};
|
||||
|
||||
declare filePath: string;
|
||||
declare sseToken: number;
|
||||
declare theme: MermaidTheme;
|
||||
declare layout: MermaidLayout;
|
||||
declare mermaidLogLevel: MermaidLogLevel;
|
||||
declare useMaxWidth: boolean;
|
||||
declare loading: boolean;
|
||||
declare error: string;
|
||||
declare source: string;
|
||||
declare svg: string;
|
||||
|
||||
#renderSeq = 0;
|
||||
#consolePatched = false;
|
||||
#originalConsole?: {
|
||||
log: typeof console.log;
|
||||
info: typeof console.info;
|
||||
debug: typeof console.debug;
|
||||
warn: typeof console.warn;
|
||||
error: typeof console.error;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const themeParam = readUrlParam('theme');
|
||||
const layoutParam = readUrlParam('layout');
|
||||
const rendererParam = readUrlParam('renderer'); // legacy
|
||||
const logParam = readUrlParam('logLevel');
|
||||
const useMaxWidthParam = readUrlParam('useMaxWidth');
|
||||
|
||||
const storedTheme = readStorage('devExplorer.viewer.theme');
|
||||
const storedLayout = readStorage('devExplorer.viewer.layout');
|
||||
const storedRenderer = readStorage('devExplorer.viewer.renderer'); // legacy
|
||||
const storedLog = readStorage('devExplorer.viewer.logLevel');
|
||||
const storedUseMaxWidth = readStorage('devExplorer.viewer.useMaxWidth');
|
||||
|
||||
this.theme = isTheme(themeParam)
|
||||
? themeParam
|
||||
: isTheme(storedTheme)
|
||||
? storedTheme
|
||||
: DEFAULT_THEME;
|
||||
this.layout =
|
||||
normalizeLayout(layoutParam) ??
|
||||
normalizeLayout(rendererParam) ??
|
||||
normalizeLayout(storedLayout) ??
|
||||
normalizeLayout(storedRenderer) ??
|
||||
DEFAULT_LAYOUT;
|
||||
this.mermaidLogLevel = isMermaidLogLevel(logParam)
|
||||
? logParam
|
||||
: isMermaidLogLevel(storedLog)
|
||||
? storedLog
|
||||
: DEFAULT_MERMAID_LOG_LEVEL;
|
||||
|
||||
this.useMaxWidth = parseBoolean(useMaxWidthParam) ?? parseBoolean(storedUseMaxWidth) ?? true;
|
||||
|
||||
this.filePath = '';
|
||||
this.sseToken = 0;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.source = '';
|
||||
this.svg = '';
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.#installConsoleCapture();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.#restoreConsoleCapture();
|
||||
}
|
||||
|
||||
updated(changed: Map<string, unknown>) {
|
||||
if (changed.has('filePath')) {
|
||||
void this.#loadAndRender();
|
||||
} else if (changed.has('sseToken')) {
|
||||
// On rebuild events, re-fetch + re-render the currently open diagram.
|
||||
if (this.filePath) void this.#loadAndRender();
|
||||
} else if (
|
||||
changed.has('theme') ||
|
||||
changed.has('layout') ||
|
||||
changed.has('mermaidLogLevel') ||
|
||||
changed.has('useMaxWidth')
|
||||
) {
|
||||
// Re-render the currently loaded diagram with the new config without refetching.
|
||||
if (this.source) void this.#renderCurrentSource();
|
||||
}
|
||||
}
|
||||
|
||||
#back() {
|
||||
this.dispatchEvent(new CustomEvent('back', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
#persistSettings() {
|
||||
writeStorage('devExplorer.viewer.theme', this.theme);
|
||||
writeStorage('devExplorer.viewer.layout', this.layout);
|
||||
writeStorage('devExplorer.viewer.logLevel', this.mermaidLogLevel);
|
||||
writeStorage('devExplorer.viewer.useMaxWidth', String(this.useMaxWidth));
|
||||
setUrlParams({
|
||||
theme: this.theme,
|
||||
layout: this.layout,
|
||||
renderer: null, // drop legacy param
|
||||
logLevel: this.mermaidLogLevel,
|
||||
useMaxWidth: this.useMaxWidth ? '1' : '0',
|
||||
});
|
||||
}
|
||||
|
||||
#syncConsolePanelFilters() {
|
||||
const panel = this.querySelector('dev-console-panel') as any;
|
||||
if (!panel) return;
|
||||
// This is intentionally opinionated: less noise by default as logLevel increases.
|
||||
if (
|
||||
this.mermaidLogLevel === 'trace' ||
|
||||
this.mermaidLogLevel === 'debug' ||
|
||||
this.mermaidLogLevel === 'info'
|
||||
) {
|
||||
panel.showInfo = true;
|
||||
panel.showWarn = true;
|
||||
panel.showError = true;
|
||||
return;
|
||||
}
|
||||
if (this.mermaidLogLevel === 'warn') {
|
||||
panel.showInfo = false;
|
||||
panel.showWarn = true;
|
||||
panel.showError = true;
|
||||
return;
|
||||
}
|
||||
// error / fatal
|
||||
panel.showInfo = false;
|
||||
panel.showWarn = false;
|
||||
panel.showError = true;
|
||||
}
|
||||
|
||||
#appendLog(entry: LogEntry) {
|
||||
const panel = this.querySelector('dev-console-panel') as any;
|
||||
panel?.append?.(entry);
|
||||
}
|
||||
|
||||
#installConsoleCapture() {
|
||||
if (this.#consolePatched) return;
|
||||
this.#consolePatched = true;
|
||||
|
||||
this.#originalConsole = {
|
||||
log: console.log,
|
||||
info: console.info,
|
||||
debug: console.debug,
|
||||
warn: console.warn,
|
||||
error: console.error,
|
||||
};
|
||||
|
||||
const capture = (level: LogLevel, args: unknown[]) => {
|
||||
this.#appendLog({
|
||||
ts: Date.now(),
|
||||
level,
|
||||
message: stringifyArgs(args),
|
||||
});
|
||||
};
|
||||
|
||||
// Mermaid uses its own logger which routes to console.info/debug/warn/error.
|
||||
// Capture those too (map debug/info/log -> panel "info").
|
||||
console.log = (...args) => {
|
||||
capture('info', args);
|
||||
this.#originalConsole!.log.apply(console, args as any);
|
||||
};
|
||||
console.info = (...args) => {
|
||||
capture('info', args);
|
||||
this.#originalConsole!.info.apply(console, args as any);
|
||||
};
|
||||
console.debug = (...args) => {
|
||||
capture('info', args);
|
||||
this.#originalConsole!.debug.apply(console, args as any);
|
||||
};
|
||||
console.warn = (...args) => {
|
||||
capture('warn', args);
|
||||
this.#originalConsole!.warn.apply(console, args as any);
|
||||
};
|
||||
console.error = (...args) => {
|
||||
capture('error', args);
|
||||
this.#originalConsole!.error.apply(console, args as any);
|
||||
};
|
||||
}
|
||||
|
||||
#restoreConsoleCapture() {
|
||||
if (!this.#consolePatched) return;
|
||||
this.#consolePatched = false;
|
||||
if (!this.#originalConsole) return;
|
||||
console.log = this.#originalConsole.log;
|
||||
console.info = this.#originalConsole.info;
|
||||
console.debug = this.#originalConsole.debug;
|
||||
console.warn = this.#originalConsole.warn;
|
||||
console.error = this.#originalConsole.error;
|
||||
this.#originalConsole = undefined;
|
||||
}
|
||||
|
||||
#clearLogs() {
|
||||
const panel = this.querySelector('dev-console-panel') as any;
|
||||
panel?.clear?.();
|
||||
}
|
||||
|
||||
async #fetchSource() {
|
||||
const url = new URL('/dev/api/file', window.location.origin);
|
||||
url.searchParams.set('path', this.filePath);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return await res.text();
|
||||
}
|
||||
|
||||
async #loadAndRender() {
|
||||
const seq = ++this.#renderSeq;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.svg = '';
|
||||
this.#clearLogs();
|
||||
this.#syncConsolePanelFilters();
|
||||
|
||||
try {
|
||||
const source = await this.#fetchSource();
|
||||
if (seq !== this.#renderSeq) return;
|
||||
this.source = source;
|
||||
await this.#renderMermaid(source);
|
||||
} catch (e) {
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async #renderCurrentSource() {
|
||||
const seq = ++this.#renderSeq;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.svg = '';
|
||||
this.#clearLogs();
|
||||
this.#syncConsolePanelFilters();
|
||||
try {
|
||||
const source = this.source;
|
||||
if (!source) return;
|
||||
if (seq !== this.#renderSeq) return;
|
||||
await this.#renderMermaid(source);
|
||||
} catch (e) {
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async #renderMermaid(text: string) {
|
||||
const m = (await window.mermaidReady?.catch(() => undefined)) ?? window.mermaid;
|
||||
if (!m) {
|
||||
throw new Error(
|
||||
'window.mermaid is not available (did /mermaid.esm.mjs load and did the bootstrap set window.mermaid?)'
|
||||
);
|
||||
}
|
||||
|
||||
const initConfig = {
|
||||
startOnLoad: false,
|
||||
securityLevel: 'strict',
|
||||
theme: this.theme,
|
||||
layout: this.layout,
|
||||
logLevel: this.mermaidLogLevel,
|
||||
flowchart: {
|
||||
useMaxWidth: this.useMaxWidth,
|
||||
},
|
||||
};
|
||||
|
||||
// Debugging aid: log exactly what we are about to initialize/render with.
|
||||
// Do it *before* initialize so detector issues can be correlated.
|
||||
const previewLimit = 4000;
|
||||
const preview =
|
||||
text.length > previewLimit
|
||||
? `${text.slice(0, previewLimit)}\n… (${text.length - previewLimit} more chars)`
|
||||
: text;
|
||||
console.log('[dev-explorer] mermaid.initialize config:', initConfig);
|
||||
console.log('[dev-explorer] diagram source preview:\n' + preview);
|
||||
|
||||
// Keep it deterministic-ish between reloads.
|
||||
await m.initialize(initConfig);
|
||||
|
||||
const id = `dev-explorer-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
const { svg, bindFunctions } = await m.render(id, text);
|
||||
this.svg = svg;
|
||||
// Allow mermaid to attach event handlers (e.g. links).
|
||||
await this.updateComplete;
|
||||
// If the page ever ended up scrolled down due to a previous oversized render, snap back to top.
|
||||
// (We intentionally removed vertical scrollbars in the viewer.)
|
||||
try {
|
||||
window.scrollTo(0, 0);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
const container = this.querySelector('.diagram-inner');
|
||||
if (container && bindFunctions) bindFunctions(container);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="header">
|
||||
<sl-button size="small" variant="default" @click=${() => this.#back()}>
|
||||
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
|
||||
Back
|
||||
</sl-button>
|
||||
<div style="min-width: 0;">
|
||||
<div class="title">Diagram</div>
|
||||
<div class="path">${this.filePath}</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="viewer-controls">
|
||||
<div class="control">
|
||||
<span class="label">Theme</span>
|
||||
<sl-select
|
||||
size="small"
|
||||
value=${this.theme}
|
||||
@sl-change=${(e: any) => {
|
||||
const v = e.target?.value;
|
||||
if (isTheme(v)) {
|
||||
this.theme = v;
|
||||
this.#persistSettings();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<sl-option value="default">default</sl-option>
|
||||
<sl-option value="dark">dark</sl-option>
|
||||
<sl-option value="forest">forest</sl-option>
|
||||
<sl-option value="neutral">neutral</sl-option>
|
||||
<sl-option value="base">base</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<span class="label">Layout</span>
|
||||
<sl-select
|
||||
size="small"
|
||||
value=${this.layout}
|
||||
@sl-change=${(e: any) => {
|
||||
const v = e.target?.value;
|
||||
if (isLayout(v)) {
|
||||
this.layout = v;
|
||||
this.#persistSettings();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<sl-option value="dagre">dagre</sl-option>
|
||||
<sl-option value="elk">elk</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<span class="label">Log</span>
|
||||
<sl-select
|
||||
size="small"
|
||||
value=${this.mermaidLogLevel}
|
||||
@sl-change=${(e: any) => {
|
||||
const v = e.target?.value;
|
||||
if (isMermaidLogLevel(v)) {
|
||||
this.mermaidLogLevel = v;
|
||||
this.#persistSettings();
|
||||
this.#syncConsolePanelFilters();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<sl-option value="trace">trace</sl-option>
|
||||
<sl-option value="debug">debug</sl-option>
|
||||
<sl-option value="info">info</sl-option>
|
||||
<sl-option value="warn">warn</sl-option>
|
||||
<sl-option value="error">error</sl-option>
|
||||
<sl-option value="fatal">fatal</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.useMaxWidth}
|
||||
@sl-change=${(e: any) => {
|
||||
this.useMaxWidth = Boolean(e.target?.checked);
|
||||
this.#persistSettings();
|
||||
}}
|
||||
>useMaxWidth</sl-checkbox
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
${this.loading ? html`<div class="subtle">rendering…</div>` : nothing}
|
||||
</div>
|
||||
|
||||
${this.error
|
||||
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
|
||||
: nothing}
|
||||
|
||||
<div class="content">
|
||||
<sl-split-panel position="75" style="height: 100%;">
|
||||
<div slot="start" class="diagram">
|
||||
<div class="diagram-inner" data-theme=${this.theme} .innerHTML=${this.svg}></div>
|
||||
</div>
|
||||
<div slot="end" style="height: 100%;">
|
||||
<dev-console-panel></dev-console-panel>
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-diagram-viewer', DevDiagramViewer);
|
||||
143
.esbuild/dev-explorer/explorer-app.ts
Normal file
143
.esbuild/dev-explorer/explorer-app.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { LitElement, html, nothing } from 'lit';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
|
||||
import { registerIconLibrary } from '@shoelace-style/shoelace/dist/utilities/icon-library.js';
|
||||
|
||||
import '@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js';
|
||||
import '@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js';
|
||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
|
||||
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
|
||||
import '@shoelace-style/shoelace/dist/components/badge/badge.js';
|
||||
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
|
||||
|
||||
import './file-explorer';
|
||||
import './diagram-viewer';
|
||||
|
||||
type ViewMode = 'explorer' | 'viewer';
|
||||
|
||||
function getInitialStateFromUrl() {
|
||||
const url = new URL(window.location.href);
|
||||
const dir = url.searchParams.get('path') ?? '';
|
||||
const file = url.searchParams.get('file') ?? '';
|
||||
return { dir, file };
|
||||
}
|
||||
|
||||
function setUrlState({ dir, file }: { dir: string; file: string }) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete('path');
|
||||
url.searchParams.delete('file');
|
||||
if (dir) url.searchParams.set('path', dir);
|
||||
if (file) url.searchParams.set('file', file);
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
export class DevExplorerApp extends LitElement {
|
||||
static properties = {
|
||||
mode: { state: true },
|
||||
dirPath: { state: true },
|
||||
lastDirPath: { state: true },
|
||||
filePath: { state: true },
|
||||
sseToken: { state: true },
|
||||
};
|
||||
|
||||
declare mode: ViewMode;
|
||||
declare dirPath: string;
|
||||
declare lastDirPath: string;
|
||||
declare filePath: string;
|
||||
declare sseToken: number;
|
||||
|
||||
#events?: EventSource;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const { dir, file } = getInitialStateFromUrl();
|
||||
this.dirPath = dir;
|
||||
this.lastDirPath = dir;
|
||||
this.filePath = file;
|
||||
this.mode = file ? 'viewer' : 'explorer';
|
||||
this.sseToken = 0;
|
||||
|
||||
setBasePath('/dev/vendor/shoelace');
|
||||
registerIconLibrary('default', {
|
||||
resolver: (name) => `/dev/vendor/shoelace/assets/icons/${name}.svg`,
|
||||
});
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
// Use light DOM so document-level CSS (public/styles.css => /dev/styles.css) applies to the UI.
|
||||
// Without this, Lit's default shadow root will block global selectors like `.list` / `button.card`.
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.#events = new EventSource('/events');
|
||||
this.#events.onmessage = () => {
|
||||
this.sseToken++;
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', this.#onPopState);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.#events?.close();
|
||||
window.removeEventListener('popstate', this.#onPopState);
|
||||
}
|
||||
|
||||
#onPopState = () => {
|
||||
const { dir, file } = getInitialStateFromUrl();
|
||||
this.dirPath = dir;
|
||||
this.lastDirPath = dir;
|
||||
this.filePath = file;
|
||||
this.mode = file ? 'viewer' : 'explorer';
|
||||
};
|
||||
|
||||
#goToDir = (dir: string) => {
|
||||
this.dirPath = dir;
|
||||
this.lastDirPath = dir;
|
||||
this.mode = 'explorer';
|
||||
this.filePath = '';
|
||||
setUrlState({ dir, file: '' });
|
||||
};
|
||||
|
||||
#openFile = (filePath: string) => {
|
||||
this.filePath = filePath;
|
||||
this.mode = 'viewer';
|
||||
setUrlState({ dir: this.lastDirPath, file: filePath });
|
||||
};
|
||||
|
||||
#backToExplorer = () => {
|
||||
this.mode = 'explorer';
|
||||
this.filePath = '';
|
||||
setUrlState({ dir: this.lastDirPath, file: '' });
|
||||
};
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="app">
|
||||
${this.mode === 'explorer'
|
||||
? html`
|
||||
<dev-file-explorer
|
||||
.path=${this.dirPath}
|
||||
.sseToken=${this.sseToken}
|
||||
@navigate=${(e: CustomEvent<{ path: string }>) => this.#goToDir(e.detail.path)}
|
||||
@open-file=${(e: CustomEvent<{ path: string }>) => this.#openFile(e.detail.path)}
|
||||
></dev-file-explorer>
|
||||
`
|
||||
: nothing}
|
||||
${this.mode === 'viewer'
|
||||
? html`
|
||||
<dev-diagram-viewer
|
||||
.filePath=${this.filePath}
|
||||
.sseToken=${this.sseToken}
|
||||
@back=${this.#backToExplorer}
|
||||
></dev-diagram-viewer>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-explorer-app', DevExplorerApp);
|
||||
182
.esbuild/dev-explorer/file-explorer.ts
Normal file
182
.esbuild/dev-explorer/file-explorer.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { LitElement, html, nothing } from 'lit';
|
||||
|
||||
type Entry = {
|
||||
name: string;
|
||||
kind: 'dir' | 'file';
|
||||
path: string;
|
||||
};
|
||||
|
||||
type FilesResponse = {
|
||||
root: string;
|
||||
path: string;
|
||||
entries: Entry[];
|
||||
};
|
||||
|
||||
function dirname(posixPath: string) {
|
||||
const p = posixPath.replaceAll('\\', '/').replace(/\/+$/, '');
|
||||
if (!p) return '';
|
||||
const idx = p.lastIndexOf('/');
|
||||
if (idx <= 0) return '';
|
||||
return p.slice(0, idx);
|
||||
}
|
||||
|
||||
function pathSegments(posixPath: string) {
|
||||
const p = posixPath.replaceAll('\\', '/').replace(/^\/+/, '').replace(/\/+$/, '');
|
||||
if (!p) return [];
|
||||
return p.split('/').filter(Boolean);
|
||||
}
|
||||
|
||||
export class DevFileExplorer extends LitElement {
|
||||
static properties = {
|
||||
path: { type: String },
|
||||
sseToken: { type: Number },
|
||||
loading: { state: true },
|
||||
error: { state: true },
|
||||
root: { state: true },
|
||||
entries: { state: true },
|
||||
};
|
||||
|
||||
declare path: string;
|
||||
declare sseToken: number;
|
||||
declare loading: boolean;
|
||||
declare error: string;
|
||||
declare root: string;
|
||||
declare entries: Entry[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.path = '';
|
||||
this.sseToken = 0;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.root = '';
|
||||
this.entries = [];
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
// Use light DOM so global CSS in public/styles.css applies.
|
||||
return this;
|
||||
}
|
||||
|
||||
updated(changed: Map<string, unknown>) {
|
||||
if (changed.has('path') || changed.has('sseToken')) {
|
||||
void this.#load();
|
||||
}
|
||||
}
|
||||
|
||||
async #load() {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
try {
|
||||
const url = new URL('/dev/api/files', window.location.origin);
|
||||
if (this.path) url.searchParams.set('path', this.path);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const json = (await res.json()) as FilesResponse;
|
||||
this.root = json.root ?? '';
|
||||
this.entries = json.entries ?? [];
|
||||
} catch (e) {
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
this.entries = [];
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
#emitNavigate(nextPath: string) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('navigate', { detail: { path: nextPath }, bubbles: true, composed: true })
|
||||
);
|
||||
}
|
||||
|
||||
#emitOpenFile(filePath: string) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('open-file', { detail: { path: filePath }, bubbles: true, composed: true })
|
||||
);
|
||||
}
|
||||
|
||||
#onActivate(kind: Entry['kind'], entryPath: string) {
|
||||
if (kind === 'dir') {
|
||||
this.#emitNavigate(entryPath);
|
||||
} else {
|
||||
this.#emitOpenFile(entryPath);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const segments = pathSegments(this.path);
|
||||
const itemLabel = this.entries.length === 1 ? 'item' : 'items';
|
||||
|
||||
return html`
|
||||
<div class="header">
|
||||
<div style="min-width: 0;">
|
||||
<div class="title">Dev Explorer</div>
|
||||
<div class="subtle">
|
||||
root:
|
||||
<span class="path">${this.root || 'cypress/platform/dev-diagrams'}</span>
|
||||
</div>
|
||||
<div style="margin-top: 6px;">
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item @click=${() => this.#emitNavigate('')}>root</sl-breadcrumb-item>
|
||||
${segments.map((seg, idx) => {
|
||||
const to = segments.slice(0, idx + 1).join('/');
|
||||
return html`<sl-breadcrumb-item @click=${() => this.#emitNavigate(to)}
|
||||
>${seg}</sl-breadcrumb-item
|
||||
>`;
|
||||
})}
|
||||
</sl-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="subtle">
|
||||
${this.loading ? 'loading…' : html`<span>${this.entries.length} ${itemLabel}</span>`}
|
||||
</div>
|
||||
<sl-button
|
||||
size="small"
|
||||
variant="default"
|
||||
?disabled=${!this.path}
|
||||
@click=${() => this.#emitNavigate(dirname(this.path))}
|
||||
>
|
||||
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
|
||||
Up
|
||||
</sl-button>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
${this.error
|
||||
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
|
||||
: nothing}
|
||||
${!this.error && !this.loading && this.entries.length === 0
|
||||
? html`<div class="empty">No folders or <span class="path">.mmd</span> files here.</div>`
|
||||
: nothing}
|
||||
|
||||
<div class="list">
|
||||
${this.entries.map((e) => {
|
||||
const icon = e.kind === 'dir' ? 'folder-fill' : 'file-earmark-code';
|
||||
const cardClass = e.kind === 'dir' ? 'card card-folder' : 'card card-file';
|
||||
const click =
|
||||
e.kind === 'dir'
|
||||
? () => this.#emitNavigate(e.path)
|
||||
: () => this.#emitOpenFile(e.path);
|
||||
const onKeyDown = (ev: KeyboardEvent) => {
|
||||
if (ev.key === 'Enter' || ev.key === ' ') {
|
||||
ev.preventDefault();
|
||||
this.#onActivate(e.kind, e.path);
|
||||
}
|
||||
};
|
||||
return html`
|
||||
<button class=${cardClass} type="button" @click=${click} @keydown=${onKeyDown}>
|
||||
<div class="card-inner">
|
||||
<sl-icon class="card-icon" name=${icon}></sl-icon>
|
||||
<div class="card-title">${e.name}</div>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-file-explorer', DevFileExplorer);
|
||||
39
.esbuild/dev-explorer/public/index.html
Normal file
39
.esbuild/dev-explorer/public/index.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="sl-theme-dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Mermaid Dev Explorer</title>
|
||||
|
||||
<link rel="stylesheet" href="/dev/vendor/shoelace/themes/dark.css" />
|
||||
<link rel="stylesheet" href="/dev/styles.css?v=6" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- Mermaid ESM + ELK layout loaders (required for `layout: 'elk'`) -->
|
||||
<script type="module">
|
||||
// Expose a single async hook so the app can reliably await Mermaid "activation".
|
||||
// This avoids races where the UI calls initialize/render before layouts/diagrams are ready.
|
||||
window.mermaidReady = (async () => {
|
||||
try {
|
||||
const [{ default: mermaid }, { default: layouts }] = await Promise.all([
|
||||
import('/mermaid.esm.mjs'),
|
||||
import('/mermaid-layout-elk.esm.mjs'),
|
||||
]);
|
||||
|
||||
mermaid.registerLayoutLoaders(layouts);
|
||||
|
||||
// Keep the rest of the dev explorer simple: expose mermaid on window for the Lit components.
|
||||
window.mermaid = mermaid;
|
||||
return mermaid;
|
||||
} catch (err) {
|
||||
console.error('[dev-explorer] Failed to initialize mermaid (ESM + elk loaders).', err);
|
||||
throw err;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<dev-explorer-app></dev-explorer-app>
|
||||
|
||||
<script type="module" src="/dev/assets/explorer-app.js?v=6"></script>
|
||||
</body>
|
||||
</html>
|
||||
359
.esbuild/dev-explorer/public/styles.css
Normal file
359
.esbuild/dev-explorer/public/styles.css
Normal file
@@ -0,0 +1,359 @@
|
||||
/* Dev Explorer tokens. Keep on :root so the page background picks them up too. */
|
||||
:root {
|
||||
--app-bg: #0b1020;
|
||||
--app-fg: #e8eefc;
|
||||
--panel-bg: #0f1733;
|
||||
--muted: rgba(232, 238, 252, 0.75);
|
||||
--border: rgba(232, 238, 252, 0.12);
|
||||
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--app-bg);
|
||||
color: var(--app-fg);
|
||||
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
||||
/* Keep the page from becoming scrollable when components accidentally overflow. */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Scope app-level layout rules to avoid generic class collisions. */
|
||||
dev-explorer-app {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
dev-explorer-app .app {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Let the top-level view components actually fill the available vertical space. */
|
||||
dev-explorer-app dev-file-explorer,
|
||||
dev-explorer-app dev-diagram-viewer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
dev-explorer-app .header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--panel-bg);
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls .control {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls .label {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls sl-select {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
dev-explorer-app .header .spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
dev-explorer-app .title {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
dev-explorer-app .subtle {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
dev-explorer-app .content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Diagram view should behave like a full-height canvas; avoid nested scrollbars. */
|
||||
dev-explorer-app dev-diagram-viewer .content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Shoelace split panel: ensure slot content can't overflow and push itself off-screen. */
|
||||
dev-explorer-app sl-split-panel {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
dev-explorer-app sl-split-panel::part(base) {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
dev-explorer-app sl-split-panel::part(start),
|
||||
dev-explorer-app sl-split-panel::part(end) {
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
dev-explorer-app .list {
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
grid-template-columns: repeat(auto-fill, 260px);
|
||||
}
|
||||
|
||||
/* Card base - use button element */
|
||||
dev-explorer-app button.card {
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
width: 260px;
|
||||
height: 200px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
/* Folder card style */
|
||||
dev-explorer-app button.card.card-folder {
|
||||
background: linear-gradient(160deg, #1a3052 0%, #0d1a2d 100%);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.4),
|
||||
0 2px 8px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-folder:hover {
|
||||
transform: translateY(-6px) scale(1.02);
|
||||
box-shadow:
|
||||
0 16px 48px rgba(0, 0, 0, 0.5),
|
||||
0 4px 12px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
/* File card style */
|
||||
dev-explorer-app button.card.card-file {
|
||||
background: linear-gradient(160deg, #1e2d4a 0%, #111827 100%);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.4),
|
||||
0 2px 8px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-file:hover {
|
||||
transform: translateY(-6px) scale(1.02);
|
||||
box-shadow:
|
||||
0 16px 48px rgba(0, 0, 0, 0.5),
|
||||
0 4px 12px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
dev-explorer-app button.card:focus-visible {
|
||||
outline: 3px solid rgba(96, 165, 250, 0.7);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
/* Subtle border overlay */
|
||||
dev-explorer-app button.card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
dev-explorer-app .card-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
padding: 24px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Icon styling */
|
||||
dev-explorer-app .card-icon {
|
||||
font-size: 56px !important;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-folder .card-icon {
|
||||
color: #f59e0b;
|
||||
filter: drop-shadow(0 4px 12px rgba(245, 158, 11, 0.4));
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-file .card-icon {
|
||||
color: #3b82f6;
|
||||
filter: drop-shadow(0 4px 12px rgba(59, 130, 246, 0.35));
|
||||
}
|
||||
|
||||
/* Title styling */
|
||||
dev-explorer-app .card-title {
|
||||
font-size: 12px;
|
||||
font-family: var(--mono);
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 230px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
dev-explorer-app .row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
dev-explorer-app .row:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
dev-explorer-app .row sl-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
dev-explorer-app .path {
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
dev-explorer-app .diagram {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
dev-explorer-app .diagram-inner {
|
||||
/* Canvas background behind the rendered SVG (theme-dependent). */
|
||||
background: #fff;
|
||||
color: #111;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Fit the rendered SVG within the available pane (no scrollbars). */
|
||||
dev-explorer-app .diagram-inner > svg {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100% !important;
|
||||
max-height: 100% !important;
|
||||
}
|
||||
|
||||
dev-explorer-app .diagram-inner[data-theme='dark'] {
|
||||
background: #0b1020;
|
||||
color: #e8eefc;
|
||||
}
|
||||
|
||||
dev-explorer-app .console {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-left: 1px solid var(--border);
|
||||
background: var(--panel-bg);
|
||||
}
|
||||
|
||||
dev-explorer-app .spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
dev-explorer-app .console-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
dev-explorer-app .console-toolbar sl-input {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
dev-explorer-app .console-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
dev-explorer-app .logline {
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
margin: 0 0 8px 0;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
dev-explorer-app .logmeta {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
dev-explorer-app .empty {
|
||||
padding: 18px 12px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
dev-explorer-app sl-breadcrumb::part(base) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
import chokidar from 'chokidar';
|
||||
import cors from 'cors';
|
||||
import { context } from 'esbuild';
|
||||
import { promises as fs } from 'fs';
|
||||
import type { Request, Response } from 'express';
|
||||
import express from 'express';
|
||||
import path, { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
import { defaultOptions, getBuildConfig } from './util.js';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
const configs = Object.values(packageOptions).map(({ packageName }) =>
|
||||
getBuildConfig({
|
||||
...defaultOptions,
|
||||
@@ -18,7 +23,7 @@ const configs = Object.values(packageOptions).map(({ packageName }) =>
|
||||
);
|
||||
const mermaidIIFEConfig = getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: false,
|
||||
minify: true,
|
||||
core: false,
|
||||
options: packageOptions.mermaid,
|
||||
format: 'iife',
|
||||
@@ -84,6 +89,81 @@ function sendEventsToAll() {
|
||||
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
|
||||
}
|
||||
|
||||
interface DevExplorerEntry {
|
||||
name: string;
|
||||
kind: 'dir' | 'file';
|
||||
path: string; // posix-style, relative to root
|
||||
}
|
||||
|
||||
const devExplorerRootAbs = resolve(
|
||||
process.cwd(),
|
||||
process.env.MERMAID_DEV_EXPLORER_ROOT ?? 'cypress/platform/dev-diagrams'
|
||||
);
|
||||
|
||||
function toPosixPath(p: string) {
|
||||
return p.split(path.sep).join('/');
|
||||
}
|
||||
|
||||
function resolveWithinDevExplorerRoot(requestedPath: unknown) {
|
||||
const requested = typeof requestedPath === 'string' ? requestedPath : '';
|
||||
if (requested.includes('\0')) {
|
||||
throw new Error('Invalid path');
|
||||
}
|
||||
|
||||
// Normalize slashes and avoid weird absolute-path cases.
|
||||
const cleaned = requested.replaceAll('\\', '/').replace(/^\/+/, '');
|
||||
const absPath = resolve(devExplorerRootAbs, cleaned);
|
||||
const rel = path.relative(devExplorerRootAbs, absPath);
|
||||
|
||||
// Prevent traversal above root.
|
||||
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
||||
throw new Error('Path escapes configured root');
|
||||
}
|
||||
|
||||
return { absPath, relPath: toPosixPath(rel) };
|
||||
}
|
||||
|
||||
async function createDevExplorerBundle() {
|
||||
const devExplorerDir = resolve(__dirname, 'dev-explorer');
|
||||
const entryPoint = resolve(devExplorerDir, 'explorer-app.ts');
|
||||
const outDir = resolve(devExplorerDir, 'dist');
|
||||
|
||||
try {
|
||||
const devExplorerCtx = await context({
|
||||
absWorkingDir: process.cwd(),
|
||||
entryPoints: [entryPoint],
|
||||
bundle: true,
|
||||
format: 'esm',
|
||||
target: 'es2020',
|
||||
sourcemap: true,
|
||||
outdir: outDir,
|
||||
logLevel: 'info',
|
||||
plugins: [
|
||||
{
|
||||
name: 'dev-explorer-reload',
|
||||
setup(build) {
|
||||
build.onEnd(() => {
|
||||
sendEventsToAll();
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await devExplorerCtx.watch();
|
||||
await devExplorerCtx.rebuild();
|
||||
} catch (err) {
|
||||
console.error(
|
||||
[
|
||||
'Dev Explorer bundle build failed.',
|
||||
'Install dependencies: pnpm add -D lit @shoelace-style/shoelace',
|
||||
'Then restart the dev server.',
|
||||
].join('\n')
|
||||
);
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function createServer() {
|
||||
await generateLangium();
|
||||
handleFileChange();
|
||||
@@ -106,8 +186,117 @@ async function createServer() {
|
||||
handleFileChange();
|
||||
});
|
||||
|
||||
// Rebuild the dev-explorer client bundle on changes (and emit SSE so the browser reloads).
|
||||
await createDevExplorerBundle();
|
||||
|
||||
// Emit SSE when Dev Explorer static assets change (e.g. public/styles.css),
|
||||
// otherwise CSS-only changes can look "ignored" unless the user manually refreshes.
|
||||
chokidar
|
||||
.watch(['.esbuild/dev-explorer/public/**/*'], {
|
||||
ignoreInitial: true,
|
||||
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
|
||||
})
|
||||
.on('all', (event, changedPath) => {
|
||||
if (!['add', 'change', 'unlink'].includes(event)) {
|
||||
return;
|
||||
}
|
||||
console.log(`[dev-explorer] ${event}: ${changedPath}`);
|
||||
sendEventsToAll();
|
||||
});
|
||||
|
||||
// Emit SSE when .mmd files inside the configured explorer root change.
|
||||
chokidar
|
||||
.watch('**/*.mmd', {
|
||||
cwd: devExplorerRootAbs,
|
||||
ignoreInitial: true,
|
||||
})
|
||||
.on('all', (event, changedPath) => {
|
||||
if (!['add', 'change', 'unlink'].includes(event)) {
|
||||
return;
|
||||
}
|
||||
console.log(`[dev-explorer] ${event}: ${changedPath}`);
|
||||
sendEventsToAll();
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
app.get('/events', eventsHandler);
|
||||
|
||||
// --- Dev Explorer API + UI -------------------------------------------------
|
||||
const devExplorerDir = resolve(__dirname, 'dev-explorer');
|
||||
const devExplorerPublicDir = resolve(devExplorerDir, 'public');
|
||||
const devExplorerDistDir = resolve(devExplorerDir, 'dist');
|
||||
|
||||
// Shoelace assets (theme css + icons). Safe: only mounted in dev server.
|
||||
app.use(
|
||||
'/dev/vendor/shoelace',
|
||||
express.static(resolve(process.cwd(), 'node_modules/@shoelace-style/shoelace/dist'))
|
||||
);
|
||||
|
||||
app.get('/dev/api/files', async (req, res) => {
|
||||
try {
|
||||
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
|
||||
const stats = await fs.stat(absPath);
|
||||
if (!stats.isDirectory()) {
|
||||
res.status(400).json({ error: 'Not a directory' });
|
||||
return;
|
||||
}
|
||||
|
||||
const dirEntries = await fs.readdir(absPath, { withFileTypes: true });
|
||||
const entries = dirEntries
|
||||
.filter((d) => d.isDirectory() || (d.isFile() && d.name.endsWith('.mmd')))
|
||||
.map<DevExplorerEntry>((d) => ({
|
||||
name: d.name,
|
||||
kind: d.isDirectory() ? 'dir' : 'file',
|
||||
path: toPosixPath(path.join(relPath, d.name)),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.kind !== b.kind) {
|
||||
return a.kind === 'dir' ? -1 : 1;
|
||||
}
|
||||
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
|
||||
});
|
||||
|
||||
res.json({
|
||||
root: toPosixPath(path.relative(process.cwd(), devExplorerRootAbs)),
|
||||
path: relPath === '' ? '' : relPath,
|
||||
entries,
|
||||
});
|
||||
} catch (_e) {
|
||||
res.status(400).json({ error: 'Invalid path' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/dev/api/file', async (req, res) => {
|
||||
try {
|
||||
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
|
||||
if (!absPath.endsWith('.mmd')) {
|
||||
res.status(400).send('Only .mmd files are allowed');
|
||||
return;
|
||||
}
|
||||
const stats = await fs.stat(absPath);
|
||||
if (!stats.isFile()) {
|
||||
res.status(400).send('Not a file');
|
||||
return;
|
||||
}
|
||||
const content = await fs.readFile(absPath, 'utf-8');
|
||||
res.type('text/plain').send(content);
|
||||
// Optional: include relPath for debugging.
|
||||
void relPath;
|
||||
} catch (_e) {
|
||||
res.status(400).send('Invalid path');
|
||||
}
|
||||
});
|
||||
|
||||
// Static assets for the dev-explorer UI.
|
||||
app.use('/dev/assets', express.static(devExplorerDistDir));
|
||||
// Serve `/dev/` (and `/dev`) from public/, including index.html.
|
||||
app.use(
|
||||
'/dev',
|
||||
express.static(devExplorerPublicDir, {
|
||||
index: ['index.html'],
|
||||
})
|
||||
);
|
||||
|
||||
for (const { packageName } of Object.values(packageOptions)) {
|
||||
app.use(express.static(`./packages/${packageName}/dist`));
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
|
||||
|
||||
const external: string[] = ['require', 'fs', 'path'];
|
||||
const outFileName = getFileName(name, options);
|
||||
const { dependencies, version } = JSON.parse(
|
||||
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
|
||||
);
|
||||
const output: BuildOptions = buildOptions({
|
||||
...rest,
|
||||
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
|
||||
@@ -82,15 +85,13 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
|
||||
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
|
||||
define: {
|
||||
// This needs to be stringified for esbuild
|
||||
includeLargeFeatures: `${includeLargeFeatures}`,
|
||||
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
|
||||
'injected.version': `'${version}'`,
|
||||
'import.meta.vitest': 'undefined',
|
||||
},
|
||||
});
|
||||
|
||||
if (core) {
|
||||
const { dependencies } = JSON.parse(
|
||||
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
|
||||
);
|
||||
// Core build is used to generate file without bundled dependencies.
|
||||
// This is used by downstream projects to bundle dependencies themselves.
|
||||
// Ignore dependencies and any dependencies of dependencies
|
||||
|
||||
7
.github/lychee.toml
vendored
7
.github/lychee.toml
vendored
@@ -46,13 +46,20 @@ exclude = [
|
||||
# Drupal 403
|
||||
"https://(www.)?drupal.org",
|
||||
|
||||
# Phbpp 403
|
||||
"https://(www.)?phpbb.com",
|
||||
|
||||
# Swimm returns 404, even though the link is valid
|
||||
"https://docs.swimm.io",
|
||||
|
||||
# Certificate Error
|
||||
"https://noteshub.app",
|
||||
|
||||
# Timeout
|
||||
"https://huehive.co",
|
||||
"https://foswiki.org",
|
||||
"https://www.gnu.org",
|
||||
"https://redmine.org",
|
||||
"https://mermaid-preview.com"
|
||||
]
|
||||
|
||||
|
||||
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@@ -42,4 +42,4 @@ jobs:
|
||||
working-directory: ./packages/mermaid
|
||||
run: pnpm run docs:build
|
||||
|
||||
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef # main
|
||||
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 # main
|
||||
|
||||
10
.github/workflows/codeql.yml
vendored
10
.github/workflows/codeql.yml
vendored
@@ -26,8 +26,8 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['javascript']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
language: ['javascript', 'actions']
|
||||
# CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: ${{ matrix.language }}
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||
uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -62,4 +62,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
|
||||
5
.github/workflows/e2e-applitools.yml
vendored
5
.github/workflows/e2e-applitools.yml
vendored
@@ -23,9 +23,6 @@ env:
|
||||
jobs:
|
||||
e2e-applitools:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
|
||||
options: --user 1001
|
||||
steps:
|
||||
- if: ${{ ! env.USE_APPLI }}
|
||||
name: Warn if not using Applitools
|
||||
@@ -56,7 +53,7 @@ jobs:
|
||||
args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
id: cypress
|
||||
with:
|
||||
start: pnpm run dev
|
||||
|
||||
6
.github/workflows/e2e-timings.yml
vendored
6
.github/workflows/e2e-timings.yml
vendored
@@ -27,12 +27,12 @@ jobs:
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
- name: Install dependencies
|
||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
with:
|
||||
runTests: false
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
id: cypress
|
||||
with:
|
||||
install: false
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit and create pull request
|
||||
uses: peter-evans/create-pull-request@889dce9eaba7900ce30494f5e1ac7220b27e5c81
|
||||
uses: peter-evans/create-pull-request@0979079bc20c05bbbb590a56c21c4e2b1d1f1bbe
|
||||
with:
|
||||
add-paths: |
|
||||
cypress/timings.json
|
||||
|
||||
10
.github/workflows/e2e.yml
vendored
10
.github/workflows/e2e.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
node-version-file: '.node-version'
|
||||
- name: Cache snapshots
|
||||
id: cache-snapshot
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ./cypress/snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
|
||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
with:
|
||||
# just perform install
|
||||
runTests: false
|
||||
@@ -95,13 +95,13 @@ jobs:
|
||||
# These cached snapshots are downloaded, providing the reference snapshots.
|
||||
- name: Cache snapshots
|
||||
id: cache-snapshot
|
||||
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ./cypress/snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||
|
||||
- name: Install dependencies
|
||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
with:
|
||||
runTests: false
|
||||
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
# Install NPM dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
id: cypress
|
||||
with:
|
||||
install: false
|
||||
|
||||
2
.github/workflows/link-checker.yml
vendored
2
.github/workflows/link-checker.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Restore lychee cache
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
|
||||
26
.github/workflows/pr-labeler.yml
vendored
26
.github/workflows/pr-labeler.yml
vendored
@@ -29,3 +29,29 @@ jobs:
|
||||
disable-releaser: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add "Sponsored by MermaidChart" label
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { data: commits } = await github.rest.pulls.listCommits({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
const isSponsored = commits.every(
|
||||
(c) => c.commit.author.email?.endsWith('@mermaidchart.com')
|
||||
);
|
||||
|
||||
if (isSponsored) {
|
||||
console.log('PR is sponsored. Adding label.');
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['Sponsored by MermaidChart'],
|
||||
});
|
||||
}
|
||||
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -36,11 +36,10 @@ jobs:
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
|
||||
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
|
||||
with:
|
||||
version: pnpm changeset:version
|
||||
publish: pnpm changeset:publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
6
.github/workflows/scorecard.yml
vendored
6
.github/workflows/scorecard.yml
vendored
@@ -20,18 +20,18 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run analysis
|
||||
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
publish_results: true
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
- name: Upload to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
|
||||
uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/update-browserlist.yml
vendored
2
.github/workflows/update-browserlist.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
message: 'chore: update browsers list'
|
||||
push: false
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
|
||||
with:
|
||||
branch: update-browserslist
|
||||
title: Update Browserslist
|
||||
|
||||
90
.github/workflows/validate-lockfile.yml
vendored
Normal file
90
.github/workflows/validate-lockfile.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: Validate pnpm-lock.yaml
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'pnpm-lock.yaml'
|
||||
- '**/package.json'
|
||||
- '.github/workflows/validate-lockfile.yml'
|
||||
|
||||
jobs:
|
||||
validate-lockfile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- name: Validate pnpm-lock.yaml entries
|
||||
id: validate # give this step an ID so we can reference its outputs
|
||||
run: |
|
||||
issues=()
|
||||
|
||||
# 1) No tarball references
|
||||
if grep -qF 'tarball:' pnpm-lock.yaml; then
|
||||
issues+=("• Tarball references found (forbidden)")
|
||||
fi
|
||||
|
||||
# 2) No unwanted vitepress paths
|
||||
if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then
|
||||
issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run \`rm -rf packages/mermaid/src/vitepress && pnpm install\` to regenerate.")
|
||||
fi
|
||||
|
||||
# 3) Lockfile only changes when package.json changes
|
||||
git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} > changed.txt
|
||||
if grep -q '^pnpm-lock.yaml$' changed.txt && ! grep -q 'package.json' changed.txt; then
|
||||
issues+=("• pnpm-lock.yaml changed without any package.json modification")
|
||||
fi
|
||||
|
||||
# If any issues, output them and fail
|
||||
if [ ${#issues[@]} -gt 0 ]; then
|
||||
# Use the new GITHUB_OUTPUT approach to set a multiline output
|
||||
{
|
||||
echo "errors<<EOF"
|
||||
printf '%s\n' "${issues[@]}"
|
||||
echo "EOF"
|
||||
} >> $GITHUB_OUTPUT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Find existing lockfile validation comment
|
||||
if: always()
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: find-comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: 'Lockfile Validation Failed'
|
||||
|
||||
- name: Comment on PR if validation failed
|
||||
if: failure()
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body: |
|
||||
❌ **Lockfile Validation Failed**
|
||||
|
||||
The following issue(s) were detected:
|
||||
${{ steps.validate.outputs.errors }}
|
||||
|
||||
Please address these and push an update.
|
||||
|
||||
_Posted automatically by GitHub Actions_
|
||||
|
||||
- name: Delete comment if validation passed
|
||||
if: success() && steps.find-comment.outputs.comment-id != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: ${{ steps.find-comment.outputs.comment-id }},
|
||||
});
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ node_modules/
|
||||
coverage/
|
||||
.idea/
|
||||
.pnpm-store/
|
||||
.instructions/
|
||||
|
||||
dist
|
||||
v8-compile-cache-0
|
||||
|
||||
@@ -78,6 +78,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
},
|
||||
define: {
|
||||
'import.meta.vitest': 'undefined',
|
||||
'injected.includeLargeFeatures': 'true',
|
||||
'injected.version': `'0.0.0'`,
|
||||
},
|
||||
resolve: {
|
||||
extensions: [],
|
||||
@@ -94,10 +96,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
}),
|
||||
...visualizerOptions(packageName, core),
|
||||
],
|
||||
define: {
|
||||
// Needs to be string
|
||||
includeLargeFeatures: 'true',
|
||||
},
|
||||
};
|
||||
|
||||
if (watch && config.build) {
|
||||
|
||||
1005
CHANGELOG.md
1005
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
1
CHANGELOG.md
Symbolic link
1
CHANGELOG.md
Symbolic link
@@ -0,0 +1 @@
|
||||
./packages/mermaid/CHANGELOG.md
|
||||
@@ -5,7 +5,7 @@ USER 0:0
|
||||
RUN corepack enable \
|
||||
&& corepack enable pnpm
|
||||
|
||||
RUN apk add --no-cache git~=2.43.4 \
|
||||
RUN apk add --no-cache git~=2.43 \
|
||||
&& git config --add --system safe.directory /mermaid
|
||||
|
||||
ENV NODE_OPTIONS="--max_old_space_size=8192"
|
||||
|
||||
@@ -95,10 +95,6 @@ In our release process we rely heavily on visual regression tests using [applito
|
||||
|
||||
<!-- </Main description> -->
|
||||
|
||||
## Mermaid AI Bot
|
||||
|
||||
[Mermaid](https://codeparrot.ai/oracle?owner=mermaid-js&repo=mermaid) Bot will help you understand this repository better. You can ask for code examples, installation guide, debugging help and much more.
|
||||
|
||||
## Examples
|
||||
|
||||
**The following are some examples of the diagrams, charts and graphs that can be made using Mermaid. Click here to jump into the [text syntax](https://mermaid.js.org/intro/syntax-reference.html).**
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { MockedD3 } from '../packages/mermaid/src/tests/MockedD3.js';
|
||||
|
||||
export const select = function () {
|
||||
return new MockedD3();
|
||||
};
|
||||
|
||||
export const selectAll = function () {
|
||||
return new MockedD3();
|
||||
};
|
||||
|
||||
export const curveBasis = 'basis';
|
||||
export const curveLinear = 'linear';
|
||||
export const curveCardinal = 'cardinal';
|
||||
@@ -26,7 +26,10 @@ export default eyesPlugin(
|
||||
config.env.useArgos = process.env.RUN_VISUAL_TEST === 'true';
|
||||
|
||||
if (config.env.useArgos) {
|
||||
registerArgosTask(on, config);
|
||||
registerArgosTask(on, config, {
|
||||
// Enable upload to Argos only when it runs on CI.
|
||||
uploadToArgos: !!process.env.CI,
|
||||
});
|
||||
} else {
|
||||
addMatchImageSnapshotPlugin(on, config);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ interface CypressConfig {
|
||||
listUrl?: boolean;
|
||||
listId?: string;
|
||||
name?: string;
|
||||
screenshot?: boolean;
|
||||
}
|
||||
type CypressMermaidConfig = MermaidConfig & CypressConfig;
|
||||
|
||||
@@ -14,7 +15,7 @@ interface CodeObject {
|
||||
mermaid: CypressMermaidConfig;
|
||||
}
|
||||
|
||||
const utf8ToB64 = (str: string): string => {
|
||||
export const utf8ToB64 = (str: string): string => {
|
||||
return Buffer.from(decodeURIComponent(encodeURIComponent(str))).toString('base64');
|
||||
};
|
||||
|
||||
@@ -22,7 +23,7 @@ const batchId: string =
|
||||
'mermaid-batch-' +
|
||||
(Cypress.env('useAppli')
|
||||
? Date.now().toString()
|
||||
: Cypress.env('CYPRESS_COMMIT') || Date.now().toString());
|
||||
: (Cypress.env('CYPRESS_COMMIT') ?? Date.now().toString()));
|
||||
|
||||
export const mermaidUrl = (
|
||||
graphStr: string | string[],
|
||||
@@ -61,9 +62,7 @@ export const imgSnapshotTest = (
|
||||
sequence: {
|
||||
...(_options.sequence ?? {}),
|
||||
actorFontFamily: 'courier',
|
||||
noteFontFamily: _options.sequence?.noteFontFamily
|
||||
? _options.sequence.noteFontFamily
|
||||
: 'courier',
|
||||
noteFontFamily: _options.sequence?.noteFontFamily ?? 'courier',
|
||||
messageFontFamily: 'courier',
|
||||
},
|
||||
};
|
||||
@@ -92,20 +91,33 @@ export const renderGraph = (
|
||||
|
||||
export const openURLAndVerifyRendering = (
|
||||
url: string,
|
||||
options: CypressMermaidConfig,
|
||||
{ screenshot = true, ...options }: CypressMermaidConfig,
|
||||
validation?: any
|
||||
): void => {
|
||||
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||
|
||||
cy.visit(url);
|
||||
cy.window().should('have.property', 'rendered', true);
|
||||
cy.get('svg').should('be.visible');
|
||||
|
||||
if (validation) {
|
||||
cy.get('svg').should(validation);
|
||||
// Handle sandbox mode where SVG is inside an iframe
|
||||
if (options.securityLevel === 'sandbox') {
|
||||
cy.get('iframe').should('be.visible');
|
||||
if (validation) {
|
||||
cy.get('iframe').should(validation);
|
||||
}
|
||||
} else {
|
||||
cy.get('svg').should('be.visible');
|
||||
// cspell:ignore viewbox
|
||||
cy.get('svg').should('not.have.attr', 'viewbox');
|
||||
|
||||
if (validation) {
|
||||
cy.get('svg').should(validation);
|
||||
}
|
||||
}
|
||||
|
||||
verifyScreenshot(name);
|
||||
if (screenshot) {
|
||||
verifyScreenshot(name);
|
||||
}
|
||||
};
|
||||
|
||||
export const verifyScreenshot = (name: string): void => {
|
||||
|
||||
@@ -98,12 +98,12 @@ describe('Configuration', () => {
|
||||
it('should handle arrowMarkerAbsolute set to true', () => {
|
||||
renderGraph(
|
||||
`flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{
|
||||
arrowMarkerAbsolute: true,
|
||||
}
|
||||
@@ -113,8 +113,7 @@ describe('Configuration', () => {
|
||||
cy.get('path')
|
||||
.first()
|
||||
.should('have.attr', 'marker-end')
|
||||
.should('exist')
|
||||
.and('include', 'url(http\\:\\/\\/localhost');
|
||||
.and('include', 'url(http://localhost');
|
||||
});
|
||||
});
|
||||
it('should not taint the initial configuration when using multiple directives', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mermaidUrl } from '../../helpers/util.ts';
|
||||
import { imgSnapshotTest, mermaidUrl, utf8ToB64 } from '../../helpers/util.ts';
|
||||
describe('XSS', () => {
|
||||
it('should handle xss in tags', () => {
|
||||
const str =
|
||||
@@ -141,4 +141,37 @@ describe('XSS', () => {
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sanitize icon labels in architecture diagrams', () => {
|
||||
const str = JSON.stringify({
|
||||
code: `architecture-beta
|
||||
group api(cloud)[API]
|
||||
service db "<img src=x onerror=\\"xssAttack()\\">" [Database] in api`,
|
||||
});
|
||||
imgSnapshotTest(utf8ToB64(str), {}, true);
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sanitize katex blocks', () => {
|
||||
const str = JSON.stringify({
|
||||
code: `sequenceDiagram
|
||||
participant A as Alice<img src="x" onerror="xssAttack()">$$\\text{Alice}$$
|
||||
A->>John: Hello John, how are you?`,
|
||||
});
|
||||
imgSnapshotTest(utf8ToB64(str), {}, true);
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sanitize labels', () => {
|
||||
const str = JSON.stringify({
|
||||
code: `erDiagram
|
||||
"<img src=x onerror=xssAttack()>" ||--|| ENTITY2 : "<img src=x onerror=xssAttack()>"
|
||||
`,
|
||||
});
|
||||
imgSnapshotTest(utf8ToB64(str), {}, true);
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL2: should handle columns statement in sub-blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
id1["Hello"]
|
||||
block
|
||||
columns 3
|
||||
@@ -32,7 +32,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL3: should align block widths and handle columns statement in sub-blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
block
|
||||
columns 1
|
||||
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', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 1
|
||||
block
|
||||
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)', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 1
|
||||
block
|
||||
id1
|
||||
@@ -87,7 +87,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL6: should handle block arrows and spece statements', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
space:3
|
||||
ida idb idc
|
||||
@@ -106,7 +106,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL7: should handle different types of edges', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
A space:5
|
||||
A --o B
|
||||
@@ -119,7 +119,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL8: should handle sub-blocks without columns statements', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 2
|
||||
C A B
|
||||
block
|
||||
@@ -133,7 +133,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL9: should handle edges from blocks in sub blocks to other blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
B space
|
||||
block
|
||||
@@ -147,7 +147,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL10: should handle edges from composite blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
B space
|
||||
block BL
|
||||
@@ -161,7 +161,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL11: should handle edges to composite blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
B space
|
||||
block BL
|
||||
@@ -175,7 +175,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL12: edges should handle labels', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A
|
||||
space
|
||||
A -- "apa" --> E
|
||||
@@ -186,7 +186,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL13: should handle block arrows in different directions', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
space blockArrowId1<["down"]>(down) space
|
||||
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', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A
|
||||
B
|
||||
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', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
block
|
||||
D
|
||||
E
|
||||
@@ -225,7 +225,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL16: width alignment - C should be as wide as the composite block', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
block
|
||||
A("This is the text")
|
||||
B
|
||||
@@ -238,7 +238,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL17: width alignment - blocks should be equal in width', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A("This is the text")
|
||||
B
|
||||
C
|
||||
@@ -249,7 +249,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL18: block types 1 - square, rounded and circle', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A["square"]
|
||||
B("rounded")
|
||||
C(("circle"))
|
||||
@@ -260,7 +260,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL19: block types 2 - odd, diamond and hexagon', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A>"rect_left_inv_arrow"]
|
||||
B{"diamond"}
|
||||
C{{"hexagon"}}
|
||||
@@ -271,7 +271,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL20: block types 3 - stadium', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A(["stadium"])
|
||||
`,
|
||||
{}
|
||||
@@ -280,7 +280,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A[/"lean right"/]
|
||||
B[\"lean left"\]
|
||||
C[/"trapezoid"\]
|
||||
@@ -292,7 +292,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL22: block types 1 - square, rounded and circle', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A["square"]
|
||||
B("rounded")
|
||||
C(("circle"))
|
||||
@@ -303,7 +303,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL23: sizing - it should be possible to make a block wider', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
A("rounded"):2
|
||||
B:2
|
||||
C
|
||||
@@ -314,7 +314,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL24: sizing - it should be possible to make a composite block wider', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
block:2
|
||||
A
|
||||
end
|
||||
@@ -326,7 +326,7 @@ describe('Block diagram', () => {
|
||||
|
||||
it('BL25: block in the middle with space on each side', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
space
|
||||
middle["In the middle"]
|
||||
@@ -337,7 +337,7 @@ describe('Block diagram', () => {
|
||||
});
|
||||
it('BL26: space and an edge', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 5
|
||||
A space B
|
||||
A --x B
|
||||
@@ -347,7 +347,7 @@ describe('Block diagram', () => {
|
||||
});
|
||||
it('BL27: block sizes for regular blocks', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
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', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
a: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', () => {
|
||||
imgSnapshotTest(
|
||||
`block-beta
|
||||
`block
|
||||
columns 3
|
||||
a:3
|
||||
block:e:3
|
||||
@@ -384,4 +384,28 @@ 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
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,4 +114,28 @@ describe('C4 diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
C4Context
|
||||
title System Context diagram with ComponentQueue_Ext
|
||||
|
||||
Enterprise_Boundary(b0, "BankBoundary0") {
|
||||
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
|
||||
|
||||
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
|
||||
|
||||
Enterprise_Boundary(b1, "BankBoundary") {
|
||||
ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
|
||||
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
|
||||
}
|
||||
}
|
||||
|
||||
BiRel(customerA, SystemAA, "Uses")
|
||||
Rel(SystemAA, msgQueue, "Sends messages to")
|
||||
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -562,6 +562,20 @@ class C13["With Città foreign language"]
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should add notes in namespaces', function () {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
note "This is a outer note"
|
||||
note for C1 "This is a outer note for C1"
|
||||
namespace Namespace1 {
|
||||
note "This is a inner note"
|
||||
note for C1 "This is a inner note for C1"
|
||||
class C1
|
||||
}
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render a simple class diagram with no members', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
||||
@@ -709,6 +709,20 @@ class C13["With Città foreign language"]
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should add notes in namespaces', function () {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
note "This is a outer note"
|
||||
note for C1 "This is a outer note for C1"
|
||||
namespace Namespace1 {
|
||||
note "This is a inner note"
|
||||
note for C1 "This is a inner note for C1"
|
||||
class C1
|
||||
}
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render a simple class diagram with no members', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
||||
@@ -495,4 +495,47 @@ describe('Class diagram', () => {
|
||||
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\`
|
||||
`,
|
||||
{}
|
||||
);
|
||||
it('should handle an empty class body with empty braces', () => {
|
||||
imgSnapshotTest(
|
||||
` classDiagram
|
||||
class FooBase~T~ {}
|
||||
class Bar {
|
||||
+Zip
|
||||
+Zap()
|
||||
}
|
||||
FooBase <|-- Ba
|
||||
`,
|
||||
{ flowchart: { defaultRenderer: 'elk' } }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -354,4 +354,107 @@ ORDER ||--|{ LINE-ITEM : contains
|
||||
{ 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' } }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Special characters and numbers syntax', () => {
|
||||
it('should render ER diagram with numeric entity names', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
erDiagram
|
||||
1 ||--|| ORDER : places
|
||||
ORDER ||--|{ 2 : contains
|
||||
2 ||--o{ 3.5 : references
|
||||
`,
|
||||
{ logLevel: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render ER diagram with "u" character in entity names and cardinality', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
erDiagram
|
||||
CUSTOMER ||--|| u : has
|
||||
u ||--|| ORDER : places
|
||||
PROJECT u--o{ TEAM_MEMBER : "parent"
|
||||
`,
|
||||
{ logLevel: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render ER diagram with decimal numbers in relationships', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
erDiagram
|
||||
2.5 ||--|| 1.5 : has
|
||||
CUSTOMER ||--o{ 3.14 : references
|
||||
1.0 ||--|{ ORDER : contains
|
||||
`,
|
||||
{ logLevel: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render ER diagram with numeric entity names and attributes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
erDiagram
|
||||
1 {
|
||||
string name
|
||||
int value
|
||||
}
|
||||
1 ||--|| ORDER : places
|
||||
ORDER {
|
||||
float price
|
||||
string description
|
||||
}
|
||||
`,
|
||||
{ logLevel: 1 }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render complex ER diagram with mixed special entity names', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ 1 : places
|
||||
1 ||--|{ u : contains
|
||||
1.5
|
||||
u ||--|| 2.5 : processes
|
||||
2.5 {
|
||||
string id
|
||||
float value
|
||||
}
|
||||
u {
|
||||
varchar(50) name
|
||||
int count
|
||||
}
|
||||
`,
|
||||
{ logLevel: 1 }
|
||||
);
|
||||
});
|
||||
it('should render ER diagram with standalone numeric entities', () => {
|
||||
imgSnapshotTest(
|
||||
`erDiagram
|
||||
PRODUCT ||--o{ ORDER-ITEM : has
|
||||
1.5
|
||||
u
|
||||
1
|
||||
`,
|
||||
{ logLevel: 1 }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('Flowchart ELK', () => {
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
verifyNumber(maxWidthValue, 380);
|
||||
verifyNumber(maxWidthValue, 380, 15);
|
||||
});
|
||||
});
|
||||
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
|
||||
@@ -128,7 +128,7 @@ describe('Flowchart ELK', () => {
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
|
||||
verifyNumber(width, 380);
|
||||
verifyNumber(width, 380, 15);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
@@ -1053,6 +1053,21 @@ flowchart LR
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('6647-elk: should keep node order when using elk layout unless it would add crossings', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
a --> a1 & a2 & a3 & a4
|
||||
b --> b1 & b2
|
||||
b2 --> b3
|
||||
b1 --> b4
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Title and arrow styling #4813', () => {
|
||||
|
||||
@@ -1029,4 +1029,19 @@ graph TD
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('FDH49: should add edge animation', () => {
|
||||
renderGraph(
|
||||
`
|
||||
flowchart TD
|
||||
A(["Start"]) L_A_B_0@--> B{"Decision"}
|
||||
B --> C["Option A"] & D["Option B"]
|
||||
style C stroke-width:4px,stroke-dasharray: 5
|
||||
L_A_B_0@{ animation: slow }
|
||||
L_B_D_0@{ animation: fast }`,
|
||||
{ look: 'handDrawn', screenshot: false }
|
||||
);
|
||||
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
|
||||
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,6 +79,18 @@ describe('Flowchart v2', () => {
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('6a: should render complex HTML in labels with sandbox security', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{ securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
|
||||
);
|
||||
});
|
||||
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`flowchart TD
|
||||
@@ -1113,4 +1125,90 @@ 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 }
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
describe('when rendering unsuported markdown', () => {
|
||||
const graph = `flowchart TB
|
||||
mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"]
|
||||
mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"]
|
||||
subgraph subgraph1["\`How to fix **fix**\`"]
|
||||
broken --> B["B"]
|
||||
end
|
||||
githost["Github, Gitlab, BitBucket, etc."]
|
||||
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
|
||||
a["1."]
|
||||
b["- x"]
|
||||
`;
|
||||
|
||||
it('should render raw strings', () => {
|
||||
imgSnapshotTest(graph);
|
||||
});
|
||||
|
||||
it('should render raw strings with htmlLabels: false', () => {
|
||||
imgSnapshotTest(graph, { htmlLabels: false });
|
||||
});
|
||||
});
|
||||
|
||||
it('V2 - 17: should apply class def colour to edge label', () => {
|
||||
imgSnapshotTest(
|
||||
` graph LR
|
||||
id1(Start) link@-- "Label" -->id2(Stop)
|
||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||
|
||||
class id2 myClass
|
||||
classDef myClass fill:#bbf,stroke:#f66,stroke-width:2px,color:white,stroke-dasharray: 5 5
|
||||
class link myClass
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -774,6 +774,21 @@ describe('Graph', () => {
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
it('40: should add edge animation', () => {
|
||||
renderGraph(
|
||||
`
|
||||
flowchart TD
|
||||
A(["Start"]) L_A_B_0@--> B{"Decision"}
|
||||
B --> C["Option A"] & D["Option B"]
|
||||
style C stroke-width:4px,stroke-dasharray: 5
|
||||
L_A_B_0@{ animation: slow }
|
||||
L_B_D_0@{ animation: fast }`,
|
||||
{ screenshot: false }
|
||||
);
|
||||
// Verify animation classes are applied to both edges
|
||||
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
|
||||
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
|
||||
});
|
||||
it('58: handle styling with style expressions', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
@@ -973,4 +988,19 @@ graph TD
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('70: should render a subgraph with direction TD', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
flowchart LR
|
||||
subgraph A
|
||||
direction TD
|
||||
a --> b
|
||||
end
|
||||
`,
|
||||
{
|
||||
fontFamily: 'courier',
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -565,6 +565,18 @@ 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
|
||||
//
|
||||
// This test is skipped deliberately
|
||||
@@ -647,6 +659,49 @@ 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", () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
@@ -748,4 +803,64 @@ describe('Gantt diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should handle numeric timestamps with dateFormat x', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title Process time profile (ms)
|
||||
dateFormat x
|
||||
axisFormat %L
|
||||
tickInterval 250millisecond
|
||||
|
||||
section Pipeline
|
||||
Parse JSON p1: 000, 120
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should handle numeric timestamps with dateFormat X', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title Process time profile (ms)
|
||||
dateFormat X
|
||||
axisFormat %L
|
||||
tickInterval 250millisecond
|
||||
|
||||
section Pipeline
|
||||
Parse JSON p1: 000, 120
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should handle seconds-only format with tickInterval (issue #5496)', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
tickInterval 1second
|
||||
dateFormat ss
|
||||
axisFormat %s
|
||||
|
||||
section Network Request
|
||||
RTT : rtt, 0, 20
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should handle dates with year typo like 202 instead of 2024 (issue #5496)', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title Schedule
|
||||
dateFormat YYYY-MM-DD
|
||||
tickInterval 1week
|
||||
axisFormat %m-%d
|
||||
|
||||
section Vacation
|
||||
London : 2024-12-01, 7d
|
||||
London : 202-12-01, 7d
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
79
cypress/integration/rendering/mindmap-tidy-tree.spec.js
Normal file
79
cypress/integration/rendering/mindmap-tidy-tree.spec.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Mindmap Tidy Tree', () => {
|
||||
it('1-tidy-tree: should render a simple mindmap without children', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
A
|
||||
B
|
||||
`
|
||||
);
|
||||
});
|
||||
it('2-tidy-tree: should render a simple mindmap', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap is a long thing))
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
`
|
||||
);
|
||||
});
|
||||
it('3-tidy-tree: should render a mindmap with different shapes', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
id)I am a cloud(
|
||||
id))I am a bang((
|
||||
Tools
|
||||
`
|
||||
);
|
||||
});
|
||||
it('4-tidy-tree: should render a mindmap with children', () => {
|
||||
imgSnapshotTest(
|
||||
` ---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
((This is a mindmap))
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -159,12 +159,10 @@ root
|
||||
});
|
||||
it('square shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
root[
|
||||
The root
|
||||
]
|
||||
`,
|
||||
]`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -172,12 +170,10 @@ mindmap
|
||||
});
|
||||
it('rounded rect shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
root((
|
||||
The root
|
||||
))
|
||||
`,
|
||||
))`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -185,12 +181,10 @@ mindmap
|
||||
});
|
||||
it('circle shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
root(
|
||||
The root
|
||||
)
|
||||
`,
|
||||
)`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -198,10 +192,8 @@ mindmap
|
||||
});
|
||||
it('default shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
`,
|
||||
`mindmap
|
||||
The root`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -209,12 +201,10 @@ mindmap
|
||||
});
|
||||
it('adding children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
`,
|
||||
child2`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -222,13 +212,11 @@ mindmap
|
||||
});
|
||||
it('adding grand children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
`mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
child3
|
||||
`,
|
||||
child3`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
@@ -240,9 +228,48 @@ mindmap
|
||||
`mindmap
|
||||
id1[\`**Start** with
|
||||
a second line 😎\`]
|
||||
id2[\`The dog in **the** hog... a *very long text* about it
|
||||
Word!\`]
|
||||
`
|
||||
id2[\`The dog in **the** hog... a *very long text* about it 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' } }
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Level 2 nodes exceeding 11', () => {
|
||||
it('should render all Level 2 nodes correctly when there are more than 11', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
Node1
|
||||
Node2
|
||||
Node3
|
||||
Node4
|
||||
Node5
|
||||
Node6
|
||||
Node7
|
||||
Node8
|
||||
Node9
|
||||
Node10
|
||||
Node11
|
||||
Node12
|
||||
Node13
|
||||
Node14
|
||||
Node15`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
|
||||
describe('packet structure', () => {
|
||||
it('should render a simple packet diagram', () => {
|
||||
it('should render a simple packet-beta diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`packet-beta
|
||||
title Hello world
|
||||
@@ -10,9 +10,18 @@ describe('packet structure', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a simple packet diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`packet
|
||||
title Hello world
|
||||
0-10: "hello"
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a simple packet diagram without ranges', () => {
|
||||
imgSnapshotTest(
|
||||
`packet-beta
|
||||
`packet
|
||||
0: "h"
|
||||
1: "i"
|
||||
`
|
||||
@@ -21,7 +30,7 @@ describe('packet structure', () => {
|
||||
|
||||
it('should render a complex packet diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`packet-beta
|
||||
`packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -52,7 +61,7 @@ describe('packet structure', () => {
|
||||
packet:
|
||||
showBits: false
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
|
||||
@@ -82,4 +82,13 @@ 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
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('Sankey Diagram', () => {
|
||||
describe('when given a linkColor', function () {
|
||||
this.beforeAll(() => {
|
||||
cy.wrap(
|
||||
`sankey-beta
|
||||
`sankey
|
||||
a,b,10
|
||||
`
|
||||
).as('graph');
|
||||
@@ -62,7 +62,7 @@ describe('Sankey Diagram', () => {
|
||||
this.beforeAll(() => {
|
||||
cy.wrap(
|
||||
`
|
||||
sankey-beta
|
||||
sankey
|
||||
|
||||
a,b,8
|
||||
b,c,8
|
||||
|
||||
969
cypress/integration/rendering/sequencediagram-v2.spec.js
Normal file
969
cypress/integration/rendering/sequencediagram-v2.spec.js
Normal file
@@ -0,0 +1,969 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
|
||||
const looks = ['classic'];
|
||||
const participantTypes = [
|
||||
{ type: 'participant', display: 'participant' },
|
||||
{ type: 'actor', display: 'actor' },
|
||||
{ type: 'boundary', display: 'boundary' },
|
||||
{ type: 'control', display: 'control' },
|
||||
{ type: 'entity', display: 'entity' },
|
||||
{ type: 'database', display: 'database' },
|
||||
{ type: 'collections', display: 'collections' },
|
||||
{ type: 'queue', display: 'queue' },
|
||||
];
|
||||
|
||||
const restrictedTypes = ['boundary', 'control', 'entity', 'database', 'collections', 'queue'];
|
||||
|
||||
const interactionTypes = ['->>', '-->>', '->', '-->', '-x', '--x', '->>+', '-->>+'];
|
||||
|
||||
const notePositions = ['left of', 'right of', 'over'];
|
||||
|
||||
function getParticipantLine(name, type, alias) {
|
||||
if (restrictedTypes.includes(type)) {
|
||||
return ` participant ${name}@{ "type" : "${type}" }\n`;
|
||||
} else if (alias) {
|
||||
return ` participant ${name}@{ "type" : "${type}" } \n`;
|
||||
} else {
|
||||
return ` participant ${name}@{ "type" : "${type}" }\n`;
|
||||
}
|
||||
}
|
||||
|
||||
looks.forEach((look) => {
|
||||
describe(`Sequence Diagram Tests - ${look} look`, () => {
|
||||
it('should render all participant types', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.forEach((pt, index) => {
|
||||
const name = `${pt.display}${index}`;
|
||||
diagramCode += getParticipantLine(name, pt.type);
|
||||
});
|
||||
for (let i = 0; i < participantTypes.length - 1; i++) {
|
||||
diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
|
||||
}
|
||||
imgSnapshotTest(diagramCode, { look, sequence: { diagramMarginX: 50, diagramMarginY: 10 } });
|
||||
});
|
||||
|
||||
it('should render all interaction types', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += getParticipantLine('A', 'actor');
|
||||
diagramCode += getParticipantLine('B', 'boundary');
|
||||
interactionTypes.forEach((interaction, index) => {
|
||||
diagramCode += ` A ${interaction} B: ${interaction} message ${index}\n`;
|
||||
});
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render participant creation and destruction', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.forEach((pt, index) => {
|
||||
const name = `${pt.display}${index}`;
|
||||
diagramCode += getParticipantLine('A', pt.type);
|
||||
diagramCode += getParticipantLine('B', pt.type);
|
||||
diagramCode += ` create participant ${name}@{ "type" : "${pt.type}" }\n`;
|
||||
diagramCode += ` A ->> ${name}: Hello ${pt.display}\n`;
|
||||
if (index % 2 === 0) {
|
||||
diagramCode += ` destroy ${name}\n`;
|
||||
}
|
||||
});
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render notes in all positions', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += getParticipantLine('A', 'actor');
|
||||
diagramCode += getParticipantLine('B', 'boundary');
|
||||
notePositions.forEach((position, index) => {
|
||||
diagramCode += ` Note ${position} A: Note ${position} ${index}\n`;
|
||||
});
|
||||
diagramCode += ` A ->> B: Message with notes\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render parallel interactions', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.slice(0, 4).forEach((pt, index) => {
|
||||
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
|
||||
});
|
||||
diagramCode += ` par Parallel actions\n`;
|
||||
for (let i = 0; i < 3; i += 2) {
|
||||
diagramCode += ` ${participantTypes[i].display}${i} ->> ${participantTypes[i + 1].display}${i + 1}: Message ${i}\n`;
|
||||
if (i < participantTypes.length - 2) {
|
||||
diagramCode += ` and\n`;
|
||||
}
|
||||
}
|
||||
diagramCode += ` end\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render alternative flows', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += getParticipantLine('A', 'actor');
|
||||
diagramCode += getParticipantLine('B', 'boundary');
|
||||
diagramCode += ` alt Successful case\n`;
|
||||
diagramCode += ` A ->> B: Request\n`;
|
||||
diagramCode += ` B -->> A: Success\n`;
|
||||
diagramCode += ` else Failure case\n`;
|
||||
diagramCode += ` A ->> B: Request\n`;
|
||||
diagramCode += ` B --x A: Failure\n`;
|
||||
diagramCode += ` end\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render loops', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.slice(0, 3).forEach((pt, index) => {
|
||||
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
|
||||
});
|
||||
diagramCode += ` loop For each participant\n`;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Message ${i}\n`;
|
||||
}
|
||||
diagramCode += ` end\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render boxes around groups', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
diagramCode += ` box Group 1\n`;
|
||||
participantTypes.slice(0, 3).forEach((pt, index) => {
|
||||
diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
|
||||
});
|
||||
diagramCode += ` end\n`;
|
||||
diagramCode += ` box rgb(200,220,255) Group 2\n`;
|
||||
participantTypes.slice(3, 6).forEach((pt, index) => {
|
||||
diagramCode += ` ${getParticipantLine(`${pt.display}${index}`, pt.type)}`;
|
||||
});
|
||||
diagramCode += ` end\n`;
|
||||
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[3].display}0: Cross-group message\n`;
|
||||
imgSnapshotTest(diagramCode, { look });
|
||||
});
|
||||
|
||||
it('should render with different font settings', () => {
|
||||
let diagramCode = `sequenceDiagram\n`;
|
||||
participantTypes.slice(0, 3).forEach((pt, index) => {
|
||||
diagramCode += getParticipantLine(`${pt.display}${index}`, pt.type);
|
||||
});
|
||||
diagramCode += ` ${participantTypes[0].display}0 ->> ${participantTypes[1].display}1: Regular message\n`;
|
||||
diagramCode += ` Note right of ${participantTypes[1].display}1: Regular note\n`;
|
||||
imgSnapshotTest(diagramCode, {
|
||||
look,
|
||||
sequence: {
|
||||
actorFontFamily: 'courier',
|
||||
actorFontSize: 14,
|
||||
messageFontFamily: 'Arial',
|
||||
messageFontSize: 12,
|
||||
noteFontFamily: 'times',
|
||||
noteFontSize: 16,
|
||||
noteAlign: 'left',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Additional tests for specific combinations
|
||||
describe('Sequence Diagram Special Cases', () => {
|
||||
it('should render complex sequence with all features', () => {
|
||||
const diagramCode = `
|
||||
sequenceDiagram
|
||||
box rgb(200,220,255) Authentication
|
||||
actor User
|
||||
participant LoginUI@{ "type": "boundary" }
|
||||
participant AuthService@{ "type": "control" }
|
||||
participant UserDB@{ "type": "database" }
|
||||
end
|
||||
|
||||
box rgb(200,255,220) Order Processing
|
||||
participant Order@{ "type": "entity" }
|
||||
participant OrderQueue@{ "type": "queue" }
|
||||
participant AuditLogs@{ "type": "collections" }
|
||||
end
|
||||
|
||||
User ->> LoginUI: Enter credentials
|
||||
LoginUI ->> AuthService: Validate
|
||||
AuthService ->> UserDB: Query user
|
||||
UserDB -->> AuthService: User data
|
||||
alt Valid credentials
|
||||
AuthService -->> LoginUI: Success
|
||||
LoginUI -->> User: Welcome
|
||||
|
||||
par Place order
|
||||
User ->> Order: New order
|
||||
Order ->> OrderQueue: Process
|
||||
and
|
||||
Order ->> AuditLogs: Record
|
||||
end
|
||||
|
||||
loop Until confirmed
|
||||
OrderQueue ->> Order: Update status
|
||||
Order -->> User: Notification
|
||||
end
|
||||
else Invalid credentials
|
||||
AuthService --x LoginUI: Failure
|
||||
LoginUI --x User: Retry
|
||||
end
|
||||
`;
|
||||
imgSnapshotTest(diagramCode, {});
|
||||
});
|
||||
|
||||
it('should render with wrapped messages and notes', () => {
|
||||
const diagramCode = `
|
||||
sequenceDiagram
|
||||
participant A
|
||||
participant B
|
||||
|
||||
A ->> B: This is a very long message that should wrap properly in the diagram rendering
|
||||
Note over A,B: This is a very long note that should also wrap properly when rendered in the diagram
|
||||
|
||||
par Wrapped parallel
|
||||
A ->> B: Parallel message 1<br>with explicit line break
|
||||
and
|
||||
B ->> A: Parallel message 2<br>with explicit line break
|
||||
end
|
||||
|
||||
loop Wrapped loop
|
||||
Note right of B: This is a long note<br>in a loop
|
||||
A ->> B: Message in loop
|
||||
end
|
||||
`;
|
||||
imgSnapshotTest(diagramCode, { sequence: { wrap: true } });
|
||||
});
|
||||
describe('Sequence Diagram Rendering with Different Participant Types', () => {
|
||||
it('should render a sequence diagram with various participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant User@{ "type": "actor" }
|
||||
participant AuthService@{ "type": "control" }
|
||||
participant UI@{ "type": "boundary" }
|
||||
participant OrderController@{ "type": "control" }
|
||||
participant Product@{ "type": "entity" }
|
||||
participant MongoDB@{ "type": "database" }
|
||||
participant Products@{ "type": "collections" }
|
||||
participant OrderQueue@{ "type": "queue" }
|
||||
User ->> UI: Login request
|
||||
UI ->> AuthService: Validate credentials
|
||||
AuthService -->> UI: Authentication token
|
||||
UI ->> OrderController: Place order
|
||||
OrderController ->> Product: Check availability
|
||||
Product -->> OrderController: Available
|
||||
OrderController ->> MongoDB: Save order
|
||||
MongoDB -->> OrderController: Order saved
|
||||
OrderController ->> OrderQueue: Process payment
|
||||
OrderQueue -->> User: Order confirmation
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render participant creation and destruction with different types', () => {
|
||||
imgSnapshotTest(`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
Alice->>Bob: Hello Bob, how are you ?
|
||||
Bob->>Alice: Fine, thank you. And you?
|
||||
create participant Carl@{ "type" : "control" }
|
||||
Alice->>Carl: Hi Carl!
|
||||
create actor D as Donald
|
||||
Carl->>D: Hi!
|
||||
destroy Carl
|
||||
Alice-xCarl: We are too many
|
||||
destroy Bob
|
||||
Bob->>Alice: I agree
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle complex interactions between different participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
box rgb(200,220,255) Authentication
|
||||
participant User@{ "type": "actor" }
|
||||
participant LoginUI@{ "type": "boundary" }
|
||||
participant AuthService@{ "type": "control" }
|
||||
participant UserDB@{ "type": "database" }
|
||||
end
|
||||
|
||||
box rgb(200,255,220) Order Processing
|
||||
participant Order@{ "type": "entity" }
|
||||
participant OrderQueue@{ "type": "queue" }
|
||||
participant AuditLogs@{ "type": "collections" }
|
||||
end
|
||||
|
||||
User ->> LoginUI: Enter credentials
|
||||
LoginUI ->> AuthService: Validate
|
||||
AuthService ->> UserDB: Query user
|
||||
UserDB -->> AuthService: User data
|
||||
|
||||
alt Valid credentials
|
||||
AuthService -->> LoginUI: Success
|
||||
LoginUI -->> User: Welcome
|
||||
|
||||
par Place order
|
||||
User ->> Order: New order
|
||||
Order ->> OrderQueue: Process
|
||||
and
|
||||
Order ->> AuditLogs: Record
|
||||
end
|
||||
|
||||
loop Until confirmed
|
||||
OrderQueue ->> Order: Update status
|
||||
Order -->> User: Notification
|
||||
end
|
||||
else Invalid credentials
|
||||
AuthService --x LoginUI: Failure
|
||||
LoginUI --x User: Retry
|
||||
end
|
||||
`,
|
||||
{ sequence: { useMaxWidth: false } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render parallel processes with different participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Customer@{ "type": "actor" }
|
||||
participant Frontend@{ "type": "participant" }
|
||||
participant PaymentService@{ "type": "boundary" }
|
||||
participant InventoryManager@{ "type": "control" }
|
||||
participant Order@{ "type": "entity" }
|
||||
participant OrdersDB@{ "type": "database" }
|
||||
participant NotificationQueue@{ "type": "queue" }
|
||||
|
||||
Customer ->> Frontend: Place order
|
||||
Frontend ->> Order: Create order
|
||||
par Parallel Processing
|
||||
Order ->> PaymentService: Process payment
|
||||
and
|
||||
Order ->> InventoryManager: Reserve items
|
||||
end
|
||||
PaymentService -->> Order: Payment confirmed
|
||||
InventoryManager -->> Order: Items reserved
|
||||
Order ->> OrdersDB: Save finalized order
|
||||
OrdersDB -->> Order: Order saved
|
||||
Order ->> NotificationQueue: Send confirmation
|
||||
NotificationQueue -->> Customer: Order confirmation
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
it('should render different participant types with notes and loops', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Admin
|
||||
participant Dashboard
|
||||
participant AuthService@{ "type" : "boundary" }
|
||||
participant UserManager@{ "type" : "control" }
|
||||
participant UserProfile@{ "type" : "entity" }
|
||||
participant UserDB@{ "type" : "database" }
|
||||
participant Logs@{ "type" : "database" }
|
||||
|
||||
Admin ->> Dashboard: Open user management
|
||||
loop Authentication check
|
||||
Dashboard ->> AuthService: Verify admin rights
|
||||
AuthService ->> Dashboard: Access granted
|
||||
end
|
||||
Dashboard ->> UserManager: List users
|
||||
UserManager ->> UserDB: Query users
|
||||
UserDB ->> UserManager: Return user data
|
||||
Note right of UserDB: Encrypted data<br/>requires decryption
|
||||
UserManager ->> UserProfile: Format profiles
|
||||
UserProfile ->> UserManager: Formatted data
|
||||
UserManager ->> Dashboard: Display users
|
||||
Dashboard ->> Logs: Record access
|
||||
Logs ->> Admin: Audit trail
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render different participant types with alternative flows', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Client
|
||||
participant MobileApp
|
||||
participant CloudService@{ "type" : "boundary" }
|
||||
participant DataProcessor@{ "type" : "control" }
|
||||
participant Transaction@{ "type" : "entity" }
|
||||
participant TransactionsDB@{ "type" : "database" }
|
||||
participant EventBus@{ "type" : "queue" }
|
||||
|
||||
Client ->> MobileApp: Initiate transaction
|
||||
MobileApp ->> CloudService: Authenticate
|
||||
alt Authentication successful
|
||||
CloudService -->> MobileApp: Auth token
|
||||
MobileApp ->> DataProcessor: Process data
|
||||
DataProcessor ->> Transaction: Create transaction
|
||||
Transaction ->> TransactionsDB: Save record
|
||||
TransactionsDB -->> Transaction: Confirmation
|
||||
Transaction ->> EventBus: Publish event
|
||||
EventBus -->> Client: Notification
|
||||
else Authentication failed
|
||||
CloudService -->> MobileApp: Error
|
||||
MobileApp -->> Client: Show error
|
||||
end
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render different participant types with wrapping text', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant B@{ "type" : "boundary" }
|
||||
participant C@{ "type" : "control" }
|
||||
participant E@{ "type" : "entity" }
|
||||
participant DB@{ "type" : "database" }
|
||||
participant COL@{ "type" : "collections" }
|
||||
participant Q@{ "type" : "queue" }
|
||||
|
||||
FE ->> B: Another long message<br/>with explicit<br/>line breaks
|
||||
B -->> FE: Response message that is also quite long and needs to wrap
|
||||
FE ->> C: Process data
|
||||
C ->> E: Validate
|
||||
E -->> C: Validation result
|
||||
C ->> DB: Save
|
||||
DB -->> C: Save result
|
||||
C ->> COL: Log
|
||||
COL -->> Q: Forward
|
||||
Q -->> LongNameUser: Final response with confirmation of all actions taken
|
||||
`,
|
||||
{ sequence: { wrap: true } }
|
||||
);
|
||||
});
|
||||
|
||||
describe('Sequence Diagram - New Participant Types with Long Notes and Messages', () => {
|
||||
it('should render long notes left of boundary', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "boundary" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note left of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long notes left of control', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note left of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long notes right of entity', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "entity" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note right of Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long notes right of database', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note right of Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long notes over collections', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "collections" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note over Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long notes over queue', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
actor Bob
|
||||
Alice->>Bob: Hola
|
||||
Note over Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render notes over actor and boundary', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Charlie@{ "type" : "boundary" }
|
||||
note over Alice: Some note
|
||||
note over Charlie: Other note
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long messages from database to collections', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "database" }
|
||||
participant Bob@{ "type" : "collections" }
|
||||
Alice->>Bob: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long messages from control to entity', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "control" }
|
||||
participant Bob@{ "type" : "entity" }
|
||||
Alice->>Bob:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
Bob->>Alice: I'm short though
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render long messages from queue to boundary', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
participant Alice@{ "type" : "queue" }
|
||||
participant Bob@{ "type" : "boundary" }
|
||||
Alice->>Bob: I'm short
|
||||
Bob->>Alice: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render wrapped long messages from actor to database', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Bob@{ "type" : "database" }
|
||||
Alice->>Bob: I'm short
|
||||
Bob->>Alice:wrap: Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('svg size', () => {
|
||||
it('should render a sequence diagram when useMaxWidth is true (default)', () => {
|
||||
renderGraph(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Bob@{ "type" : "boundary" }
|
||||
participant John@{ "type" : "control" }
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
|
||||
Bob-->Alice: Checking with John...
|
||||
alt either this
|
||||
Alice->>John: Yes
|
||||
else or this
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
`,
|
||||
{ sequence: { useMaxWidth: true } }
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
expect(svg).to.have.attr('width', '100%');
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(820 * 0.95, 820 * 1.05);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a sequence diagram when useMaxWidth is false', () => {
|
||||
renderGraph(
|
||||
`
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant Bob@{ "type" : "boundary" }
|
||||
participant John@{ "type" : "control" }
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
|
||||
Bob-->Alice: Checking with John...
|
||||
alt either this
|
||||
Alice->>John: Yes
|
||||
else or this
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
`,
|
||||
{ sequence: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(width).to.be.within(820 * 0.95, 820 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Central Connection Rendering Tests', () => {
|
||||
it('should render central connection circles on actor vertical lines', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant Charlie
|
||||
Alice ()->>() Bob: Central connection
|
||||
Bob ()-->> Charlie: Reverse central connection
|
||||
Charlie ()<<-->>() Alice: Dual central connection`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections with different arrow types', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice ()->>() Bob: Solid open arrow
|
||||
Alice ()-->>() Bob: Dotted open arrow
|
||||
Alice ()-x() Bob: Solid cross
|
||||
Alice ()--x() Bob: Dotted cross
|
||||
Alice ()->() Bob: Solid arrow`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections with bidirectional arrows', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice ()<<->>() Bob: Bidirectional solid
|
||||
Alice ()<<-->>() Bob: Bidirectional dotted`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections with activations', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant Charlie
|
||||
Alice ()->>() Bob: Activate Bob
|
||||
activate Bob
|
||||
Bob ()-->> Charlie: Message to Charlie
|
||||
Bob ()->>() Alice: Response to Alice
|
||||
deactivate Bob`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections mixed with normal messages', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant Charlie
|
||||
Alice ->> Bob: Normal message
|
||||
Bob ()->>() Charlie: Central connection
|
||||
Charlie -->> Alice: Normal dotted message
|
||||
Alice ()<<-->>() Bob: Dual central connection
|
||||
Bob -x Charlie: Normal cross message`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections with notes', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant Charlie
|
||||
Alice ()->>() Bob: Central connection
|
||||
Note over Alice,Bob: Central connection note
|
||||
Bob ()-->> Charlie: Reverse central connection
|
||||
Note right of Charlie: Response note
|
||||
Charlie ()<<-->>() Alice: Dual central connection`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections with loops and alternatives', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
participant Charlie
|
||||
loop Every minute
|
||||
Alice ()->>() Bob: Central heartbeat
|
||||
Bob ()-->> Charlie: Forward heartbeat
|
||||
end
|
||||
alt Success
|
||||
Charlie ()<<-->>() Alice: Success response
|
||||
else Failure
|
||||
Charlie ()-x() Alice: Failure response
|
||||
end`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render central connections with different participant types', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Alice
|
||||
actor Bob
|
||||
participant Charlie@{"type":"boundary"}
|
||||
participant David@{"type":"control"}
|
||||
participant Eve@{"type":"entity"}
|
||||
Alice ()->>() Bob: To actor
|
||||
Bob ()-->> Charlie: To boundary
|
||||
Charlie ()->>() David: To control
|
||||
David ()<<-->>() Eve: To entity
|
||||
Eve ()-x() Alice: Back to participant`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Participant Stereotypes with Aliases', () => {
|
||||
it('should render participants with stereotypes and aliases', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant API@{ "type" : "boundary" } as Public API
|
||||
participant Auth@{ "type" : "control" } as Auth Controller
|
||||
participant DB@{ "type" : "database" } as User Database
|
||||
participant Cache@{ "type" : "entity" } as Cache Layer
|
||||
API ->> Auth: Authenticate request
|
||||
Auth ->> DB: Query user
|
||||
DB -->> Auth: User data
|
||||
Auth ->> Cache: Store session
|
||||
Cache -->> Auth: Confirmed
|
||||
Auth -->> API: Token`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render actors with stereotypes and aliases', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
actor U@{ "type" : "actor" } as End User
|
||||
actor A@{ "type" : "boundary" } as API Gateway
|
||||
actor S@{ "type" : "control" } as Service Layer
|
||||
actor D@{ "type" : "database" } as Data Store
|
||||
U ->> A: Send request
|
||||
A ->> S: Process
|
||||
S ->> D: Persist
|
||||
D -->> S: Success
|
||||
S -->> A: Response
|
||||
A -->> U: Result`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render mixed participants and actors with stereotypes and aliases', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
actor Client@{ "type" : "actor" } AS Mobile Client
|
||||
participant Gateway@{ "type" : "boundary" } as API Gateway
|
||||
participant OrderSvc@{ "type" : "control" } as Order Service
|
||||
participant Queue@{ "type" : "queue" } as Message Queue
|
||||
participant DB@{ "type" : "database" } as Order Database
|
||||
participant Logs@{ "type" : "collections" } as Audit Logs
|
||||
Client ->> Gateway: Place order
|
||||
Gateway ->> OrderSvc: Validate order
|
||||
OrderSvc ->> Queue: Queue for processing as well
|
||||
OrderSvc ->> DB: Save order
|
||||
OrderSvc ->> Logs: Log transaction
|
||||
Queue -->> OrderSvc: Processing started AS Well
|
||||
DB -->> OrderSvc: Order saved
|
||||
Logs -->> OrderSvc: Logged
|
||||
OrderSvc -->> Gateway: Order confirmed
|
||||
Gateway -->> Client: Confirmation`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render stereotypes with aliases in boxes', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
box rgb(200,220,255) Frontend Layer
|
||||
actor User@{ "type" : "actor" } as End User
|
||||
participant UI@{ "type" : "boundary" } as User Interface
|
||||
end
|
||||
box rgb(255,220,200) Backend Layer
|
||||
participant API@{ "type" : "boundary" } as REST API
|
||||
participant Svc@{ "type" : "control" } as Business Logic
|
||||
end
|
||||
box rgb(220,255,200) Data Layer
|
||||
participant DB@{ "type" : "database" } as Primary DB
|
||||
participant Cache@{ "type" : "entity" } as Cache Store
|
||||
end
|
||||
User ->> UI: Click button
|
||||
UI ->> API: HTTP request
|
||||
API ->> Svc: Process
|
||||
Svc ->> Cache: Check cache
|
||||
Cache -->> Svc: Cache miss
|
||||
Svc ->> DB: Query data
|
||||
DB -->> Svc: Data
|
||||
Svc ->> Cache: Update cache
|
||||
Svc -->> API: Response
|
||||
API -->> UI: Data
|
||||
UI -->> User: Display`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render stereotypes with aliases and complex interactions', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant Web@{ "type" : "boundary" } as Web Portal
|
||||
participant Auth@{ "type" : "control" } as Auth Service
|
||||
participant UserDB@{ "type" : "database" } as User DB
|
||||
participant Queue@{ "type" : "queue" } as Event Queue
|
||||
participant Audit@{ "type" : "collections" } as Audit Trail
|
||||
Web ->> Auth: Login request
|
||||
activate Auth
|
||||
Auth ->> UserDB: Verify credentials
|
||||
activate UserDB
|
||||
UserDB -->> Auth: User found
|
||||
deactivate UserDB
|
||||
alt Valid credentials
|
||||
Auth ->> Queue: Publish login event
|
||||
Auth ->> Audit: Log success
|
||||
par Parallel processing
|
||||
Queue -->> Auth: Event queued
|
||||
and
|
||||
Audit -->> Auth: Logged
|
||||
end
|
||||
Auth -->> Web: Success token
|
||||
else Invalid credentials
|
||||
Auth ->> Audit: Log failure
|
||||
Audit -->> Auth: Logged
|
||||
Auth --x Web: Access denied
|
||||
end
|
||||
deactivate Auth
|
||||
Note over Web,Audit: All interactions logged`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Participant Inline Alias in Config', () => {
|
||||
it('should render participants with inline alias in config object', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant API@{ "type" : "boundary", "alias": "Public API" }
|
||||
participant Auth@{ "type" : "control", "alias": "Auth Service" }
|
||||
participant DB@{ "type" : "database", "alias": "User DB" }
|
||||
API ->> Auth: Login request
|
||||
Auth ->> DB: Query user
|
||||
DB -->> Auth: User data
|
||||
Auth -->> API: Token`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render actors with inline alias in config object', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
actor U@{ "type" : "actor", "alias": "End User" }
|
||||
actor G@{ "type" : "boundary", "alias": "Gateway" }
|
||||
actor S@{ "type" : "control", "alias": "Service" }
|
||||
U ->> G: Request
|
||||
G ->> S: Process
|
||||
S -->> G: Response
|
||||
G -->> U: Result`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle mixed inline and external alias syntax', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant A@{ "type" : "boundary", "alias": "Service A" }
|
||||
participant B@{ "type" : "control" } as Service B
|
||||
participant C@{ "type" : "database" }
|
||||
A ->> B: Request
|
||||
B ->> C: Query
|
||||
C -->> B: Data
|
||||
B -->> A: Response`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should prioritize external alias over inline alias', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant API@{ "type" : "boundary", "alias": "Internal Name" } as External Name
|
||||
participant DB@{ "type" : "database", "alias": "Internal DB" } AS External DB
|
||||
API ->> DB: Query
|
||||
DB -->> API: Result`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render inline alias with only alias field (no type)', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
participant API@{ "alias": "Public API" }
|
||||
participant Auth@{ "alias": "Auth Service" }
|
||||
API ->> Auth: Request
|
||||
Auth -->> API: Response`,
|
||||
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -893,6 +893,17 @@ describe('Sequence diagram', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle bidirectional arrows with autonumber', () => {
|
||||
imgSnapshotTest(`
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant A
|
||||
participant B
|
||||
A<<->>B: This is a bidirectional message
|
||||
A->B: This is a normal message`);
|
||||
});
|
||||
|
||||
it('should support actor links and properties when not mirrored EXPERIMENTAL: USE WITH CAUTION', () => {
|
||||
//Be aware that the syntax for "properties" is likely to be changed.
|
||||
imgSnapshotTest(
|
||||
@@ -1042,4 +1053,167 @@ describe('Sequence diagram', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('render new arrow type', () => {
|
||||
it('should render Solid half arrow top', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice -|\\ John: Hello John, how are you?
|
||||
Alice-|\\ John: Hi Alice, I can hear you!
|
||||
Alice -|\\ John: Test
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render Solid half arrow bottom', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice-|/John: Hello John, how are you?
|
||||
Alice-|/John: Hi Alice, I can hear you!
|
||||
Alice-|/John: Test
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow top ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice-\\\\John: Hello John, how are you?
|
||||
Alice-\\\\John: Hi Alice, I can hear you!
|
||||
Alice-\\\\John: Test
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render Stick half arrow bottom ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice-//John: Hello John, how are you?
|
||||
Alice-//John: Hi Alice, I can hear you!
|
||||
Alice-//John: Test
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render Solid half arrow top reverse ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice/|-John: Hello Alice, how are you?
|
||||
Alice/|-John: Hi Alice, I can hear you!
|
||||
Alice/|-John: Test
|
||||
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Solid half arrow bottom reverse ', () => {
|
||||
imgSnapshotTest(
|
||||
`sequenceDiagram
|
||||
Alice \\|- John: Hello Alice, how are you?
|
||||
Alice \\|- John: Hi Alice, I can hear you!
|
||||
Alice \\|- John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow top reverse ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice //-John: Hello Alice, how are you?
|
||||
Alice //-John: Hi Alice, I can hear you!
|
||||
Alice //-John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow bottom reverse ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice \\\\-John: Hello Alice, how are you?
|
||||
Alice \\\\-John: Hi Alice, I can hear you!
|
||||
Alice \\\\-John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Solid half arrow top dotted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice --|\\John: Hello John, how are you?
|
||||
Alice --|\\John: Hi Alice, I can hear you!
|
||||
Alice --|\\John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Solid half arrow bottom dotted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice --|/John: Hello John, how are you?
|
||||
Alice --|/John: Hi Alice, I can hear you!
|
||||
Alice --|/John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow top dotted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice--\\\\John: Hello John, how are you?
|
||||
Alice--\\\\John: Hi Alice, I can hear you!
|
||||
Alice--\\\\John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow bottom dotted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice--//John: Hello John, how are you?
|
||||
Alice--//John: Hi Alice, I can hear you!
|
||||
Alice--//John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Solid half arrow top reverse dotted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice/|--John: Hello Alice, how are you?
|
||||
Alice/|--John: Hi Alice, I can hear you!
|
||||
Alice/|--John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Solid half arrow bottom reverse dotted', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice\\|--John: Hello Alice, how are you?
|
||||
Alice\\|--John: Hi Alice, I can hear you!
|
||||
Alice\\|--John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow top reverse dotted ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice//--John: Hello Alice, how are you?
|
||||
Alice//--John: Hi Alice, I can hear you!
|
||||
Alice//--John: Test`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Stick half arrow bottom reverse dotted ', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice\\\\--John: Hello Alice, how are you?
|
||||
Alice\\\\--John: Hi Alice, I can hear you!
|
||||
Alice\\\\--John: Test`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -602,6 +602,231 @@ State1 --> [*]
|
||||
--
|
||||
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_____
|
||||
`,
|
||||
{}
|
||||
);
|
||||
|
||||
471
cypress/integration/rendering/treemap.spec.ts
Normal file
471
cypress/integration/rendering/treemap.spec.ts
Normal file
@@ -0,0 +1,471 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Treemap Diagram', () => {
|
||||
it('1: should render a basic treemap', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('2: should render a hierarchical treemap', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Products"
|
||||
"Electronics"
|
||||
"Phones": 50
|
||||
"Computers": 30
|
||||
"Accessories": 20
|
||||
"Clothing"
|
||||
"Men's"
|
||||
"Shirts": 10
|
||||
"Pants": 15
|
||||
"Women's"
|
||||
"Dresses": 20
|
||||
"Skirts": 10
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('3: should render a treemap with styling using classDef', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('4: should handle long text that wraps', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Main Category"
|
||||
"This is a very long item name that should wrap to the next line when rendered in the treemap diagram": 50
|
||||
"Short item": 20
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('5: should render with a forest theme', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
theme: forest
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('6: should handle multiple levels of nesting', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Level 1"
|
||||
"Level 2A"
|
||||
"Level 3A": 10
|
||||
"Level 3B": 15
|
||||
"Level 2B"
|
||||
"Level 3C": 20
|
||||
"Level 3D"
|
||||
"Level 4A": 5
|
||||
"Level 4B": 5
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('7: should handle classDef with multiple styles', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Main"
|
||||
"A": 20
|
||||
"B":::important
|
||||
"B1": 10
|
||||
"B2": 15
|
||||
"C": 5:::secondary
|
||||
|
||||
classDef important fill:#f96,stroke:#333,stroke-width:2px;
|
||||
classDef secondary fill:#6cf,stroke:#333,stroke-dasharray:5 5;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8: should handle dollar value formatting with thousands separator', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$0,0"
|
||||
---
|
||||
treemap
|
||||
"Budget"
|
||||
"Operations"
|
||||
"Salaries": 700000
|
||||
"Equipment": 200000
|
||||
"Supplies": 100000
|
||||
"Marketing"
|
||||
"Advertising": 400000
|
||||
"Events": 100000
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8a: should handle percentage formatting', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ".1%"
|
||||
---
|
||||
treemap-beta
|
||||
"Market Share"
|
||||
"Company A": 0.35
|
||||
"Company B": 0.25
|
||||
"Company C": 0.15
|
||||
"Others": 0.25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8b: should handle decimal formatting', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ".2f"
|
||||
---
|
||||
treemap-beta
|
||||
"Metrics"
|
||||
"Conversion Rate": 0.0567
|
||||
"Bounce Rate": 0.6723
|
||||
"Click-through Rate": 0.1289
|
||||
"Engagement": 0.4521
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8c: should handle dollar sign with decimal places', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$.2f"
|
||||
---
|
||||
treemap-beta
|
||||
"Product Prices"
|
||||
"Basic": 19.99
|
||||
"Standard": 49.99
|
||||
"Premium": 99.99
|
||||
"Enterprise": 199.99
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8d: should handle dollar sign with thousands separator and decimal places', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$,.2f"
|
||||
---
|
||||
treemap-beta
|
||||
"Revenue"
|
||||
"Q1": 1250345.75
|
||||
"Q2": 1645789.25
|
||||
"Q3": 1845123.50
|
||||
"Q4": 2145678.75
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8e: should handle simple thousands separator', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ","
|
||||
---
|
||||
treemap-beta
|
||||
"User Counts"
|
||||
"Active Users": 1250345
|
||||
"New Signups": 45789
|
||||
"Churned": 12350
|
||||
"Converted": 78975
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8f: should handle valueFormat set via directive with dollar and thousands separator', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$,.0f"
|
||||
---
|
||||
treemap-beta
|
||||
"Sales by Region"
|
||||
"North": 1234567
|
||||
"South": 7654321
|
||||
"East": 4567890
|
||||
"West": 9876543
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8g: should handle scientific notation format', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ".2e"
|
||||
---
|
||||
treemap-beta
|
||||
"Scientific Values"
|
||||
"Value 1": 1234567
|
||||
"Value 2": 0.0000123
|
||||
"Value 3": 1000000000
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('9: should handle a complex example with multiple features', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
theme: dark
|
||||
treemap:
|
||||
valueFormat: "$0,0"
|
||||
---
|
||||
treemap-beta
|
||||
"Company Budget"
|
||||
"Engineering":::engineering
|
||||
"Frontend": 300000
|
||||
"Backend": 400000
|
||||
"DevOps": 200000
|
||||
"Marketing":::marketing
|
||||
"Digital": 250000
|
||||
"Print": 100000
|
||||
"Events": 150000
|
||||
"Sales":::sales
|
||||
"Direct": 500000
|
||||
"Channel": 300000
|
||||
|
||||
classDef engineering fill:#6b9bc3,stroke:#333;
|
||||
classDef marketing fill:#c36b9b,stroke:#333;
|
||||
classDef sales fill:#c3a66b,stroke:#333;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('10: should render the example from documentation', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('11: should handle comments', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
%% This is a comment
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
%% Another comment
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('12: should apply classDef fill color to leaf nodes', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Root"
|
||||
"Item A": 30:::redClass
|
||||
"Item B": 20
|
||||
"Item C": 25:::blueClass
|
||||
|
||||
classDef redClass fill:#ff0000;
|
||||
classDef blueClass fill:#0000ff;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('13: should apply classDef stroke styles to sections', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
%% This is a comment
|
||||
"Category A":::thickBorder
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
%% Another comment
|
||||
"Category B":::dashedBorder
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
|
||||
classDef thickBorder stroke:red,stroke-width:8px;
|
||||
classDef dashedBorder stroke:black,stroke-dasharray:5,stroke-width:8px;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('14: should apply classDef color to text labels', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Products"
|
||||
"Electronics":::whiteText
|
||||
"Phones": 40
|
||||
"Laptops": 30
|
||||
"Furniture":::darkText
|
||||
"Chairs": 25
|
||||
"Tables": 20
|
||||
|
||||
classDef whiteText fill:#2c3e50,color:#ffffff;
|
||||
classDef darkText fill:#ecf0f1,color:#000000;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('15: should apply multiple classDef properties simultaneously', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Budget"
|
||||
"Critical":::critical
|
||||
"Server Costs": 50000
|
||||
"Salaries": 80000
|
||||
"Normal":::normal
|
||||
"Office Supplies": 5000
|
||||
"Marketing": 15000
|
||||
classDef critical fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:3px;
|
||||
classDef normal fill:#3498db,color:#fff,stroke:#2980b9,stroke-width:1px;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('16: should handle classDef on nested sections and leaves', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Company"
|
||||
"Engineering":::engSection
|
||||
"Frontend": 30:::highlight
|
||||
"Backend": 40
|
||||
"DevOps": 20:::highlight
|
||||
"Sales"
|
||||
"Direct": 35
|
||||
"Channel": 25:::highlight
|
||||
|
||||
classDef engSection fill:#9b59b6,stroke:#8e44ad,stroke-width:2px;
|
||||
classDef highlight fill:#f39c12,color:#000,stroke:#e67e22,stroke-width:2px;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
it.skip('17: should render a treemap with title', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
title Treemap with Title
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('13: should render a treemap with accessibility attributes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
accTitle: Accessible Treemap Title
|
||||
accDescr: This is a description of the treemap for accessibility purposes
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('14: should render a treemap with title and accessibility attributes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap
|
||||
title Treemap with Title and Accessibility
|
||||
accTitle: Accessible Treemap Title
|
||||
accDescr: This is a description of the treemap for accessibility purposes
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
*/
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
|
||||
describe('XY Chart', () => {
|
||||
it('should render the simplest possible chart', () => {
|
||||
it('should render the simplest possible xy-beta chart', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
@@ -10,10 +10,19 @@ describe('XY Chart', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should render the simplest possible xy chart', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart
|
||||
line [10, 30, 20]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('Should render a complete chart', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -26,7 +35,7 @@ describe('XY Chart', () => {
|
||||
it('Should render a chart without title', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
@@ -38,7 +47,7 @@ describe('XY Chart', () => {
|
||||
it('y-axis title not required', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
@@ -50,7 +59,7 @@ describe('XY Chart', () => {
|
||||
it('Should render a chart without y-axis with different range', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
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]
|
||||
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
|
||||
@@ -61,7 +70,7 @@ describe('XY Chart', () => {
|
||||
it('x axis title not required', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
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]
|
||||
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
|
||||
@@ -72,7 +81,7 @@ describe('XY Chart', () => {
|
||||
it('Multiple plots can be rendered', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
line [23, 46, 77, 34]
|
||||
line [45, 32, 33, 12]
|
||||
bar [87, 54, 99, 85]
|
||||
@@ -86,7 +95,7 @@ describe('XY Chart', () => {
|
||||
it('Decimals and negative numbers are supported', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
y-axis -2.4 --> 3.5
|
||||
line [+1.3, .6, 2.4, -.34]
|
||||
`,
|
||||
@@ -104,7 +113,7 @@ describe('XY Chart', () => {
|
||||
height: 20
|
||||
plotReservedSpacePercent: 100
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
|
||||
`,
|
||||
{}
|
||||
@@ -130,7 +139,7 @@ describe('XY Chart', () => {
|
||||
showTick: false
|
||||
showAxisLine: false
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
|
||||
`,
|
||||
{}
|
||||
@@ -140,7 +149,7 @@ describe('XY Chart', () => {
|
||||
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 }}}%%
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -181,7 +190,7 @@ describe('XY Chart', () => {
|
||||
plotReservedSpacePercent: 60
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -202,7 +211,7 @@ describe('XY Chart', () => {
|
||||
yAxis:
|
||||
showTitle: false
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -223,7 +232,7 @@ describe('XY Chart', () => {
|
||||
yAxis:
|
||||
showLabel: false
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -244,7 +253,7 @@ describe('XY Chart', () => {
|
||||
yAxis:
|
||||
showTick: false
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -265,7 +274,7 @@ describe('XY Chart', () => {
|
||||
yAxis:
|
||||
showAxisLine: false
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -294,7 +303,7 @@ describe('XY Chart', () => {
|
||||
xAxisLineColor: "#87ceeb"
|
||||
plotColorPalette: "#008000, #faba63"
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -307,7 +316,7 @@ describe('XY Chart', () => {
|
||||
it('should use the correct distances between data points', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
x-axis 0 --> 2
|
||||
line [0, 1, 0, 1]
|
||||
bar [1, 0, 1, 0]
|
||||
@@ -325,7 +334,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -344,7 +353,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -357,7 +366,7 @@ describe('XY Chart', () => {
|
||||
it('should render vertical bar chart without labels by default', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -375,7 +384,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
@@ -393,7 +402,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Multiple Bar Plots"
|
||||
x-axis Categories [A, B, C]
|
||||
y-axis "Values" 0 --> 100
|
||||
@@ -412,7 +421,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Multiple Bar Plots"
|
||||
x-axis Categories [A, B, C]
|
||||
y-axis "Values" 0 --> 100
|
||||
@@ -430,7 +439,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Single Bar Chart"
|
||||
x-axis Categories [A]
|
||||
y-axis "Value" 0 --> 100
|
||||
@@ -449,7 +458,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Single Bar Chart"
|
||||
x-axis Categories [A]
|
||||
y-axis "Value" 0 --> 100
|
||||
@@ -467,7 +476,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Decimal and Negative Values"
|
||||
x-axis Categories [A, B, C]
|
||||
y-axis -10 --> 10
|
||||
@@ -486,7 +495,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Decimal and Negative Values"
|
||||
x-axis Categories [A, B, C]
|
||||
y-axis -10 --> 10
|
||||
@@ -504,7 +513,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan,b,c]
|
||||
y-axis "Revenue (in $)" 4000 --> 12000
|
||||
@@ -561,7 +570,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan,b,c]
|
||||
y-axis "Revenue (in $)" 4000 --> 12000
|
||||
@@ -615,7 +624,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
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]
|
||||
y-axis "Revenue (in $)" 4000 --> 12000
|
||||
@@ -672,7 +681,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
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]
|
||||
y-axis "Revenue (in $)" 4000 --> 12000
|
||||
@@ -726,7 +735,7 @@ describe('XY Chart', () => {
|
||||
xyChart:
|
||||
showDataLabel: true
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan]
|
||||
y-axis "Revenue (in $)" 3000 --> 12000
|
||||
@@ -783,7 +792,7 @@ describe('XY Chart', () => {
|
||||
showDataLabel: true
|
||||
chartOrientation: horizontal
|
||||
---
|
||||
xychart-beta
|
||||
xychart
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan]
|
||||
y-axis "Revenue (in $)" 3000 --> 12000
|
||||
|
||||
35
cypress/platform/darshan.html
Normal file
35
cypress/platform/darshan.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!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>
|
||||
16
cypress/platform/dev-diagrams/knsv/knsv2-01-sequence.mmd
Normal file
16
cypress/platform/dev-diagrams/knsv/knsv2-01-sequence.mmd
Normal file
@@ -0,0 +1,16 @@
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
participant John@{ "type": "boundary" }
|
||||
participant P1@{ "type": "control" } as New Control
|
||||
participant P2@{ "type": "entity" } as New Entity
|
||||
participant P3@{ "type": "database" } as New Database
|
||||
Alice->>+John: Hello John, how are you?
|
||||
John->>P1: new msg
|
||||
P1->>P2: new msg
|
||||
P2->>P3: new msg
|
||||
Alice->>+John: John, can you hear me?
|
||||
P3->>P2: new msg
|
||||
P2->>P1: new msg
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
P1->>John: new msg
|
||||
John-->>-Alice: I feel great!
|
||||
9
cypress/platform/dev-diagrams/knsv/knsv2-02-treemap.mmd
Normal file
9
cypress/platform/dev-diagrams/knsv/knsv2-02-treemap.mmd
Normal file
@@ -0,0 +1,9 @@
|
||||
treemap
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
11
cypress/platform/dev-diagrams/knsv/knsv2-03-radar.mmd
Normal file
11
cypress/platform/dev-diagrams/knsv/knsv2-03-radar.mmd
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: "Grades"
|
||||
---
|
||||
radar-beta
|
||||
axis m["Math"], s["Science"], e["English"]
|
||||
axis h["History"], g["Geography"], a["Art"]
|
||||
curve a["Alice"]{85, 90, 80, 70, 75, 90}
|
||||
curve b["Bob"]{70, 75, 85, 80, 90, 85}
|
||||
|
||||
max 100
|
||||
min 0
|
||||
@@ -0,0 +1,11 @@
|
||||
flowchart LR
|
||||
A["A"] e1@==> B["B"]
|
||||
A L_A_n1_0@--> n1["C"]
|
||||
B L_B_n2_0@==> n2["D"]
|
||||
n1 L_n1_n2_0@--> n2
|
||||
|
||||
|
||||
e1@{ animate: true }
|
||||
L_A_n1_0@{ animation: slow }
|
||||
L_B_n2_0@{ animation: slow }
|
||||
L_n1_n2_0@{ animation: fast }
|
||||
@@ -0,0 +1,3 @@
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true }
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
config:
|
||||
layout: ogdc
|
||||
---
|
||||
flowchart-elk TB
|
||||
c1-->a2
|
||||
subgraph one
|
||||
a1-->a2
|
||||
end
|
||||
subgraph two
|
||||
b1-->b2
|
||||
end
|
||||
subgraph three
|
||||
c1-->c2
|
||||
end
|
||||
one --> two
|
||||
three --> two
|
||||
two --> c2
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
|
||||
process_C
|
||||
subgraph container_Alpha
|
||||
subgraph process_B
|
||||
pppB
|
||||
end
|
||||
subgraph process_A
|
||||
pppA
|
||||
end
|
||||
process_B-->|via_AWSBatch|container_Beta
|
||||
process_A-->|messages|container_Beta
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
subgraph container_Beta
|
||||
process_C
|
||||
end
|
||||
subgraph container_Alpha
|
||||
subgraph process_B
|
||||
pppB
|
||||
end
|
||||
subgraph process_A
|
||||
pppA
|
||||
end
|
||||
process_B-->|via_AWSBatch|container_Beta
|
||||
process_A-->|messages|container_Beta
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
subgraph container_Beta
|
||||
process_C
|
||||
end
|
||||
|
||||
process_B-->|via_AWSBatch|container_Beta
|
||||
31
cypress/platform/dev-diagrams/knsv/knsv2-10-class-elk.mmd
Normal file
31
cypress/platform/dev-diagrams/knsv/knsv2-10-class-elk.mmd
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
classDiagram
|
||||
note "I love this diagram!\nDo you love it?"
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
<<interface>> Class01
|
||||
Class03 "1" *-- "*" Class04
|
||||
Class05 "1" o-- "many" Class06
|
||||
Class07 "1" .. "*" Class08
|
||||
Class09 "1" --> "*" C2 : Where am i?
|
||||
Class09 "*" --* "*" C3
|
||||
Class09 "1" --|> "1" Class07
|
||||
Class12 <|.. Class08
|
||||
Class11 ..>Class12
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class01 : -int privateChimp
|
||||
Class01 : +int publicGorilla
|
||||
Class01 : #int protectedMarmoset
|
||||
Class08 <--> C2: Cool label
|
||||
class Class10 {
|
||||
<<service>>
|
||||
int id
|
||||
test()
|
||||
}
|
||||
note for Class10 "Cool class\nI said it's very cool class!"
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
requirementDiagram
|
||||
requirement test_req {
|
||||
id: 1
|
||||
text: the test text.
|
||||
risk: high
|
||||
verifymethod: test
|
||||
}
|
||||
|
||||
element test_entity {
|
||||
type: simulation
|
||||
}
|
||||
|
||||
test_entity - satisfies -> test_req
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart-elk TB
|
||||
internet
|
||||
nat
|
||||
router
|
||||
compute1
|
||||
|
||||
subgraph project
|
||||
router
|
||||
nat
|
||||
subgraph subnet1
|
||||
compute1
|
||||
end
|
||||
end
|
||||
|
||||
%% router --> subnet1
|
||||
subnet1 --> nat
|
||||
%% nat --> internet
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart-elk TB
|
||||
internet
|
||||
nat
|
||||
router
|
||||
lb1
|
||||
lb2
|
||||
compute1
|
||||
compute2
|
||||
subgraph project
|
||||
router
|
||||
nat
|
||||
subgraph subnet1
|
||||
compute1
|
||||
lb1
|
||||
end
|
||||
subgraph subnet2
|
||||
compute2
|
||||
lb2
|
||||
end
|
||||
end
|
||||
internet --> router
|
||||
router --> subnet1 & subnet2
|
||||
subnet1 & subnet2 --> nat --> internet
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
elk:
|
||||
mergeEdges: false
|
||||
forceNodeModelOrder: false
|
||||
considerModelOrder: NONE
|
||||
|
||||
---
|
||||
flowchart TB
|
||||
a --> a1 & a2 & a3 & a4
|
||||
b --> b1 & b2
|
||||
b2 --> b3
|
||||
b1 --> b4
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
curve: rounded
|
||||
---
|
||||
flowchart LR
|
||||
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
|
||||
I --> D & D
|
||||
D@{ shape: question}
|
||||
I@{ shape: question}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
config:
|
||||
layout: tidy-tree
|
||||
---
|
||||
mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
flowchart LR
|
||||
A[A] --> B[B]
|
||||
A[A] --- B([C])
|
||||
A@{ shape: diamond}
|
||||
%%B@{ shape: diamond}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
curve: linear
|
||||
---
|
||||
flowchart LR
|
||||
A[A] -- Mermaid js --> B[B]
|
||||
A[A] -- Mermaid js --- B[B]
|
||||
A@{ shape: diamond}
|
||||
B@{ shape: diamond}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
curve: rounded
|
||||
---
|
||||
flowchart LR
|
||||
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
|
||||
I --> D & D
|
||||
D@{ shape: question}
|
||||
I@{ shape: question}
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
curve: rounded
|
||||
elk:
|
||||
nodePlacementStrategy: NETWORK_SIMPLEX
|
||||
---
|
||||
flowchart LR
|
||||
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
|
||||
D --> I & I
|
||||
a["a"]
|
||||
D@{ shape: trap-b}
|
||||
I@{ shape: lean-l}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
|
||||
---
|
||||
flowchart LR
|
||||
%% subgraph s1["Untitled subgraph"]
|
||||
C["Evaluate"]
|
||||
%% end
|
||||
|
||||
B --> C
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
//curve: linear
|
||||
---
|
||||
flowchart LR
|
||||
%% A ==> B
|
||||
%% A2 --> B2
|
||||
A{A} --> B((Bo boo)) & B & B & B
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
theme: default
|
||||
look: classic
|
||||
---
|
||||
flowchart LR
|
||||
subgraph s1["APA"]
|
||||
D{"Use the editor"}
|
||||
end
|
||||
subgraph S2["S2"]
|
||||
s1
|
||||
I>"fa:fa-code Text"]
|
||||
E["E"]
|
||||
end
|
||||
D -- Mermaid js --> I
|
||||
D --> I & E
|
||||
E --> I
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
a
|
||||
subgraph s0["APA"]
|
||||
subgraph s8["APA"]
|
||||
subgraph s1["APA"]
|
||||
D{"X"}
|
||||
E[Q]
|
||||
end
|
||||
subgraph s3["BAPA"]
|
||||
F[Q]
|
||||
I
|
||||
end
|
||||
D --> I
|
||||
D --> I
|
||||
D --> I
|
||||
|
||||
I{"X"}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
a
|
||||
D{"Use the editor"}
|
||||
|
||||
D -- Mermaid js --> I{"fa:fa-code Text"}
|
||||
D-->I
|
||||
D-->I
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user