Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76aef33868 | ||
|
|
f50d202fdf | ||
|
|
9403e617c1 | ||
|
|
095767ad21 | ||
|
|
0493b744e7 | ||
|
|
0d15a2e487 | ||
|
|
01efb70cf3 | ||
|
|
57f3288de3 | ||
|
|
eaa84ee5d6 | ||
|
|
f1f028737a | ||
|
|
4428038b66 | ||
|
|
a230130db5 | ||
|
|
f3681b582d | ||
|
|
bceca50b0e | ||
|
|
d062dd7132 | ||
|
|
57bb0fa248 | ||
|
|
bcb8a948a5 | ||
|
|
81589e5d98 | ||
|
|
6becf69630 | ||
|
|
1ee1701530 | ||
|
|
0828d4393e | ||
|
|
1777df6ab4 | ||
|
|
e9a982d813 | ||
|
|
8c0aaa94a4 | ||
|
|
bf8afc0c13 | ||
|
|
9c315b2d0d | ||
|
|
d0a0c0a82d | ||
|
|
6c44a7218b | ||
|
|
01162adbd1 | ||
|
|
8e304d2568 | ||
|
|
1af139ce23 | ||
|
|
18fad37944 | ||
|
|
0dc04419c1 | ||
|
|
1dfa32c6e5 | ||
|
|
c3b055b235 | ||
|
|
63e45c9120 | ||
|
|
5d3e7fbbf0 | ||
|
|
f9e6635d53 | ||
|
|
61ce370074 | ||
|
|
a6209977b1 | ||
|
|
7c693363ae | ||
|
|
23091491ba | ||
|
|
540df1f96f | ||
|
|
0d40be232f | ||
|
|
df2e097007 | ||
|
|
be83fa2e08 | ||
|
|
19c0ddca94 | ||
|
|
3533564c00 | ||
|
|
4585fd2994 | ||
|
|
425bd40805 | ||
|
|
411f7e9d70 | ||
|
|
79850eccef | ||
|
|
7792d26d53 | ||
|
|
3a8bc1d5ea | ||
|
|
7309aa6c42 | ||
|
|
d8d994581f | ||
|
|
b3537d5dbe | ||
|
|
fdcb1b7f1b | ||
|
|
a2b212f6e4 | ||
|
|
282d039076 | ||
|
|
030df21e4d | ||
|
|
cb2806b572 | ||
|
|
f287a643ce | ||
|
|
cd8894a27b | ||
|
|
13d1a6099a | ||
|
|
0d7ea7f4f3 | ||
|
|
39cdaff57b | ||
|
|
31ae188af3 | ||
|
|
8cde5eb23e | ||
|
|
7400507689 | ||
|
|
3bd1ea8c2e | ||
|
|
ef9395fd0e | ||
|
|
f5503f88a7 | ||
|
|
74d47a38b1 | ||
|
|
f2606d7341 | ||
|
|
1cc3478512 | ||
|
|
5e6231e3f9 | ||
|
|
775c5765a3 | ||
|
|
f05ccf497d | ||
|
|
5472528ce0 | ||
|
|
2b358ccd80 | ||
|
|
39b2ebde32 | ||
|
|
e8fad2b91c | ||
|
|
f337d16caf | ||
|
|
5773558e84 | ||
|
|
8fe4b7f536 | ||
|
|
765ed2f012 | ||
|
|
63c23b9e0b | ||
|
|
0288392a61 | ||
|
|
21072d343c | ||
|
|
9801dd606d | ||
|
|
b762d1a44a | ||
|
|
1324242337 | ||
|
|
af64307067 | ||
|
|
be6931d0ca | ||
|
|
a907674539 | ||
|
|
fbe2f127b8 | ||
|
|
81a5181877 | ||
|
|
3848f9d618 | ||
|
|
4f52a37e1d | ||
|
|
6c8c0f20c8 | ||
|
|
570d5c79a8 | ||
|
|
5548625536 | ||
|
|
bb2f5b46a8 | ||
|
|
559feacae0 | ||
|
|
ecc514816c | ||
|
|
8d2d6a83ae | ||
|
|
9ca183e654 | ||
|
|
f65dba35c4 | ||
|
|
65a1e067ab | ||
|
|
14bfbb1607 | ||
|
|
49a812c43f | ||
|
|
45010e17d2 | ||
|
|
60f553f9eb | ||
|
|
cdf9ccd3c9 | ||
|
|
566c696be8 | ||
|
|
abde98bd5f | ||
|
|
5c9afbc1ed | ||
|
|
cec1dc4df9 | ||
|
|
1096d10cb4 | ||
|
|
d5ecf60c84 | ||
|
|
4d48a14100 | ||
|
|
d751945e94 | ||
|
|
aa74eaff95 | ||
|
|
35bf065120 | ||
|
|
0604c27b56 | ||
|
|
956d5d0684 | ||
|
|
4cba8bdb72 | ||
|
|
09f4b4f35e | ||
|
|
c751fc3c01 | ||
|
|
65ab28d03b | ||
|
|
f4d6ca8c92 | ||
|
|
86953aadc7 | ||
|
|
876b419c04 | ||
|
|
8bb611c50c | ||
|
|
13286ff4d1 | ||
|
|
88f9c3b6ea | ||
|
|
29ffa1a0a4 | ||
|
|
103f02bb90 | ||
|
|
a6db07a4c1 | ||
|
|
8d8ae6a366 | ||
|
|
e81e09ab23 | ||
|
|
bf8160e31e | ||
|
|
5961c7d451 | ||
|
|
cc3f4fabc2 | ||
|
|
aed37c19c7 | ||
|
|
43bac28b30 | ||
|
|
ed2057312a | ||
|
|
fdff260f7f | ||
|
|
f5878feaa5 | ||
|
|
aef1afe3f4 | ||
|
|
f2cf3e8111 | ||
|
|
32351ab24e | ||
|
|
bd3b39a2c5 | ||
|
|
e286fb2fb7 | ||
|
|
3097161aa8 | ||
|
|
b222e37457 | ||
|
|
0d5240dfef | ||
|
|
09c3fbb13c | ||
|
|
dd66ed095b | ||
|
|
d7308f019b | ||
|
|
634403dac7 | ||
|
|
3bfc932ff7 | ||
|
|
16e22b52a2 | ||
|
|
7773252302 | ||
|
|
d59859abc6 | ||
|
|
2702f7b045 | ||
|
|
d392347bc0 | ||
|
|
38aecc2a3e | ||
|
|
a549c4021d | ||
|
|
d4d7fd351b | ||
|
|
d5548805d0 | ||
|
|
708effa0b0 | ||
|
|
afeb08fa39 | ||
|
|
3df6b6d3f6 | ||
|
|
298ca8cc6a | ||
|
|
6c06c72614 | ||
|
|
b431571662 | ||
|
|
4d6a2478ab | ||
|
|
1064f3ce81 | ||
|
|
8db133e240 | ||
|
|
4cba7c86d5 | ||
|
|
6622cb3968 | ||
|
|
84b0db4dbc | ||
|
|
4c4427a4b9 | ||
|
|
c6d0e3d4b3 | ||
|
|
c4796f7dd7 | ||
|
|
1e787a5c16 | ||
|
|
0bc637f606 | ||
|
|
bb70e37492 | ||
|
|
e09d2f93f8 | ||
|
|
66061ef8e0 | ||
|
|
e13e09de25 | ||
|
|
3931ebbfc2 | ||
|
|
893144d16f | ||
|
|
e44fad0e85 | ||
|
|
7055b6e8ee | ||
|
|
44ef76f086 | ||
|
|
411bbcf5e5 | ||
|
|
025c524012 | ||
|
|
446e1c49c7 | ||
|
|
742cef3870 | ||
|
|
8b681530cd | ||
|
|
8c3951a5e1 | ||
|
|
cdbf3f150e | ||
|
|
1bd5b00a66 | ||
|
|
6fc5a8f696 | ||
|
|
2ae15aa74e | ||
|
|
998cc45b01 | ||
|
|
a9dcc95f8c | ||
|
|
0b7f1a8e4e | ||
|
|
70c8b6014b | ||
|
|
73d5f10334 | ||
|
|
d4d0d33ae9 | ||
|
|
5d17cd4b94 | ||
|
|
8ba1651135 | ||
|
|
12d997fc61 | ||
|
|
6a0d3e4b89 | ||
|
|
e2c2eb03ba | ||
|
|
c4c0d8a6eb | ||
|
|
e2807a2a43 | ||
|
|
bde497c08c | ||
|
|
559f4a8183 | ||
|
|
c6d8848cbe | ||
|
|
93e4e53f0f | ||
|
|
11425fb913 | ||
|
|
4da228cb1c | ||
|
|
94a51cb3c4 | ||
|
|
6f818c38fc | ||
|
|
ba6db70e96 | ||
|
|
1d25499b86 | ||
|
|
c0b353dbfd | ||
|
|
c7765d1b6d | ||
|
|
b4f5a9814a | ||
|
|
77cf436225 | ||
|
|
0c4a72b3ec | ||
|
|
d65d44644a | ||
|
|
c7595f06ef | ||
|
|
f32a4fdc8c | ||
|
|
14f7e3916f | ||
|
|
17698d9be0 | ||
|
|
8880c88c30 | ||
|
|
a0794f250a | ||
|
|
a979432ebd | ||
|
|
ae938963a0 | ||
|
|
a04fad9a06 | ||
|
|
d337d756c0 | ||
|
|
a360a2f65d | ||
|
|
d2bcb91a8e | ||
|
|
16e55a566c | ||
|
|
da7d0fad37 | ||
|
|
046343183e | ||
|
|
7dd7e53d77 | ||
|
|
3d842a795f | ||
|
|
41e92a105c | ||
|
|
8981030ae0 | ||
|
|
0bce3b514f | ||
|
|
f3e58f6b77 | ||
|
|
8aacde4b96 | ||
|
|
efb42aa005 | ||
|
|
e28773ba70 | ||
|
|
9c20831d61 | ||
|
|
041ed1a4a4 | ||
|
|
9785983620 | ||
|
|
df433df510 | ||
|
|
4a89a32909 | ||
|
|
f662c9cfce | ||
|
|
d92e3279bb | ||
|
|
903a4fea53 | ||
|
|
1f3d3089a4 | ||
|
|
574ec57a1b | ||
|
|
de1b8a59be | ||
|
|
a212ec163f | ||
|
|
4a092c0223 | ||
|
|
df86963a71 | ||
|
|
3d763bed43 | ||
|
|
405120369a | ||
|
|
0b070ac484 | ||
|
|
e197214067 | ||
|
|
743eb092cb | ||
|
|
69a4243991 | ||
|
|
cf7d6ce55b | ||
|
|
5061eaadb7 | ||
|
|
c5841b9361 | ||
|
|
0ed041ff08 | ||
|
|
e19e28edd8 | ||
|
|
3df600425c | ||
|
|
39d0a634bb | ||
|
|
fb990e530a | ||
|
|
00a7e24392 | ||
|
|
39b8dc237f | ||
|
|
7aaa1582ff | ||
|
|
5529188cf4 | ||
|
|
5954c8d671 | ||
|
|
03593e95cb | ||
|
|
3490a119f9 | ||
|
|
cd1576f86b | ||
|
|
deca0d3741 | ||
|
|
ef9735a125 | ||
|
|
29b555bf7c | ||
|
|
eab37842de | ||
|
|
4a480cf60f | ||
|
|
062e38dd82 | ||
|
|
9a2a443963 | ||
|
|
3712ba0ca3 | ||
|
|
8f9e00b1ad | ||
|
|
efaeb6140f | ||
|
|
9088635ac8 | ||
|
|
c7d5c7c5e2 | ||
|
|
5221e20399 | ||
|
|
8133d73d3c | ||
|
|
d64f875f8e | ||
|
|
68ab2af195 | ||
|
|
95782a344d | ||
|
|
1b10dac9c9 | ||
|
|
507c473696 | ||
|
|
5548654fd5 | ||
|
|
abe3e3bff4 | ||
|
|
85d07c5b16 | ||
|
|
60ed5bfd71 | ||
|
|
2aa279a7f1 | ||
|
|
751b43eef4 | ||
|
|
8ee3ed7d93 | ||
|
|
67a3ed950a | ||
|
|
747638f0f7 | ||
|
|
fbbde3652e | ||
|
|
564f0336c9 | ||
|
|
7fa164f807 | ||
|
|
f075bd8ef1 | ||
|
|
9aaf0b8d6f | ||
|
|
44318a444f | ||
|
|
1548c18595 | ||
|
|
40a3c4ab1b | ||
|
|
93b4fbe94e | ||
|
|
4f8c22f867 | ||
|
|
8d5d9a9434 | ||
|
|
30735b3378 | ||
|
|
21abfd1956 | ||
|
|
5a437182da | ||
|
|
6d0f950148 | ||
|
|
412b02239b | ||
|
|
7d69022aef | ||
|
|
79713e86a5 | ||
|
|
786f01b180 | ||
|
|
5db0bee759 | ||
|
|
8fc85f671a | ||
|
|
ea5c5d13cd | ||
|
|
e727dc5a2b | ||
|
|
4b9090b5d6 | ||
|
|
645650799e | ||
|
|
e89916ac0b | ||
|
|
8ee8c1523a | ||
|
|
d96aa6d1f3 | ||
|
|
e5fccc1004 | ||
|
|
40699b393a | ||
|
|
fa4f1ca20f | ||
|
|
6fac8927c3 | ||
|
|
ae4d95b00b | ||
|
|
ecd1d3483e | ||
|
|
ea69cd244c | ||
|
|
e9523a40e6 | ||
|
|
bcf0a44ae2 | ||
|
|
a94b77d99d | ||
|
|
348412adf8 | ||
|
|
1874528670 | ||
|
|
d6a123bec2 | ||
|
|
247672f7e0 | ||
|
|
a60167e2f4 | ||
|
|
e21e951f3c | ||
|
|
5190ea0201 | ||
|
|
d3a2906c1c | ||
|
|
468589c497 | ||
|
|
38eb50351e | ||
|
|
3f3c59b67f | ||
|
|
597f195208 | ||
|
|
39053130ec | ||
|
|
51929523e4 | ||
|
|
4f831176ea | ||
|
|
77ac35acaa | ||
|
|
626060cbdb | ||
|
|
46ec921090 | ||
|
|
52b313b3bd | ||
|
|
cfb21bee16 | ||
|
|
2b01ce2d74 | ||
|
|
3d777b4487 | ||
|
|
3ac0e708e3 | ||
|
|
aaaeac26e9 | ||
|
|
5399762e9f | ||
|
|
ba5f5239bd | ||
|
|
f5adcb0a5a | ||
|
|
bc392dfa6a | ||
|
|
2d95fd7ee2 | ||
|
|
1821bc7795 | ||
|
|
01ef10652e | ||
|
|
cb598c11b1 | ||
|
|
a09982e7c1 | ||
|
|
b71ef8ee8e | ||
|
|
68b668d3ff | ||
|
|
2802a1bd75 | ||
|
|
c28e6e37bc | ||
|
|
1a71445ee0 | ||
|
|
21b818fa04 | ||
|
|
1e8c44d009 | ||
|
|
37fd57d7ff | ||
|
|
3187d8a0ea | ||
|
|
8de870e62e | ||
|
|
6ba138a9a3 | ||
|
|
503c71e798 | ||
|
|
d395fc78f0 | ||
|
|
99a4dcaae7 | ||
|
|
96d93e12b3 | ||
|
|
d491327c72 | ||
|
|
89efa697ef | ||
|
|
9a469c3bc5 | ||
|
|
1f7f18cf09 | ||
|
|
6dc24ded90 | ||
|
|
a3346d6ebd | ||
|
|
bb90bb20b2 | ||
|
|
82dd3f0978 | ||
|
|
d590d27c74 | ||
|
|
52ad755b1c | ||
|
|
1ad014e991 | ||
|
|
32806021eb | ||
|
|
c77ee668d4 | ||
|
|
f254912063 | ||
|
|
42492fd26a | ||
|
|
91e6201292 | ||
|
|
da8a8a89bd | ||
|
|
086fe1587b | ||
|
|
17a0dbf7dc | ||
|
|
8654af888c | ||
|
|
2e5814d13f | ||
|
|
6b1513e724 | ||
|
|
d66be21571 | ||
|
|
0f64a84458 | ||
|
|
18dc0943b0 | ||
|
|
a0e12c708a | ||
|
|
9c113e605f | ||
|
|
9752f5941a | ||
|
|
b896eac28e | ||
|
|
2c5a1ea5a3 | ||
|
|
c2ac877cad | ||
|
|
8232dcf91c | ||
|
|
cfd85ebaa3 | ||
|
|
1451e3352f | ||
|
|
ccb354e826 | ||
|
|
ff591da9e7 | ||
|
|
ba35907877 | ||
|
|
d7aa14fae7 | ||
|
|
d437271d26 | ||
|
|
7527af522d | ||
|
|
fdb72415da | ||
|
|
d8215b3bc3 | ||
|
|
ac60f6a565 | ||
|
|
4e9390cea0 | ||
|
|
591910371d | ||
|
|
21ec09d039 | ||
|
|
eb5d17af02 | ||
|
|
627f21bd20 | ||
|
|
734d5a5e5c | ||
|
|
ebb6b44dfb | ||
|
|
1979d7e5c1 | ||
|
|
a5ce78bf73 | ||
|
|
69f3466f5a | ||
|
|
efdbf51598 | ||
|
|
4d87a5bb0f | ||
|
|
27188643e0 | ||
|
|
75fd3e4b15 | ||
|
|
7dfd0fd974 | ||
|
|
ae538127e4 | ||
|
|
f84d5ad2d9 | ||
|
|
217e84c990 | ||
|
|
c555208bf1 | ||
|
|
d61e178d4b | ||
|
|
e59e73cb9c | ||
|
|
dfab1b0977 | ||
|
|
5158bfd06a | ||
|
|
96f6c74ecb | ||
|
|
c89224aeb7 | ||
|
|
e94a64efe2 | ||
|
|
1849eb6d13 | ||
|
|
d186071a39 | ||
|
|
abdd74e885 | ||
|
|
7704e4dd1d | ||
|
|
fab404b40b | ||
|
|
3390cccbf3 | ||
|
|
8954898954 | ||
|
|
8dc340a680 | ||
|
|
19e00e65b0 | ||
|
|
43ffa36a8b | ||
|
|
ae6070d7ac | ||
|
|
035ec5ef95 | ||
|
|
c1c9944a6d | ||
|
|
04cd36aa2e | ||
|
|
1b94a2c6cf | ||
|
|
f1558d41b8 | ||
|
|
ecbad1c884 | ||
|
|
c4bc957d03 | ||
|
|
7a2d06b706 | ||
|
|
25b66d5a84 | ||
|
|
a89548dab6 | ||
|
|
be3b763838 | ||
|
|
f9fee87fca | ||
|
|
5c76852bbb | ||
|
|
73a36fe8f4 | ||
|
|
d9c58bdace | ||
|
|
9f51a111e4 | ||
|
|
489bb2d931 | ||
|
|
aa2b9a7b84 | ||
|
|
cbcf45c1f8 | ||
|
|
ff859daadd | ||
|
|
20dad3265b | ||
|
|
022e9be059 | ||
|
|
807eeb1250 | ||
|
|
5f73a3b49d | ||
|
|
c566e2128d | ||
|
|
47e150f42e | ||
|
|
a22e499576 | ||
|
|
068de0c067 | ||
|
|
d482c16fd6 | ||
|
|
b81d629f7e | ||
|
|
75c5b1b5ed | ||
|
|
39c3cc9062 | ||
|
|
85618169c4 | ||
|
|
2b5748fc10 | ||
|
|
787239343e | ||
|
|
9ef81958fc | ||
|
|
21b343c20b | ||
|
|
fbb972d556 | ||
|
|
d80c65afad | ||
|
|
a2d745dcce | ||
|
|
9194b4edea | ||
|
|
3614b3de20 | ||
|
|
1b139b100e | ||
|
|
c0e30a280e | ||
|
|
b5edbe6969 | ||
|
|
2d27057810 | ||
|
|
3fc9666fd0 | ||
|
|
9deae0c555 | ||
|
|
104aee916d | ||
|
|
975b54b3fc | ||
|
|
1855836d14 | ||
|
|
bf845d6215 | ||
|
|
eee077ca7b | ||
|
|
940b5803f1 | ||
|
|
58fbb7c414 | ||
|
|
8db5a1abc2 | ||
|
|
ab558c6f32 | ||
|
|
8b5bbf0bef | ||
|
|
cb57204365 | ||
|
|
fc90b56494 | ||
|
|
93e95ffb6e | ||
|
|
4b712a88c3 | ||
|
|
13570248f2 | ||
|
|
46718bd3a2 | ||
|
|
3643be8b5a | ||
|
|
28106a43aa | ||
|
|
ac59ac16ec | ||
|
|
82bf728c0c | ||
|
|
535a5c5264 | ||
|
|
93568cc0dc | ||
|
|
6bbc4268e5 | ||
|
|
5a417c96ca | ||
|
|
7fb51d82b3 | ||
|
|
fc87b45645 | ||
|
|
75a84522da | ||
|
|
66f7af5b95 | ||
|
|
46c4db6a39 | ||
|
|
fa632376ee | ||
|
|
aacfd6f1ad | ||
|
|
a0940dd544 | ||
|
|
803627185b | ||
|
|
af560cc3d0 | ||
|
|
059c90733b | ||
|
|
9860834a5d | ||
|
|
47fe0d8fff | ||
|
|
764b802ca9 | ||
|
|
0e79406bda | ||
|
|
8f859c6715 | ||
|
|
5f26a67de0 | ||
|
|
bfe3a09b8a | ||
|
|
71f964b11e | ||
|
|
cbdf28c521 | ||
|
|
27dc099bca | ||
|
|
749ded1f65 | ||
|
|
2783f9306d | ||
|
|
eef85b47f1 | ||
|
|
872bfb2df1 | ||
|
|
6708615c48 | ||
|
|
233379636b | ||
|
|
c78c177a17 | ||
|
|
0ed730dfa6 | ||
|
|
a368d4173d | ||
|
|
3f8a8d0d82 | ||
|
|
870d9e609e | ||
|
|
d2f78a260c | ||
|
|
7fc6ccf7d3 | ||
|
|
45c181b71a | ||
|
|
6f586ff22c | ||
|
|
9da7238c3b | ||
|
|
ec8c53cd5d | ||
|
|
3e87022910 | ||
|
|
5289ecf2a5 | ||
|
|
978a0a22a1 | ||
|
|
4594446106 | ||
|
|
4c6bdf9f86 | ||
|
|
206b933c68 | ||
|
|
a581b93f63 | ||
|
|
715d0f90b4 | ||
|
|
17166a33e1 | ||
|
|
0ae041e775 | ||
|
|
48ace182eb | ||
|
|
5da091fc1f | ||
|
|
11f9ad2e63 | ||
|
|
ee8ea8f710 | ||
|
|
39751eb17d | ||
|
|
1b958de70e | ||
|
|
b1d8ea152a | ||
|
|
5f1c5e1387 | ||
|
|
ac9680ddc7 | ||
|
|
c7c0090491 | ||
|
|
0ab410ae6d | ||
|
|
4b7351f3c8 | ||
|
|
5b89e39371 | ||
|
|
17deed9f2d | ||
|
|
d37fbed1c4 | ||
|
|
23f8c31b92 | ||
|
|
99298a4ea0 | ||
|
|
70aef3f9b4 | ||
|
|
437ff58915 | ||
|
|
a1d0b7b0c1 | ||
|
|
d0e972dab0 | ||
|
|
9a26aaf524 | ||
|
|
e146992470 | ||
|
|
d82a24ab66 | ||
|
|
befe6f05bc | ||
|
|
7dd14021fa | ||
|
|
449276c2dd | ||
|
|
0161857277 | ||
|
|
3fb966ddb0 | ||
|
|
df0e6edce1 | ||
|
|
f6bebecfba | ||
|
|
0a27e93e18 | ||
|
|
0dcd27e5c4 | ||
|
|
18e3f8bfb8 | ||
|
|
d59803ba71 | ||
|
|
1987a272ed | ||
|
|
e7432fbba1 | ||
|
|
49cd313392 | ||
|
|
7afee79614 | ||
|
|
96ff83b2ca | ||
|
|
b669912618 | ||
|
|
b535387ff7 | ||
|
|
d7215d3314 | ||
|
|
1b7535b67b | ||
|
|
ca0c081863 | ||
|
|
8e913ac148 | ||
|
|
7a37e8582a | ||
|
|
116cf5926e | ||
|
|
87992c0faf | ||
|
|
b528c78970 | ||
|
|
f0e9e58bf7 | ||
|
|
b5d5aeb122 | ||
|
|
fedb119865 | ||
|
|
c8e3b05d5a | ||
|
|
e12ac9ef68 | ||
|
|
28181f7872 | ||
|
|
63b449135a | ||
|
|
32c144abb6 | ||
|
|
af582d989b | ||
|
|
5b1ad5d23b | ||
|
|
2c7658cc2c | ||
|
|
9ef35539ef |
@@ -0,0 +1,46 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'prettier'
|
||||
],
|
||||
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint'],
|
||||
ignorePatterns: [
|
||||
'docs/*',
|
||||
'*.cjs',
|
||||
'*.md',
|
||||
'snapshots.js',
|
||||
'svelte.config.js',
|
||||
'package.json',
|
||||
'tsconfig.json'
|
||||
],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
settings: {
|
||||
'svelte3/typescript': () => require('typescript')
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2019,
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-ignore': 'allow-with-description'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Link to Live Editor:
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,7 @@
|
||||
name: 'Mermaid CodeQL config'
|
||||
|
||||
paths:
|
||||
- src
|
||||
paths-ignore:
|
||||
- node_modules
|
||||
- '**/*.spec.js'
|
||||
@@ -0,0 +1,8 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'npm'
|
||||
directory: '/'
|
||||
target-branch: 'develop'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'friday'
|
||||
@@ -1,13 +1,17 @@
|
||||
## :bookmark_tabs: Summary
|
||||
|
||||
Brief description about the content of your PR:
|
||||
|
||||
Resolves #<your issue id here>
|
||||
|
||||
## :straight_ruler: Design Decisions
|
||||
|
||||
Describe the way your implementation works or what design decisions you made if applicable:
|
||||
|
||||
### :clipboard: Tasks
|
||||
|
||||
Make sure you
|
||||
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
|
||||
- [ ] :computer: have added unit/e2e tests (if appropriate)
|
||||
- [ ] :bookmark: targeted `master` branch
|
||||
|
||||
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/master/CONTRIBUTING.md)
|
||||
- [ ] :computer: have added unit/e2e tests (if appropriate)
|
||||
- [ ] :bookmark: targeted `develop` branch
|
||||
@@ -9,14 +9,14 @@
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '36 1 * * 2'
|
||||
|
||||
@@ -28,40 +28,41 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
language: ['javascript']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
config-file: ./.github/codeql/config.yml
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# 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@v1
|
||||
# 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@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Cypress Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
cypress-run:
|
||||
runs-on: ubuntu-latest
|
||||
container: cypress/browsers:node14.16.0-chrome90-ff88
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-and-build-cache
|
||||
with:
|
||||
path: |
|
||||
~/.cache/Cypress
|
||||
build
|
||||
node_modules
|
||||
key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-build-
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
yarn install
|
||||
yarn run lint
|
||||
|
||||
# Install NPM dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
build: yarn build
|
||||
start: yarn preview
|
||||
wait-on: 'http://localhost:3000'
|
||||
record: true
|
||||
headless: true
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
@@ -0,0 +1,51 @@
|
||||
name: Deploy Live Editor
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Build & Deploy
|
||||
run: |
|
||||
npm i -g yarn
|
||||
export DEPLOY=true
|
||||
[ "$GITHUB_EVENT_NAME" == "pull_request" ] && export BETA=true && rm -rf docs/beta/
|
||||
[ "$GITHUB_EVENT_NAME" != "pull_request" ] && rm -rf docs/_app/
|
||||
yarn install
|
||||
yarn run lint
|
||||
version=$(yarn version --patch --no-git-tag-version | grep "New version" | cut -d':' -f 2)
|
||||
yarn build
|
||||
cd bin
|
||||
./fix-path
|
||||
cd ..
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./docs
|
||||
keep_files: true
|
||||
@@ -0,0 +1,69 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
# Publish `master` as Docker `latest` image.
|
||||
branches:
|
||||
- master
|
||||
|
||||
# Publish `v1.2.3` tags as releases.
|
||||
tags:
|
||||
- v*
|
||||
|
||||
# Run tests for all PRs to master and develop.
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
env:
|
||||
IMAGE_NAME: mermaid-live-editor
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
docker build . --file Dockerfile
|
||||
|
||||
push:
|
||||
# Ensure test job passes before pushing image.
|
||||
needs: test
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build image
|
||||
run: docker build . --file Dockerfile --tag $IMAGE_NAME
|
||||
|
||||
- name: Log into registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
|
||||
- name: Push image
|
||||
run: |
|
||||
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
|
||||
|
||||
# Change all uppercase to lowercase
|
||||
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
|
||||
|
||||
# Strip git ref prefix from version
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
|
||||
# Strip "v" prefix from tag name
|
||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||
|
||||
# Use Docker `latest` tag convention
|
||||
[ "$VERSION" == "master" ] && VERSION=latest
|
||||
|
||||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
|
||||
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
|
||||
docker push $IMAGE_ID:$VERSION
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 4'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exempt-issue-labels: 'retained'
|
||||
exempt-pr-labels: 'retained'
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-pr-message: 'This pr is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
@@ -4,3 +4,10 @@ coverage/
|
||||
build/
|
||||
yarn-error.log
|
||||
.npmrc
|
||||
.DS_Store
|
||||
/.svelte-kit
|
||||
/build
|
||||
/functions
|
||||
/snapshots.js
|
||||
/cypress/videos
|
||||
/cypress/screenshots
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
_
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn pre-commit
|
||||
Binary file not shown.
+6
-1
@@ -1 +1,6 @@
|
||||
docs
|
||||
docs/**
|
||||
.svelte-kit/**
|
||||
static/**
|
||||
build/**
|
||||
node_modules/**
|
||||
snapshots.js
|
||||
+7
-2
@@ -1,3 +1,8 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
"singleQuote": true,
|
||||
"svelteSortOrder": "options-scripts-markup-styles",
|
||||
"svelteBracketNewLine": false,
|
||||
"useTabs": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100
|
||||
}
|
||||
|
||||
+1
-3
@@ -6,9 +6,7 @@
|
||||
# Stop : press ctrl + c
|
||||
# or
|
||||
# docker stop mermaid-live-editor
|
||||
|
||||
|
||||
FROM node:13.6.0-alpine as mermaid-live-editor-builder
|
||||
FROM node:14.17.0 as mermaid-live-editor-builder
|
||||
COPY --chown=node:node . /home
|
||||
WORKDIR /home
|
||||
RUN yarn install
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
[](https://dashboard.cypress.io/projects/2ckppp/runs) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
|
||||
# Contributors are welcome
|
||||
# Contributors are welcome!
|
||||
|
||||
If you want to speed up the progress for mermaid-live-editor, join the slack channel and contact knsv.
|
||||
|
||||
@@ -8,7 +8,6 @@ If you want to speed up the progress for mermaid-live-editor, join the slack cha
|
||||
|
||||
Edit, preview and share mermaid charts/diagrams.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Edit and preview flowcharts, sequence diagrams, gantt diagrams in real time.
|
||||
@@ -16,46 +15,38 @@ Edit, preview and share mermaid charts/diagrams.
|
||||
- Get a link to a viewer of the diagram so that you can share it with others.
|
||||
- Get a link to edit the diagram so that someone else can tweak it and send a new link back
|
||||
|
||||
|
||||
## Live demo
|
||||
|
||||
You can try out a live version [here](https://mermaid-js.github.io/mermaid-live-editor/).
|
||||
|
||||
## Docker
|
||||
|
||||
```bash
|
||||
docker run --publish 8000:80 ghcr.io/mermaid-js/mermaid-live-editor
|
||||
```
|
||||
|
||||
Then open http://localhost:8000
|
||||
|
||||
## Setup
|
||||
|
||||
[Volta](https://volta.sh) is used for managing node and yarn versions.
|
||||
|
||||
This project is set up using [Yarn](https://classic.yarnpkg.com/en/docs/getting-started):
|
||||
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
or in develop branch to use the beta version of mermaid use
|
||||
|
||||
```
|
||||
yarn install --update-checksums
|
||||
```
|
||||
|
||||
This together with a .npmrc file:
|
||||
|
||||
```
|
||||
registry=https://registry.npmjs.com/
|
||||
@mermaid-js:registry=https://npm.pkg.github.com
|
||||
//npm.pkg.github.com/:_authToken="XXXXXXXXX"
|
||||
```
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
yarn dev
|
||||
open http://localhost:8080
|
||||
yarn dev -- --open
|
||||
```
|
||||
|
||||
This app is created with Svelte + svelte-spa-router.
|
||||
|
||||
This app is created with Svelte Kit.
|
||||
|
||||
## Release
|
||||
|
||||
```
|
||||
yarn release
|
||||
```
|
||||
When a PR is created targeting master, it will be built and deployed as a beta in http://mermaid-js.github.io/mermaid-live-editor/beta
|
||||
|
||||
Once the PR is merged, it will automatically be released.
|
||||
|
||||
+9
-2
@@ -4,11 +4,18 @@
|
||||
|
||||
# git clone git@github.com:mermaid-js/docs.git
|
||||
# git clone https://github.com/mermaid-js/docs.git
|
||||
|
||||
set -e
|
||||
rm -rf docs
|
||||
yarn release
|
||||
pushd .
|
||||
if [ ! -d ../docs ]; then
|
||||
echo "Clone https://github.com/mermaid-js/docs to parent folder before continuing."
|
||||
exit
|
||||
fi
|
||||
rm -rf ../docs/mermaid-live-editor-beta/*
|
||||
cp -r docs/* ../docs/mermaid-live-editor-beta
|
||||
cd ../docs
|
||||
git commit * -m "Beta updated with latest changes"
|
||||
git add .
|
||||
git commit -m "Beta updated with latest changes"
|
||||
git push
|
||||
popd
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
pushd ../docs
|
||||
# find ./ -type f -exec sed -i 's/service-worker.js/mermaid-live-editor\/service-worker.js/g' {} \;
|
||||
find ./ -type f -exec sed -i 's/manifest.json/\/mermaid-live-editor\/manifest.json/g' {} \;
|
||||
find ./ -type f -exec sed -i 's/favicon.png/\/mermaid-live-editor\/favicon.png/g' {} \;
|
||||
popd
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"projectId": "2ckppp",
|
||||
"$schema": "https://on.cypress.io/cypress.schema.json",
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"pluginsFile": "./cypress/plugins/index.cjs",
|
||||
"viewportWidth": 1440,
|
||||
"viewportHeight": 768,
|
||||
"snapshotFileName": "./cypress/snapshots.js",
|
||||
"defaultCommandTimeout": 16000,
|
||||
"requestTimeout": 16000
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"plugins": ["cypress", "mocha"],
|
||||
"extends": ["plugin:cypress/recommended", "plugin:mocha/recommended"],
|
||||
"rules": {
|
||||
"jest/expect-expect": "off",
|
||||
"mocha/no-mocha-arrows": "off"
|
||||
},
|
||||
"env": {
|
||||
"cypress/globals": true,
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
describe('Check actions', () => {
|
||||
// eslint-disable-next-line mocha/no-hooks-for-single-case
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/edit');
|
||||
cy.contains('Actions').click();
|
||||
});
|
||||
it('should update markdown code', () => {
|
||||
cy.get('#markdown')
|
||||
.invoke('val')
|
||||
.then((oldText) => {
|
||||
cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
|
||||
cy.get('#markdown')
|
||||
.invoke('val')
|
||||
.then((newText) => {
|
||||
expect(oldText).to.not.eq(newText);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should load gists from URL', () => {
|
||||
cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a');
|
||||
cy.contains('Load Gist').click();
|
||||
cy.contains('Go shopping!!');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
describe('Auto sync tests', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
});
|
||||
it('should dim diagram when code is edited', () => {
|
||||
cy.contains('Auto sync').click();
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.get('#editor').type(' C --> Test');
|
||||
cy.get('#view').should('have.class', 'outOfSync');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('should show/hide sync button with auto sync', () => {
|
||||
cy.get('[data-cy=sync]').should('not.exist');
|
||||
cy.contains('Auto sync').click();
|
||||
cy.get('[data-cy=sync]').should('exist');
|
||||
cy.get('#autoSync').check();
|
||||
cy.get('[data-cy=sync]').should('not.exist');
|
||||
});
|
||||
it('should not dim diagram when code is in sync', () => {
|
||||
cy.contains('Auto sync').click();
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.get('#editor').type(' C --> Test');
|
||||
cy.get('#view').should('have.class', 'outOfSync');
|
||||
cy.get('[data-cy=sync]').click();
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.get('#autoSync').check();
|
||||
cy.get('#editor').type('ing');
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
describe('Save History', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/edit');
|
||||
});
|
||||
it('should save when clicked', () => {
|
||||
cy.get('#historyList').find('li').should('have.length', 0);
|
||||
cy.get('#historyList').contains('No items in History');
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#historyList').find('No items in History').should('not.exist');
|
||||
cy.get('#historyList').find('li').should('have.length', 1);
|
||||
cy.get('#saveHistory').click();
|
||||
cy.on('window:alert', (str) => {
|
||||
expect(str).to.equal('State already saved.');
|
||||
});
|
||||
cy.on('window:confirm', () => true);
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#historyList').find('li').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('should be able to restore and delete', () => {
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.get('#historyList').find('No items in History').should('not.exist');
|
||||
cy.get('#historyList').find('li').should('have.length', 1);
|
||||
cy.contains('HistoryTest');
|
||||
cy.contains('Restore').click();
|
||||
cy.contains('HistoryTest').should('not.exist');
|
||||
cy.contains('Delete').click();
|
||||
cy.get('#historyList').find('li').should('have.length', 0);
|
||||
cy.get('#historyList').contains('No items in History');
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#editor').type('ing');
|
||||
cy.get('#clearHistory').click();
|
||||
cy.on('window:alert', (str) => {
|
||||
expect(str).to.equal('Clear all saved items?');
|
||||
});
|
||||
cy.on('window:confirm', () => true);
|
||||
cy.get('#historyList').contains('No items in History');
|
||||
});
|
||||
|
||||
it('should auto save history', () => {
|
||||
cy.clock();
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.tick(70000);
|
||||
cy.contains('Timeline').click();
|
||||
cy.get('#historyList').find('li').should('have.length', 1);
|
||||
cy.get('#editor').type('ing');
|
||||
cy.tick(70000);
|
||||
cy.get('#historyList').find('li').should('have.length', 2);
|
||||
for (let i = 0; i < 31; i++) {
|
||||
cy.get('#editor').type('.');
|
||||
cy.tick(70000);
|
||||
}
|
||||
cy.get('#historyList').find('li').should('have.length', 30);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
import { toBase64 } from 'js-base64';
|
||||
|
||||
describe('Site Loads', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
});
|
||||
it('Check Home page load', () => {
|
||||
cy.visit('/');
|
||||
cy.url().should('include', '/edit');
|
||||
cy.contains('History').click();
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('Check Redirect from old URL', () => {
|
||||
cy.visit(
|
||||
'/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
|
||||
);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
|
||||
);
|
||||
|
||||
cy.contains('History').click();
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('should load sample diagrams when clicked', () => {
|
||||
cy.contains('Sample Diagrams').click();
|
||||
cy.contains('Pie Chart').click();
|
||||
cy.contains('pie title Pets adopted by volunteers');
|
||||
cy.contains('Class Diagram').click();
|
||||
cy.contains('classDiagram');
|
||||
});
|
||||
|
||||
it('should load diagram from gist', () => {
|
||||
cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
|
||||
cy.contains('Go shopping!!');
|
||||
cy.contains('Revisions');
|
||||
cy.contains('sidharthv96 v8f8f1e2');
|
||||
cy.contains('sidharthv96 v7851e19');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('should load diagram from gist revision', () => {
|
||||
cy.visit(
|
||||
'/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19'
|
||||
);
|
||||
cy.contains('Party');
|
||||
cy.contains('Revisions');
|
||||
cy.contains('sidharthv96 v7851e19');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('should load diagram from raw files', () => {
|
||||
cy.visit(
|
||||
'/edit?code=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd&config=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json'
|
||||
);
|
||||
cy.contains('Party');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('should prevent setting the "securityLevel" option via URL', () => {
|
||||
const b64State = toBase64(
|
||||
`{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
||||
true
|
||||
);
|
||||
cy.on('window:confirm', () => true);
|
||||
cy.visit(`/edit#${b64State}`);
|
||||
cy.contains('Config').click();
|
||||
cy.contains('forest');
|
||||
cy.contains('securityLevel').should('not.exist');
|
||||
cy.get('#view').find('img').should('not.exist');
|
||||
cy.get('#view').contains('<img');
|
||||
cy.get('#view').contains('src="https://via.placeholder.com/64"');
|
||||
});
|
||||
|
||||
it('should allow persisting "securityLevel" using confirm dialogue', () => {
|
||||
const b64State = toBase64(
|
||||
`{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
||||
true
|
||||
);
|
||||
cy.on('window:confirm', () => false);
|
||||
cy.visit(`/edit#${b64State}`);
|
||||
cy.contains('Config').click();
|
||||
cy.contains('forest');
|
||||
cy.contains('securityLevel');
|
||||
cy.get('#view').find('img').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import * as fs from 'fs';
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('task', {
|
||||
readFileMaybe(filename) {
|
||||
if (fs.existsSync(filename)) {
|
||||
return fs.readFileSync(filename, 'utf8');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
module.exports = {
|
||||
"Site Loads": {
|
||||
"Check Home page load": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}"
|
||||
},
|
||||
"Check Redirect from old URL": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}"
|
||||
},
|
||||
"should load diagram from gist": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a\"}}}"
|
||||
},
|
||||
"should load diagram from gist revision": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19\"}}}"
|
||||
},
|
||||
"should load diagram from raw files": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
|
||||
}
|
||||
},
|
||||
"__version": "8.2.0",
|
||||
"Auto sync tests": {
|
||||
"should dim diagram when code is edited": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
|
||||
},
|
||||
"should not dim diagram when code is in sync": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
import { register } from '@cypress/snapshot';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
register();
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
snapshot(): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import 'cypress-localstorage-commands';
|
||||
@@ -0,0 +1,27 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (err.message.includes('ResizeObserver loop limit exceeded')) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "cypress-localstorage-commands"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
-2481
File diff suppressed because it is too large
Load Diff
-419
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
-134
@@ -1,134 +0,0 @@
|
||||
@media (prefers-color-scheme: light) {
|
||||
html, body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#dgraph-div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(0,100,200);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: rgb(0,80,160);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
input, button, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.4em;
|
||||
margin: 0 0 0.5em 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f4f4f4;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
border-color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #3f3f3f;
|
||||
color: #c8c8c8;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html, body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
|
||||
a {
|
||||
color: rgb(0,100,200);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: rgb(0,80,160);
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input, button, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.4em;
|
||||
margin: 0 0 0.5em 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f4f4f4;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
border-color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
|
||||
<title>Mermaid live editor</title>
|
||||
|
||||
<link rel='icon' type='image/png' href='favicon.png'>
|
||||
<link rel='stylesheet' href='styles.css'>
|
||||
<link rel='stylesheet' href='bundle.css'>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-153180559-1', 'auto');
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
|
||||
<style>
|
||||
.myInlineDecoration {
|
||||
/* cursor: pointer; */
|
||||
/* text-decoration: underline; */
|
||||
font-weight: bold;
|
||||
font-style: oblique;
|
||||
background: #ffcccc;
|
||||
}
|
||||
#dgraph-div {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<title>Online FlowChart & Diagrams Editor - Mermaid Live Editor</title>
|
||||
<meta name="og:image" content="https://mermaid-js.github.io/images/header.png">
|
||||
<link rel="canonical" href="https://mermaid-js.github.io/mermaid-live-editor/">
|
||||
<meta name="description" content="Simplify documentation and avoid heavy tools. Open source Visio Alternative. Commonly used for explaining your code! Mermaid is a simple markdown-like script language for generating charts from text via javascript.">
|
||||
</head>
|
||||
<body>
|
||||
<script src='bundle.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,43 +0,0 @@
|
||||
html {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 32px;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* @media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #3f3f3f;
|
||||
color: #c8c8c8;
|
||||
}
|
||||
a, label {
|
||||
color: #d8d8d8;
|
||||
}
|
||||
} */
|
||||
.mermaid {
|
||||
/* font-family: 'trebuchet ms', verdana, arial; */
|
||||
}
|
||||
.marketing-links {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ant-card {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.mermaidTooltip {
|
||||
position: absolute;
|
||||
}
|
||||
+68
-44
@@ -1,46 +1,70 @@
|
||||
{
|
||||
"name": "mermaid-live-editor",
|
||||
"version": "1.2.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^2.1.1",
|
||||
"husky": "^4.3.0",
|
||||
"lint-staged": "^10.5.2",
|
||||
"mini-css-extract-plugin": "^0.6.0",
|
||||
"prettier": "^2.2.1",
|
||||
"prettier-plugin-svelte": "^1.4.1",
|
||||
"serve": "^11.0.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"svelte": "^3.0.0",
|
||||
"svelte-loader": "2.13.3",
|
||||
"svelte-watch-resize": "^1.0.3",
|
||||
"webpack": "^4.30.0",
|
||||
"webpack-cli": "^3.3.0",
|
||||
"webpack-dev-server": "^3.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"release": "cross-env NODE_ENV=production webpack -p",
|
||||
"build": "cross-env NODE_ENV=production webpack",
|
||||
"dev": "webpack-dev-server --content-base docs",
|
||||
"pretty": "prettier --write ./src/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"mermaid": "8.8.4",
|
||||
"js-base64": "^2.5.1",
|
||||
"json-lint": "^0.1.0",
|
||||
"jsonlint": "^1.6.3",
|
||||
"moment": "^2.24.0",
|
||||
"monaco-editor": "0.18.1",
|
||||
"svelte-routing": "^1.4.0",
|
||||
"svelte-spa-router": "^1.3.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,svelte,js,css,md}": "prettier --plugin-search-dir=. --write"
|
||||
}
|
||||
"name": "mermaid-live-editor",
|
||||
"version": "2.0.67",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
"preview": "svelte-kit preview",
|
||||
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
"lint:fix": "prettier --write --plugin-search-dir=. . && eslint --fix --ignore-path .gitignore .",
|
||||
"format": "prettier --write --plugin-search-dir=. .",
|
||||
"pre-commit": "lint-staged",
|
||||
"postinstall": "husky install",
|
||||
"test": "cypress run",
|
||||
"cy": "cypress open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/snapshot": "^2.1.7",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.16",
|
||||
"@sveltejs/kit": "1.0.0-next.147",
|
||||
"@types/mermaid": "^8.2.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.1",
|
||||
"@typescript-eslint/parser": "^4.29.1",
|
||||
"autoprefixer": "^10.3.1",
|
||||
"chai": "^4.3.4",
|
||||
"cssnano": "^5.0.7",
|
||||
"cypress": "8.2.0",
|
||||
"cypress-localstorage-commands": "^1.5.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-cypress": "^2.11.3",
|
||||
"eslint-plugin-mocha": "^9.0.0",
|
||||
"eslint-plugin-postcss-modules": "^1.2.1",
|
||||
"eslint-plugin-svelte3": "^3.2.0",
|
||||
"eslint-plugin-tailwindcss": "^1.14.1",
|
||||
"husky": "^7.0.1",
|
||||
"lint-staged": "^11.1.2",
|
||||
"mocha": "^9.0.2",
|
||||
"postcss": "^8.3.6",
|
||||
"postcss-load-config": "^3.1.0",
|
||||
"prettier": "~2.3.2",
|
||||
"prettier-plugin-svelte": "^2.3.1",
|
||||
"svelte": "^3.42.1",
|
||||
"svelte-preprocess": "^4.7.4",
|
||||
"tailwindcss": "^2.2.7",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@analytics/google-analytics": "^0.5.3",
|
||||
"@macfja/svelte-persistent-store": "^1.1.1",
|
||||
"analytics": "^0.7.13",
|
||||
"js-base64": "^3.6.1",
|
||||
"mermaid": "^8.11.4",
|
||||
"moment": "^2.29.1",
|
||||
"monaco-editor": "^0.25.2",
|
||||
"random-word-slugs": "^0.0.5"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,svelte,js,css,md,json}": [
|
||||
"prettier --plugin-search-dir=. --write",
|
||||
"eslint --ignore-path .gitignore "
|
||||
]
|
||||
},
|
||||
"volta": {
|
||||
"node": "14.16.1",
|
||||
"yarn": "1.22.10",
|
||||
"npm": "7.11.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const cssnano = require('cssnano');
|
||||
|
||||
const mode = process.env.NODE_ENV;
|
||||
const dev = mode === 'development';
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// Some plugins, like postcss-nested, need to run before Tailwind
|
||||
|
||||
tailwindcss,
|
||||
|
||||
// But others, like autoprefixer, need to run after
|
||||
|
||||
autoprefixer,
|
||||
|
||||
!dev &&
|
||||
cssnano({
|
||||
preset: 'default'
|
||||
})
|
||||
]
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
<script>
|
||||
import Router from 'svelte-spa-router';
|
||||
// Used for SSR. A falsy value is ignored by the Router.
|
||||
import Edit from './routes/Edit.svelte';
|
||||
import View from './routes/View.svelte';
|
||||
|
||||
const routes = {
|
||||
// Exact path
|
||||
'/': Edit,
|
||||
|
||||
// Using named parameters, with last being optional
|
||||
'/edit/:data': Edit,
|
||||
|
||||
// Wildcard parameter
|
||||
'/view/:data': View,
|
||||
|
||||
// Catch-all
|
||||
// This is optional, but if present it must be the last
|
||||
'*': Edit,
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
|
||||
rel="stylesheet" />
|
||||
</svelte:head>
|
||||
<Router {routes} />
|
||||
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Online FlowChart & Diagrams Editor - Mermaid Live Editor</title>
|
||||
<meta
|
||||
name="og:image"
|
||||
content="https://github.com/mermaid-js/mermaid/raw/develop/img/header.png"
|
||||
/>
|
||||
<link rel="canonical" href="https://mermaid-js.github.io/mermaid-live-editor/" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Simplify documentation and avoid heavy tools. Open source Visio Alternative. Commonly used for explaining your code! Mermaid is a simple markdown-like script language for generating charts from text via javascript."
|
||||
/>
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/editor/editor.main.min.css"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<script>
|
||||
var require = {
|
||||
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs' }
|
||||
};
|
||||
</script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/loader.min.js"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/editor/editor.main.nls.min.js"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/vs/editor/editor.main.js"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.nav-btn {
|
||||
@apply lg:p-2 py-3 px-0 block border-b-2 border-transparent hover:border-white;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply bg-indigo-500 hover:bg-indigo-700 rounded px-4 shadow;
|
||||
}
|
||||
.action-btn {
|
||||
@apply rounded p-2 bg-indigo-400 shadow flex-auto text-white hover:bg-indigo-500;
|
||||
}
|
||||
|
||||
.input {
|
||||
@apply flex-1 border-indigo-400 border-solid border-2 rounded;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const codeErrorStore = writable(undefined);
|
||||
@@ -1,55 +0,0 @@
|
||||
import { writable, get } from 'svelte/store';
|
||||
// import mermaid from '@mermaid-js/mermaid';
|
||||
import mermaid from '@mermaid';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { push, pop, replace } from 'svelte-spa-router';
|
||||
|
||||
export const codeStore = writable({});
|
||||
export const fromUrl = (data) => {
|
||||
let code;
|
||||
let state;
|
||||
const isDarkMode =
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches && false;
|
||||
try {
|
||||
let stateStr = Base64.decode(data);
|
||||
console.log('state from url', stateStr);
|
||||
state = JSON.parse(stateStr);
|
||||
|
||||
if (state.code === undefined) {
|
||||
// not valid json
|
||||
// state = { code: '', mermaid: { theme: themeFromUrl } }
|
||||
}
|
||||
code = state.code;
|
||||
} catch (e) {
|
||||
// console.error('Init error', e);
|
||||
code = `graph 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]
|
||||
`;
|
||||
state = { code, mermaid: { theme: isDarkMode ? 'dark' : 'default' } };
|
||||
}
|
||||
|
||||
codeStore.set(state);
|
||||
};
|
||||
export const updateCodeStore = (newState) => {
|
||||
codeStore.set(newState);
|
||||
replace('/edit/' + Base64.encodeURI(JSON.stringify(newState)));
|
||||
};
|
||||
export const updateCode = (code, updateEditor) => {
|
||||
const state = get(codeStore);
|
||||
state.code = code;
|
||||
state.updateEditor = updateEditor;
|
||||
codeStore.set(state);
|
||||
};
|
||||
export const updateConfig = (config) => {
|
||||
const state = get(codeStore);
|
||||
state.mermaid = config;
|
||||
codeStore.set(state);
|
||||
};
|
||||
|
||||
const unsubscribe = codeStore.subscribe((state) => {
|
||||
replace('/edit/' + Base64.encodeURI(JSON.stringify(state)));
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
<script>
|
||||
export let title = '';
|
||||
export let noPadding = false;
|
||||
let contentClass = 'padding';
|
||||
if (noPadding) {
|
||||
contentClass = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#card {
|
||||
border: 1px solid lightgray;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
#title {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid lightgray;
|
||||
font-size: 1.25rem;
|
||||
background-color: #1e60ab;
|
||||
color: #eaebef;
|
||||
}
|
||||
#content {
|
||||
padding-top: 8px;
|
||||
}
|
||||
.padding {
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="card">
|
||||
<div id="title" class="padding">{title}</div>
|
||||
<div id="content" class={contentClass}>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,114 +0,0 @@
|
||||
<script>
|
||||
import {
|
||||
codeStore,
|
||||
updateCodeStore,
|
||||
updateConfig,
|
||||
updateCode,
|
||||
} from '../code-store.js';
|
||||
import { configErrorStore } from '../config-error-store.js';
|
||||
import { onMount } from 'svelte';
|
||||
import { push, pop, replace } from 'svelte-spa-router';
|
||||
import { Base64 } from 'js-base64';
|
||||
// import mermaid from '@mermaid-js/mermaid';
|
||||
import mermaid from '@mermaid';
|
||||
import Error from './Error.svelte';
|
||||
import { getResizeHandler, initEditor } from './editor-utils';
|
||||
import { watchResize } from 'svelte-watch-resize';
|
||||
import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js';
|
||||
import 'monaco-editor/esm/vs/editor/contrib/find/findController.js';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||
|
||||
export let conf = '';
|
||||
export let code = '';
|
||||
export let error = false;
|
||||
|
||||
let edit;
|
||||
let editorElem = null;
|
||||
|
||||
let decorations = [];
|
||||
const decArr = [];
|
||||
let resizeHandler = () => {};
|
||||
|
||||
let oldConf = {};
|
||||
const handleConfUpdate = (conf) => {
|
||||
try {
|
||||
console.log(conf);
|
||||
JSON.parse(conf);
|
||||
// let newState = { code, mermaid: JSON.parse(conf) };
|
||||
// oldConf = newState.mermaid;
|
||||
updateConfig(JSON.parse(conf));
|
||||
configErrorStore.set(undefined);
|
||||
// const model = edit.getModel();
|
||||
// // model.setValue(conf);
|
||||
// // model.dispose();
|
||||
} catch (e) {
|
||||
console.log('Error in parsed', e);
|
||||
configErrorStore.set(e);
|
||||
const str = JSON.stringify({ code, mermaid: oldConf });
|
||||
replace('/edit/' + Base64.encodeURI(str));
|
||||
}
|
||||
};
|
||||
|
||||
const unsubscribe = codeStore.subscribe((state) => {
|
||||
if (editorElem === null) {
|
||||
console.log('Starting stuff', document.getElementById('editor-conf'));
|
||||
editorElem = document.getElementById('editor-conf');
|
||||
}
|
||||
if (!conf && state) {
|
||||
conf = JSON.stringify(state.mermaid, null, 2);
|
||||
}
|
||||
if (state) {
|
||||
code = state.code;
|
||||
}
|
||||
if (!edit && conf && editorElem !== null) {
|
||||
edit = monaco.editor.create(editorElem, {
|
||||
value: [conf].join('\n'),
|
||||
theme: 'myCoolTheme',
|
||||
language: 'JSON',
|
||||
});
|
||||
resizeHandler = getResizeHandler(edit);
|
||||
let decorations = [];
|
||||
edit.onDidChangeModelContent(function (e) {
|
||||
const conf = edit.getValue();
|
||||
handleConfUpdate(conf);
|
||||
});
|
||||
handleConfUpdate(conf);
|
||||
}
|
||||
});
|
||||
|
||||
initEditor(monaco);
|
||||
|
||||
const unsubscribeError = configErrorStore.subscribe((_error) => {
|
||||
// console.log('Error ideintified' + _error.toString());
|
||||
if (_error) {
|
||||
error = _error.toString();
|
||||
} else {
|
||||
error = false;
|
||||
}
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
console.log('Mounting config');
|
||||
self.MonacoEnvironment = {
|
||||
getWorkerUrl: function (moduleId, label) {
|
||||
return './editor.worker.bundle.js';
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#editor-conf {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
max-height: 300px;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="editor-container">
|
||||
<div id="editor-conf" use:watchResize={resizeHandler} />
|
||||
{#if error}
|
||||
<Error errorText={error} />
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,136 +0,0 @@
|
||||
<script>
|
||||
import { codeStore, updateCodeStore } from '../code-store.js';
|
||||
import { codeErrorStore } from '../code-error-store.js';
|
||||
import { onMount } from 'svelte';
|
||||
import { push, pop, replace } from 'svelte-spa-router';
|
||||
import { Base64 } from 'js-base64';
|
||||
// import mermaid from '@mermaid-js/mermaid';
|
||||
import mermaid from '@mermaid';
|
||||
import Error from './Error.svelte';
|
||||
import { getResizeHandler, initEditor } from './editor-utils';
|
||||
import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js';
|
||||
import 'monaco-editor/esm/vs/editor/contrib/find/findController.js';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||
import { watchResize } from 'svelte-watch-resize';
|
||||
|
||||
export let code = '';
|
||||
const isDarkMode =
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches && false;
|
||||
export let conf = { theme: isDarkMode ? 'dark' : 'default' };
|
||||
let edit;
|
||||
export let error = false;
|
||||
|
||||
let decorations = [];
|
||||
const decArr = [];
|
||||
let editorElem = null;
|
||||
let resizeHandler = () => {};
|
||||
const handleCodeUpdate = (code) => {
|
||||
try {
|
||||
mermaid.parse(code);
|
||||
let newState = { code, mermaid: conf, updateEditor: false };
|
||||
updateCodeStore(newState);
|
||||
decArr.forEach((decor) => {
|
||||
edit.deltaDecorations(decor, []);
|
||||
});
|
||||
|
||||
codeErrorStore.set(undefined);
|
||||
const model = edit.getModel();
|
||||
} catch (e) {
|
||||
if (e) {
|
||||
codeErrorStore.set(e);
|
||||
console.log('Error in parsed', e.hash);
|
||||
const str = JSON.stringify({ code: code, mermaid: conf });
|
||||
replace('/edit/' + Base64.encodeURI(str));
|
||||
const l = e.hash.line;
|
||||
decArr.push(
|
||||
edit.deltaDecorations(
|
||||
[],
|
||||
[
|
||||
{
|
||||
range: new monaco.Range(
|
||||
e.hash.loc.first_line,
|
||||
e.hash.loc.last_line,
|
||||
e.hash.loc.first_column,
|
||||
e.hash.loc.last_column
|
||||
),
|
||||
options: { inlineClassName: 'myInlineDecoration' },
|
||||
},
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const unsubscribe = codeStore.subscribe((state) => {
|
||||
console.log('Code change');
|
||||
if (editorElem === null) {
|
||||
editorElem = document.getElementById('editor');
|
||||
}
|
||||
if (!code && state) {
|
||||
code = state.code;
|
||||
}
|
||||
if (state) {
|
||||
conf = state.mermaid;
|
||||
}
|
||||
if (!edit && code && editorElem !== null) {
|
||||
edit = monaco.editor.create(editorElem, {
|
||||
value: [code].join('\n'),
|
||||
theme: 'myCoolTheme',
|
||||
language: 'mermaid',
|
||||
});
|
||||
resizeHandler = getResizeHandler(edit);
|
||||
|
||||
let decorations = [];
|
||||
edit.onDidChangeModelContent(function (e) {
|
||||
const code = edit.getValue();
|
||||
handleCodeUpdate(code);
|
||||
});
|
||||
handleCodeUpdate(code);
|
||||
}
|
||||
if (state && state.updateEditor && edit && code && editorElem !== null) {
|
||||
edit.setValue(state.code);
|
||||
handleCodeUpdate(state.code);
|
||||
}
|
||||
});
|
||||
|
||||
const unsubscribeError = codeErrorStore.subscribe((_error) => {
|
||||
if (_error) {
|
||||
error = true;
|
||||
} else {
|
||||
error = false;
|
||||
}
|
||||
});
|
||||
|
||||
initEditor(monaco);
|
||||
|
||||
onMount(async () => {
|
||||
// editorElem = document.querySelector('#editor')
|
||||
self.MonacoEnvironment = {
|
||||
getWorkerUrl: function (moduleId, label) {
|
||||
return './editor.worker.bundle.js';
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// export let name;
|
||||
// export let params = {};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#editor-container {
|
||||
}
|
||||
#editor {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
max-height: 300px;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="editor-container">
|
||||
<div id="editor" use:watchResize={resizeHandler} />
|
||||
{#if error}
|
||||
<Error errorText="Syntax Error" />
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,49 +0,0 @@
|
||||
<script>
|
||||
import { codeErrorStore } from '../code-error-store.js';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(async () => {});
|
||||
|
||||
// export let code = '';
|
||||
export let classes = '';
|
||||
|
||||
// export let error = {};
|
||||
// export let token = '';
|
||||
// export let expected = '';
|
||||
|
||||
// export let params = {}
|
||||
export let errorText = '';
|
||||
|
||||
// const unsubscribeError = codeErrorStore.subscribe( _error => {
|
||||
// if(typeof _error === 'undefined') {
|
||||
// classes = 'invisible';
|
||||
// error = {};
|
||||
// token='';
|
||||
// expected='';
|
||||
// } else {
|
||||
// classes = 'visible';
|
||||
// error = _error;
|
||||
// console.log('error: ', _error);
|
||||
// token = error.hash.token;
|
||||
// expected = error.hash.expected.join(' ');;
|
||||
// }
|
||||
// });
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#error {
|
||||
background: darkred;
|
||||
color: white;
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
/* position: absolute; */
|
||||
}
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="error" class={classes}>{errorText}</div>
|
||||
@@ -1,228 +0,0 @@
|
||||
<script>
|
||||
import { link } from 'svelte-spa-router';
|
||||
import { Base64 } from 'js-base64';
|
||||
import moment from 'moment';
|
||||
import { codeStore } from '../code-store.js';
|
||||
|
||||
const getBase64SVG = () => {
|
||||
const container = document.getElementById('container');
|
||||
const svg = container.innerHTML.replaceAll('<br>', '<br/>');
|
||||
return Base64.encode(svg);
|
||||
};
|
||||
|
||||
const exportImage = (event, exporter) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const svg = document.querySelector('#container svg');
|
||||
const box = svg.getBoundingClientRect();
|
||||
canvas.width = box.width;
|
||||
canvas.height = box.height;
|
||||
if (imagemodeselected === 'width') {
|
||||
const ratio = box.height / box.width;
|
||||
canvas.width = userimagewidth;
|
||||
canvas.height = userimagewidth * ratio;
|
||||
} else if (imagemodeselected === 'height') {
|
||||
const ratio = box.width / box.height;
|
||||
canvas.width = userimageheight * ratio;
|
||||
canvas.height = userimageheight;
|
||||
}
|
||||
const context = canvas.getContext('2d');
|
||||
context.fillStyle = 'white';
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const image = new Image();
|
||||
image.onload = exporter(canvas, context, image);
|
||||
|
||||
console.log('SVG', getBase64SVG());
|
||||
image.src = `data:image/svg+xml;base64,${getBase64SVG()}`;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const downloadImage = (canvas, context, image) => {
|
||||
return () => {
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.download = `mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.png`;
|
||||
a.href = canvas
|
||||
.toDataURL('image/png')
|
||||
.replace('image/png', 'image/octet-stream');
|
||||
a.click();
|
||||
};
|
||||
};
|
||||
|
||||
const clipboardCopy = (canvas, context, image) => {
|
||||
return () => {
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
canvas.toBlob((blob) => {
|
||||
try {
|
||||
navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob,
|
||||
}),
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onCopyClipboard = (event) => {
|
||||
exportImage(event, clipboardCopy);
|
||||
};
|
||||
|
||||
const onDownloadPNG = (event) => {
|
||||
exportImage(event, downloadImage);
|
||||
};
|
||||
|
||||
const onDownloadSVG = (event) => {
|
||||
console.log('event', event.target);
|
||||
event.target.href = `data:image/svg+xml;base64,${getBase64SVG()}`;
|
||||
event.target.download = `mermaid-diagram-${moment().format(
|
||||
'YYYYMMDDHHmmss'
|
||||
)}.svg`;
|
||||
console.log('event', event);
|
||||
};
|
||||
|
||||
const onCopyMarkdown = (event) => {
|
||||
event.target.select();
|
||||
document.execCommand('Copy');
|
||||
};
|
||||
|
||||
let url = '/mermaid-live-editor/#/view';
|
||||
let b64Code;
|
||||
let iUrl;
|
||||
let svgUrl;
|
||||
let mdCode;
|
||||
let imagemodeselected = 'auto';
|
||||
let userimagewidth = 1920;
|
||||
let userimageheight = 1080;
|
||||
|
||||
const unsubscribe = codeStore.subscribe((state) => {
|
||||
b64Code = Base64.encodeURI(JSON.stringify(state));
|
||||
url = `/mermaid-live-editor/#/view/${b64Code}`;
|
||||
iUrl = `https://mermaid.ink/img/${b64Code}`;
|
||||
svgUrl = `https://mermaid.ink/svg/${b64Code}`;
|
||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#/edit/${b64Code})`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#links {
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid lightgray;
|
||||
}
|
||||
#markdown {
|
||||
padding: 7px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
width: 95%;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
label[for='markdown'] {
|
||||
cursor: pointer;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/*
|
||||
@media (prefers-color-scheme: dark) {
|
||||
label[for="markdown"] {
|
||||
color: #d8d8d8;
|
||||
}
|
||||
} */
|
||||
.button-style {
|
||||
background-color: #a2d9e2;
|
||||
color: #33a2c4;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #a2d9e2;
|
||||
margin: 0.25rem;
|
||||
}
|
||||
.button-style:hover {
|
||||
background-color: #fff;
|
||||
color: #33a2c4;
|
||||
border: 1px solid #33a2c4;
|
||||
}
|
||||
.button-style:focus {
|
||||
outline: none;
|
||||
}
|
||||
.link-style {
|
||||
text-decoration: none;
|
||||
color: #33a2c4;
|
||||
}
|
||||
#copy-section {
|
||||
padding-top: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="links">
|
||||
<button class="button-style">
|
||||
<a class="link-style" href={url} download="" on:click={onCopyClipboard}>
|
||||
Copy Image
|
||||
</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a class="link-style" href={url} download="" on:click={onDownloadPNG}>
|
||||
Download PNG
|
||||
</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a class="link-style" href={url} use:link>Link to view</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a class="link-style" href={url} download="" on:click={onDownloadSVG}>
|
||||
Download SVG
|
||||
</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a class="link-style" href={iUrl}>Link to Image</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a class="link-style" href={svgUrl}>Link to SVG</a>
|
||||
</button>
|
||||
(markdown is base64 encoded for these urls)
|
||||
</div>
|
||||
<div id="copy-section">
|
||||
<label for="markdown" class="button-style">Copy Markdown</label>
|
||||
<br />
|
||||
<input id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
|
||||
</div>
|
||||
<p>
|
||||
<label>PNG size:</label>
|
||||
<input
|
||||
type="radio"
|
||||
value="auto"
|
||||
id="autosize"
|
||||
bind:group={imagemodeselected} />
|
||||
<label for="autosize">auto</label>
|
||||
<input
|
||||
type="radio"
|
||||
value="width"
|
||||
id="width-active"
|
||||
bind:group={imagemodeselected} />
|
||||
<label for="width">width</label>
|
||||
<input
|
||||
id="width"
|
||||
type="number"
|
||||
min="3"
|
||||
max="10000"
|
||||
bind:value={userimagewidth}
|
||||
disabled={imagemodeselected !== 'width'} />
|
||||
<input
|
||||
type="radio"
|
||||
value="height"
|
||||
id="height-active"
|
||||
bind:group={imagemodeselected} />
|
||||
<label for="height">height</label>
|
||||
<input
|
||||
id="height"
|
||||
type="number"
|
||||
min="3"
|
||||
max="10000"
|
||||
bind:value={userimageheight}
|
||||
disabled={imagemodeselected !== 'height'} /><br />
|
||||
</p>
|
||||
@@ -1,31 +0,0 @@
|
||||
<script>
|
||||
export let noPadding = false;
|
||||
let contentClass = 'padding';
|
||||
if (noPadding) {
|
||||
contentClass = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#tag {
|
||||
height: auto;
|
||||
margin: 0 8px 0 0;
|
||||
padding: 0 7px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
cursor: default;
|
||||
font-size: smaller;
|
||||
/* padding: 8px; */
|
||||
color: #52c41a;
|
||||
background: #f6ffed;
|
||||
border-color: #b7eb8f;
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="tag">
|
||||
<slot />
|
||||
</div>
|
||||
@@ -1,124 +0,0 @@
|
||||
<script>
|
||||
import { codeStore } from '../code-store.js';
|
||||
import { codeErrorStore } from '../code-error-store.js';
|
||||
import { configErrorStore } from '../config-error-store.js';
|
||||
import { onMount } from 'svelte';
|
||||
// import mermaid from '@mermaid-js/mermaid';
|
||||
import mermaid from '@mermaid';
|
||||
|
||||
const detectType = (text) => {
|
||||
text = text.replace(/^\s*%%.*\n/g, '\n');
|
||||
console.debug('Detecting diagram type based on the text ' + text);
|
||||
if (text.match(/^\s*sequenceDiagram/)) {
|
||||
return 'sequence';
|
||||
}
|
||||
|
||||
if (text.match(/^\s*gantt/)) {
|
||||
return 'gantt';
|
||||
}
|
||||
|
||||
if (text.match(/^\s*classDiagram/)) {
|
||||
return 'class';
|
||||
}
|
||||
|
||||
if (text.match(/^\s*stateDiagram/)) {
|
||||
return 'state';
|
||||
}
|
||||
|
||||
if (text.match(/^\s*gitGraph/)) {
|
||||
return 'git';
|
||||
}
|
||||
if (text.match(/^\s*flowchart/)) {
|
||||
return 'flowchart';
|
||||
}
|
||||
|
||||
if (text.match(/^\s*info/)) {
|
||||
return 'info';
|
||||
}
|
||||
if (text.match(/^\s*pie/)) {
|
||||
return 'pie';
|
||||
}
|
||||
|
||||
return 'flowchart';
|
||||
};
|
||||
|
||||
// manual debounce
|
||||
let timeout;
|
||||
const saveStatistcs = (graphType) => {
|
||||
clearTimeout(timeout);
|
||||
// Only save statistcs after a 5 sec delay
|
||||
timeout = setTimeout(function () {
|
||||
console.log('ga:', 'send', 'event', 'render', graphType, graphType);
|
||||
ga('send', 'event', graphType, 'render', 'render');
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
let element;
|
||||
let container;
|
||||
onMount(async () => {
|
||||
element = document.querySelector('graph-div');
|
||||
});
|
||||
|
||||
let insertSvg = function (svgCode, bindFunctions) {
|
||||
// element.innerHTML = svgCode;
|
||||
};
|
||||
|
||||
export let code = '';
|
||||
export let configClasses = '';
|
||||
export let codeClasses = '';
|
||||
const unsubscribe = codeStore.subscribe((state) => {
|
||||
try {
|
||||
if (container && state) {
|
||||
code = state.code;
|
||||
|
||||
// mermaid.parse(code)
|
||||
// Replacing special characters '<' and '>' with encoded '<' and '>'
|
||||
let _code = code;
|
||||
_code = _code.replace(/</g, '<');
|
||||
_code = _code.replace(/>/g, '>');
|
||||
|
||||
container.innerHTML = _code;
|
||||
saveStatistcs(detectType(code));
|
||||
delete container.dataset.processed;
|
||||
mermaid.initialize(Object.assign({}, state.mermaid));
|
||||
mermaid.init(undefined, container);
|
||||
if (code) mermaid.render('graph-div', code, insertSvg);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('view fail', e);
|
||||
}
|
||||
});
|
||||
const unsubscribeError = codeErrorStore.subscribe((_error) => {
|
||||
if (typeof _error === 'undefined') {
|
||||
codeClasses = '';
|
||||
} else {
|
||||
codeClasses = 'error';
|
||||
console.log('code error: ', _error);
|
||||
}
|
||||
});
|
||||
const unsubscribeConfigError = configErrorStore.subscribe((_error) => {
|
||||
if (typeof _error === 'undefined') {
|
||||
configClasses = '';
|
||||
} else {
|
||||
configClasses = 'error';
|
||||
console.log('conf error: ', _error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#view {
|
||||
border: 1px solor darkred;
|
||||
flex: 1;
|
||||
}
|
||||
#container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.error {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="view" class="{codeClasses} {configClasses}">
|
||||
<div id="container" bind:this={container} />
|
||||
</div>
|
||||
@@ -1,86 +0,0 @@
|
||||
export const initEditor = (monaco) => {
|
||||
monaco.languages.register({ id: 'mermaid' });
|
||||
|
||||
// Register a tokens provider for the language
|
||||
monaco.languages.setMonarchTokensProvider('mermaid', {
|
||||
typeKeywords: [
|
||||
'graph',
|
||||
'stateDiagram',
|
||||
'sequenceDiagram',
|
||||
'classDiagram',
|
||||
'pie',
|
||||
'flowchart',
|
||||
'gantt',
|
||||
],
|
||||
keywords: ['patricipant', 'as'],
|
||||
arrows: ['---', '===', '-->', '==>'],
|
||||
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/[{}]/, 'delimiter.bracket'],
|
||||
[
|
||||
/[a-z_$][\w$]*/,
|
||||
{ cases: { '@typeKeywords': 'keyword', '@keywords': 'keyword' } },
|
||||
],
|
||||
[/[-=>ox]+/, { cases: { '@arrows': 'transition' } }],
|
||||
[/[\[\{\(}]+.+?[\)\]\}]+/, 'string'],
|
||||
[/\".*\"/, 'string'],
|
||||
],
|
||||
},
|
||||
whitespace: [
|
||||
[/[ \t\r\n]+/, 'white'],
|
||||
[/\%\%.*$/, 'comment'],
|
||||
],
|
||||
});
|
||||
|
||||
monaco.editor.defineTheme('myCoolTheme', {
|
||||
base: 'vs',
|
||||
inherit: false,
|
||||
rules: [
|
||||
{ token: 'keyword', foreground: '880000', fontStyle: 'bold' },
|
||||
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
|
||||
{ token: 'string', foreground: 'AA8500' },
|
||||
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
|
||||
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' },
|
||||
],
|
||||
});
|
||||
|
||||
// Register a completion item provider for the new language
|
||||
monaco.languages.registerCompletionItemProvider('mermaid', {
|
||||
provideCompletionItems: () => {
|
||||
var suggestions = [
|
||||
{
|
||||
label: 'simpleText',
|
||||
kind: monaco.languages.CompletionItemKind.Text,
|
||||
insertText: 'simpleText',
|
||||
},
|
||||
{
|
||||
label: 'testing',
|
||||
kind: monaco.languages.CompletionItemKind.Keyword,
|
||||
insertText: 'testing(${1:condition})',
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
},
|
||||
{
|
||||
label: 'ifelse',
|
||||
kind: monaco.languages.CompletionItemKind.Snippet,
|
||||
insertText: [
|
||||
'if (${1:condition}) {',
|
||||
'\t$0',
|
||||
'} else {',
|
||||
'\t',
|
||||
'}',
|
||||
].join('\n'),
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
documentation: 'If-Else Statement',
|
||||
},
|
||||
];
|
||||
return { suggestions: suggestions };
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const getResizeHandler = (editor) => {
|
||||
return (node) => editor && editor.layout();
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const configErrorStore = writable(undefined);
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
// (1) Desired editor features:
|
||||
import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js';
|
||||
// import 'monaco-editor/esm/vs/editor/browser/widget/codeEditorWidget.js';
|
||||
// import 'monaco-editor/esm/vs/editor/browser/widget/diffEditorWidget.js';
|
||||
// import 'monaco-editor/esm/vs/editor/browser/widget/diffNavigator.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/bracketMatching/bracketMatching.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/caretOperations/caretOperations.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/caretOperations/transpose.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/clipboard/clipboard.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/codelens/codelensController.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/colorPicker/colorDetector.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/comment/comment.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/contextmenu/contextmenu.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/cursorUndo/cursorUndo.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/dnd/dnd.js';
|
||||
import 'monaco-editor/esm/vs/editor/contrib/find/findController.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/folding/folding.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/format/formatActions.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/goToDeclaration/goToDeclarationCommands.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/goToDeclaration/goToDeclarationMouse.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/gotoError/gotoError.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/inPlaceReplace/inPlaceReplace.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/linesOperations/linesOperations.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/links/links.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/multicursor/multicursor.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/quickFix/quickFixCommands.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/referenceSearch/referenceSearch.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/rename/rename.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/smartSelect/smartSelect.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/snippet/snippetController2.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/wordHighlighter/wordHighlighter.js';
|
||||
// import 'monaco-editor/esm/vs/editor/contrib/wordOperations/wordOperations.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/inspectTokens/inspectTokens.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/quickOpen/quickOutline.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/quickOpen/gotoLine.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/quickOpen/quickCommand.js';
|
||||
// import 'monaco-editor/esm/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.js';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
|
||||
|
||||
// (2) Desired languages:
|
||||
// import 'monaco-editor/esm/vs/language/typescript/monaco.contribution';
|
||||
// import 'monaco-editor/esm/vs/language/css/monaco.contribution';
|
||||
// import 'monaco-editor/esm/vs/language/json/monaco.contribution';
|
||||
// import 'monaco-editor/esm/vs/language/html/monaco.contribution';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/bat/bat.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/coffee/coffee.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/cpp/cpp.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/csharp/csharp.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/csp/csp.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/css/css.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/fsharp/fsharp.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/go/go.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/handlebars/handlebars.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/html/html.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/java/java.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/less/less.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/lua/lua.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/msdax/msdax.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/mysql/mysql.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/objective-c/objective-c.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/pgsql/pgsql.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/php/php.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/postiats/postiats.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/powershell/powershell.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/pug/pug.contribution.js';
|
||||
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/r/r.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/razor/razor.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/redis/redis.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/redshift/redshift.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/ruby/ruby.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/sb/sb.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/scss/scss.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/solidity/solidity.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/swift/swift.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/vb/vb.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution';
|
||||
// import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution';
|
||||
|
||||
monaco.languages.register({ id: 'mermaid' });
|
||||
|
||||
// Register a tokens provider for the language
|
||||
monaco.languages.setMonarchTokensProvider('mermaid', {
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/\[graph.*/, 'custom-error'],
|
||||
[/\[notice.*/, 'custom-notice'],
|
||||
[/\[info.*/, 'custom-info'],
|
||||
[/\[[a-zA-Z 0-9:]+\]/, 'custom-date'],
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
self.MonacoEnvironment = {
|
||||
getWorkerUrl: function (moduleId, label) {
|
||||
return './editor.worker.bundle.js';
|
||||
},
|
||||
};
|
||||
|
||||
// Define a new theme that contains only rules that match this language
|
||||
monaco.editor.defineTheme('myCoolTheme', {
|
||||
base: 'vs',
|
||||
inherit: false,
|
||||
rules: [
|
||||
{ token: 'custom-info', foreground: '808080' },
|
||||
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
|
||||
{ token: 'custom-notice', foreground: 'FFA500' },
|
||||
{ token: 'custom-date', foreground: '008800' },
|
||||
],
|
||||
});
|
||||
|
||||
// Register a completion item provider for the new language
|
||||
monaco.languages.registerCompletionItemProvider('mySpecialLanguage', {
|
||||
provideCompletionItems: () => {
|
||||
var suggestions = [
|
||||
{
|
||||
label: 'simpleText',
|
||||
kind: monaco.languages.CompletionItemKind.Text,
|
||||
insertText: 'simpleText',
|
||||
},
|
||||
{
|
||||
label: 'testing',
|
||||
kind: monaco.languages.CompletionItemKind.Keyword,
|
||||
insertText: 'testing(${1:condition})',
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
},
|
||||
{
|
||||
label: 'ifelse',
|
||||
kind: monaco.languages.CompletionItemKind.Snippet,
|
||||
insertText: [
|
||||
'if (${1:condition}) {',
|
||||
'\t$0',
|
||||
'} else {',
|
||||
'\t',
|
||||
'}',
|
||||
].join('\n'),
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
documentation: 'If-Else Statement',
|
||||
},
|
||||
];
|
||||
return { suggestions: suggestions };
|
||||
},
|
||||
});
|
||||
|
||||
const edit = monaco.editor.create(document.getElementById('editor'), {
|
||||
value: [code].join('\n'),
|
||||
theme: 'myCoolTheme',
|
||||
// value: getCode(),
|
||||
//language: 'mySpecialLanguage'
|
||||
language: 'mermaid',
|
||||
});
|
||||
edit.onDidChangeModelContent(function (e) {
|
||||
codeStore.set(edit.getValue());
|
||||
});
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
@@ -0,0 +1,198 @@
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import type { State } from '$lib/types';
|
||||
import { codeStore } from '$lib/util/state';
|
||||
import { toBase64 } from 'js-base64';
|
||||
import moment from 'moment';
|
||||
|
||||
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
|
||||
|
||||
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
|
||||
svg?.setAttribute('height', `${height}px`);
|
||||
svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
|
||||
if (!svg) {
|
||||
svg = document.querySelector('#container svg');
|
||||
}
|
||||
const svgString = svg.outerHTML
|
||||
.replaceAll('<br>', '<br/>')
|
||||
.replaceAll(/<img([^>]*)>/g, (m, g) => `<img ${g} />`);
|
||||
return toBase64(svgString);
|
||||
};
|
||||
|
||||
const exportImage = (event: Event, exporter: Exporter) => {
|
||||
const canvas: HTMLCanvasElement = document.createElement('canvas');
|
||||
const svg: HTMLElement = document.querySelector('#container svg');
|
||||
const box: DOMRect = svg.getBoundingClientRect();
|
||||
canvas.width = box.width;
|
||||
canvas.height = box.height;
|
||||
if (imagemodeselected === 'width') {
|
||||
const ratio = box.height / box.width;
|
||||
canvas.width = userimagesize;
|
||||
canvas.height = userimagesize * ratio;
|
||||
} else if (imagemodeselected === 'height') {
|
||||
const ratio = box.width / box.height;
|
||||
canvas.width = userimagesize * ratio;
|
||||
canvas.height = userimagesize;
|
||||
}
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
context.fillStyle = 'white';
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const image = new Image();
|
||||
image.onload = exporter(context, image);
|
||||
image.src = `data:image/svg+xml;base64,${getBase64SVG(svg, canvas.width, canvas.height)}`;
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const simulateDownload = (download: string, href: string): void => {
|
||||
const a = document.createElement('a');
|
||||
a.download = download;
|
||||
a.href = href;
|
||||
a.click();
|
||||
a.remove();
|
||||
};
|
||||
const downloadImage: Exporter = (context, image) => {
|
||||
return () => {
|
||||
const { canvas } = context;
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
simulateDownload(
|
||||
`mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.png`,
|
||||
canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const isClipboardAvailable = (): boolean => {
|
||||
return Object.prototype.hasOwnProperty.call(window, 'ClipboardItem') as boolean;
|
||||
};
|
||||
|
||||
const clipboardCopy: Exporter = (context, image) => {
|
||||
return () => {
|
||||
const { canvas } = context;
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
canvas.toBlob((blob) => {
|
||||
try {
|
||||
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
|
||||
navigator.clipboard.write([
|
||||
/* eslint-disable no-undef */
|
||||
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob
|
||||
})
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onCopyClipboard = (event: Event) => {
|
||||
exportImage(event, clipboardCopy);
|
||||
};
|
||||
|
||||
const onDownloadPNG = (event: Event) => {
|
||||
exportImage(event, downloadImage);
|
||||
};
|
||||
|
||||
const onDownloadSVG = () => {
|
||||
simulateDownload(
|
||||
`mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.svg`,
|
||||
`data:image/svg+xml;base64,${getBase64SVG()}`
|
||||
);
|
||||
};
|
||||
|
||||
const onCopyMarkdown = () => {
|
||||
document.getElementById('markdown').select();
|
||||
document.execCommand('Copy');
|
||||
};
|
||||
|
||||
let gistURL = '';
|
||||
codeStore.subscribe((state) => {
|
||||
if (state.loader?.type === 'gist') {
|
||||
// @ts-ignore Gist will have url
|
||||
gistURL = state.loader.config.url;
|
||||
}
|
||||
});
|
||||
|
||||
const loadGist = () => {
|
||||
if (!gistURL) {
|
||||
alert('Please enter a Gist URL first');
|
||||
}
|
||||
window.location.href = `${window.location.pathname}?gist=${gistURL}`;
|
||||
};
|
||||
|
||||
let iUrl: string;
|
||||
let svgUrl: string;
|
||||
let mdCode: string;
|
||||
let imagemodeselected = 'auto';
|
||||
let userimagesize = 1080;
|
||||
|
||||
codeStore.subscribe((state: State) => {
|
||||
const stateCopy = JSON.parse(JSON.stringify(state));
|
||||
if (typeof stateCopy.mermaid === 'string') {
|
||||
stateCopy.mermaid = JSON.parse(stateCopy.mermaid);
|
||||
}
|
||||
const b64Code = toBase64(JSON.stringify(stateCopy), true);
|
||||
iUrl = `https://mermaid.ink/img/${b64Code}`;
|
||||
svgUrl = `https://mermaid.ink/svg/${b64Code}`;
|
||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${window.location.hash})`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<Card title="Actions" isOpen={false}>
|
||||
<div class="flex flex-wrap gap-2 m-2">
|
||||
{#if isClipboardAvailable()}
|
||||
<button class="action-btn w-full" on:click={onCopyClipboard}
|
||||
><i class="far fa-copy" /> Copy Image to clipboard
|
||||
</button>
|
||||
{/if}
|
||||
<button class="action-btn flex-auto" on:click={onDownloadPNG}>
|
||||
<i class="fas fa-download" /> PNG
|
||||
</button>
|
||||
<button class="action-btn flex-auto" on:click={onDownloadSVG}>
|
||||
<i class="fas fa-download" /> SVG
|
||||
</button>
|
||||
<button class="action-btn flex-auto">
|
||||
<a target="_blank" href={iUrl}><i class="fas fa-external-link-alt" /> PNG</a>
|
||||
</button>
|
||||
<button class="action-btn flex-auto">
|
||||
<a target="_blank" href={svgUrl}><i class="fas fa-external-link-alt" /> SVG</a>
|
||||
</button>
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
PNG size
|
||||
<input type="radio" value="auto" id="autosize" bind:group={imagemodeselected} />
|
||||
<label for="autosize">Auto</label>
|
||||
<input type="radio" value="width" id="width-active" bind:group={imagemodeselected} />
|
||||
<label for="width">Width</label>
|
||||
<input type="radio" value="height" id="height-active" bind:group={imagemodeselected} />
|
||||
<label for="height">Height</label>
|
||||
{#if imagemodeselected !== 'auto'}
|
||||
<input id="height" type="number" min="3" max="10000" bind:value={userimagesize} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="w-full flex gap-2 items-center">
|
||||
<input class="input" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
|
||||
<label for="markdown">
|
||||
<button class="btn text-white flex-auto" on:click={onCopyMarkdown}> Copy Markdown </button>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex gap-2 items-center">
|
||||
<input
|
||||
class="input"
|
||||
id="gist"
|
||||
type="text"
|
||||
bind:value={gistURL}
|
||||
placeholder="Enter Gist URL" />
|
||||
<label for="gist">
|
||||
<button class="btn text-white flex-auto" on:click={loadGist}> Load Gist </button>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import type { Tab } from '$lib/types';
|
||||
import { slide } from 'svelte/transition';
|
||||
import Tabs from './tabs.svelte';
|
||||
export let isCloseable = true;
|
||||
export let isOpen = true;
|
||||
export let tabs: Tab[] = [];
|
||||
export let title: string;
|
||||
$: isOpen = isCloseable ? isOpen : true;
|
||||
</script>
|
||||
|
||||
<div class="bg-white rounded overflow-hidden shadow m-2 flex-grow flex flex-col">
|
||||
<div class="bg-indigo-400 border-gray-400 p-2 flex-none" on:click={() => (isOpen = !isOpen)}>
|
||||
<div class="flex justify-between">
|
||||
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} />
|
||||
<div class="flex gap-x-4 items-center text-white">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if isOpen}
|
||||
<div class="flex-grow overflow-auto" transition:slide>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import type { Tab, TabEvents } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
export let isCloseable = true;
|
||||
export let tabs: Tab[] = [];
|
||||
export let title: string;
|
||||
export let isOpen = false;
|
||||
|
||||
$: activeTabID = tabs[0]?.id;
|
||||
|
||||
const dispatch = createEventDispatcher<TabEvents>();
|
||||
const toggleTabs = (tab: Tab) => {
|
||||
activeTabID = tab.id;
|
||||
dispatch('select', tab);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex cursor-default">
|
||||
<span class="text-white mr-2 font-semibold" on:click|stopPropagation={() => (isOpen = !isOpen)}>
|
||||
{#if isCloseable}
|
||||
<i class="fas fa-chevron-right icon" class:isOpen />
|
||||
{/if}
|
||||
{title}</span>
|
||||
{#if isOpen && tabs}
|
||||
<ul class="flex flex-wrap flex-row" transition:fade>
|
||||
{#each tabs as tab}
|
||||
<li class="mr-2 last:mr-0 w-28 h-6 flex-auto text-center">
|
||||
<div
|
||||
class="text cursor-pointer font-semibold min-w-16 w-auto px-2 py-1 -mb-4 rounded-t block leading-normal {activeTabID ===
|
||||
tab.id
|
||||
? 'text-indigo-500 bg-white border-white'
|
||||
: 'text-white bg-indigo-500 border-indigo-500 hover:bg-indigo-600'}"
|
||||
on:click|stopPropagation={() => toggleTabs(tab)}>
|
||||
<i class={tab.icon} />
|
||||
{tab.title}
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.icon {
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
.isOpen {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import type { EditorEvents } from '$lib/types';
|
||||
import { codeStore } from '$lib/util/state';
|
||||
import type monaco from 'monaco-editor';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { initEditor } from './util';
|
||||
|
||||
let divEl: HTMLDivElement = null;
|
||||
let editor: monaco.editor.IStandaloneCodeEditor;
|
||||
let Monaco;
|
||||
|
||||
export let text: string;
|
||||
export let language: string;
|
||||
export let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
value: text,
|
||||
language: language,
|
||||
scrollBeyondLastLine: false,
|
||||
minimap: {
|
||||
enabled: false
|
||||
},
|
||||
theme: 'myCoolTheme',
|
||||
overviewRulerLanes: 0
|
||||
};
|
||||
export let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||
let oldText = text;
|
||||
$: editor && Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
||||
$: {
|
||||
if (text !== oldText) {
|
||||
if ($codeStore.updateEditor) {
|
||||
editor?.setValue(text);
|
||||
}
|
||||
oldText = text;
|
||||
}
|
||||
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher<EditorEvents>();
|
||||
const loadMonaco = async () => {
|
||||
let i = 0;
|
||||
while (i++ < 10) {
|
||||
try {
|
||||
//@ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
|
||||
Monaco = monaco;
|
||||
return;
|
||||
} catch {
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
}
|
||||
alert('Loading Monaco Editor failed. Please try refreshing the page.');
|
||||
};
|
||||
onMount(async () => {
|
||||
try {
|
||||
//@ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
|
||||
Monaco = monaco;
|
||||
} catch {
|
||||
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
|
||||
}
|
||||
initEditor(Monaco);
|
||||
editor = Monaco.editor.create(divEl, editorOptions);
|
||||
editor.onDidChangeModelContent(() => {
|
||||
text = editor.getValue();
|
||||
dispatch('update', {
|
||||
text
|
||||
});
|
||||
});
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
editor.layout({
|
||||
height: entries[0].contentRect.height,
|
||||
width: entries[0].contentRect.width
|
||||
});
|
||||
});
|
||||
|
||||
resizeObserver.observe(divEl.parentElement);
|
||||
return () => {
|
||||
editor.dispose();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={divEl} id="editor" class="overflow-hidden" />
|
||||
@@ -0,0 +1,77 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const initEditor = (monacoEditor): void => {
|
||||
monacoEditor.languages.register({ id: 'mermaid' });
|
||||
|
||||
// Register a tokens provider for the language
|
||||
monacoEditor.languages.setMonarchTokensProvider('mermaid', {
|
||||
typeKeywords: [
|
||||
'graph',
|
||||
'stateDiagram',
|
||||
'sequenceDiagram',
|
||||
'classDiagram',
|
||||
'pie',
|
||||
'erDiagram',
|
||||
'flowchart',
|
||||
'gantt',
|
||||
'gitGraph',
|
||||
'journey'
|
||||
],
|
||||
keywords: ['participant', 'as'],
|
||||
arrows: ['---', '===', '-->', '==>', '->>', '->'],
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/[{}]/, 'delimiter.bracket'],
|
||||
[/[a-z_$][\w$]*/, { cases: { '@typeKeywords': 'keyword', '@keywords': 'keyword' } }],
|
||||
[/[-=>ox]+/, { cases: { '@arrows': 'transition' } }],
|
||||
[/[[{(}]+.+?[)\]}]+/, 'string'],
|
||||
[/".*"/, 'string']
|
||||
]
|
||||
},
|
||||
whitespace: [
|
||||
[/[ \t\r\n]+/, 'white'],
|
||||
[/%%.*$/, 'comment']
|
||||
]
|
||||
});
|
||||
|
||||
monacoEditor.editor.defineTheme('myCoolTheme', {
|
||||
base: 'vs',
|
||||
inherit: false,
|
||||
rules: [
|
||||
{ token: 'keyword', foreground: '880000', fontStyle: 'bold' },
|
||||
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
|
||||
{ token: 'string', foreground: 'AA8500' },
|
||||
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
|
||||
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' }
|
||||
]
|
||||
});
|
||||
|
||||
// Register a completion item provider for the new language
|
||||
monacoEditor.languages.registerCompletionItemProvider('mermaid', {
|
||||
provideCompletionItems: () => {
|
||||
const suggestions = [
|
||||
{
|
||||
label: 'simpleText',
|
||||
kind: monacoEditor.languages.CompletionItemKind.Text,
|
||||
insertText: 'simpleText'
|
||||
},
|
||||
{
|
||||
label: 'testing',
|
||||
kind: monacoEditor.languages.CompletionItemKind.Keyword,
|
||||
insertText: 'testing(${1:condition})',
|
||||
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet
|
||||
},
|
||||
{
|
||||
label: 'ifelse',
|
||||
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||
insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'),
|
||||
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
documentation: 'If-Else Statement'
|
||||
}
|
||||
];
|
||||
return { suggestions: suggestions };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import { codeStore, getStateString } from '$lib/util/state';
|
||||
import {
|
||||
addHistoryEntry,
|
||||
historyModeStore,
|
||||
clearHistoryData,
|
||||
getPreviousState,
|
||||
historyStore,
|
||||
loaderHistoryStore
|
||||
} from './history';
|
||||
import { notify, prompt } from '$lib/util/notify';
|
||||
import { onMount } from 'svelte';
|
||||
import moment from 'moment';
|
||||
import type { HistoryType, State, Tab } from '$lib/types';
|
||||
|
||||
const HISTORY_SAVE_INTERVAL = 60000;
|
||||
|
||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||
historyModeStore.set(message.detail.id as HistoryType);
|
||||
};
|
||||
let tabs: Tab[] = [
|
||||
{
|
||||
id: 'manual',
|
||||
title: 'Saved',
|
||||
icon: 'far fa-bookmark'
|
||||
},
|
||||
{
|
||||
id: 'auto',
|
||||
title: 'Timeline',
|
||||
icon: 'fas fa-history'
|
||||
}
|
||||
];
|
||||
|
||||
const saveHistory = (auto = false) => {
|
||||
const currentState: string = getStateString();
|
||||
const previousState: string = getPreviousState(auto);
|
||||
if (previousState !== currentState) {
|
||||
addHistoryEntry({
|
||||
state: $codeStore,
|
||||
time: Date.now(),
|
||||
type: auto ? 'auto' : 'manual'
|
||||
});
|
||||
} else if (!auto) {
|
||||
notify('State already saved.');
|
||||
}
|
||||
};
|
||||
|
||||
const clearHistory = (date?: number): void => {
|
||||
if (!date && !prompt('Clear all saved items?')) {
|
||||
return;
|
||||
}
|
||||
clearHistoryData(date);
|
||||
};
|
||||
|
||||
const restoreHistory = (state: State): void => {
|
||||
codeStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
||||
};
|
||||
|
||||
const relativeTime = (time: number) => {
|
||||
const t = new Date(time);
|
||||
return `${new Date(t).toLocaleString()} (${moment(t).fromNow()})`;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
historyModeStore.set('manual');
|
||||
setInterval(() => {
|
||||
saveHistory(true);
|
||||
}, HISTORY_SAVE_INTERVAL);
|
||||
});
|
||||
|
||||
loaderHistoryStore.subscribe((entries) => {
|
||||
if (entries.length > 0 && tabs.length === 2) {
|
||||
tabs = [
|
||||
{
|
||||
id: 'loader',
|
||||
title: 'Revisions',
|
||||
icon: 'fab fa-git-alt'
|
||||
},
|
||||
...tabs
|
||||
];
|
||||
historyModeStore.set('loader');
|
||||
}
|
||||
});
|
||||
|
||||
let isOpen = true;
|
||||
</script>
|
||||
|
||||
<Card on:select={tabSelectHandler} bind:isOpen {tabs} title="History">
|
||||
<div slot="actions">
|
||||
<button
|
||||
id="saveHistory"
|
||||
class="btn"
|
||||
on:click|stopPropagation={() => saveHistory()}
|
||||
title="Save current state"><i class="far fa-save" /></button>
|
||||
{#if $historyModeStore !== 'loader'}
|
||||
<button
|
||||
id="clearHistory"
|
||||
class="btn text-red-400"
|
||||
on:click|stopPropagation={() => clearHistory()}
|
||||
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
|
||||
{/if}
|
||||
</div>
|
||||
<ul class="p-2 space-y-2 overflow-auto h-56" id="historyList">
|
||||
{#if $historyStore.length > 0}
|
||||
{#each $historyStore as { state, time, name, url, type }}
|
||||
<li class="rounded p-2 shadow flex-col">
|
||||
<div class="flex">
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-col">
|
||||
{#if url}
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
title="Open revision in new tab"
|
||||
class="hover:underline text-blue-500">{name}</a>
|
||||
{:else}
|
||||
<span>{name}</span>
|
||||
{/if}
|
||||
<span class="text-gray-400 text-sm">{relativeTime(time)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 content-center">
|
||||
<button
|
||||
class="rounded px-2 w-24 bg-green-200 hover:bg-green-300"
|
||||
on:click={() => restoreHistory(state)}><i class="fas fa-undo" /> Restore</button>
|
||||
{#if type !== 'loader'}
|
||||
<button
|
||||
class="rounded px-2 w-24 bg-red-200 hover:bg-red-300"
|
||||
on:click={() => clearHistory(time)}><i class="fas fa-trash-alt" /> Delete</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="m-2 text-gray-600">
|
||||
No items in History<br />
|
||||
Click the Save button to save current state and restore it later.<br />
|
||||
Timeline will automatically be saved every minute.
|
||||
</div>
|
||||
{/if}
|
||||
</ul>
|
||||
</Card>
|
||||
@@ -0,0 +1,76 @@
|
||||
import { derived, Readable, Writable, writable, get } from 'svelte/store';
|
||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||
import { generateSlug } from 'random-word-slugs';
|
||||
import type { HistoryEntry, HistoryType } from '$lib/types';
|
||||
|
||||
const MAX_AUTO_HISTORY_LENGTH = 30;
|
||||
|
||||
export const historyModeStore: Writable<HistoryType> = persist(
|
||||
writable('manual'),
|
||||
localStorage(),
|
||||
'autoHistoryMode'
|
||||
);
|
||||
|
||||
const autoHistoryStore: Writable<HistoryEntry[]> = persist(
|
||||
writable([]),
|
||||
localStorage(),
|
||||
'autoHistoryStore'
|
||||
);
|
||||
|
||||
const manualHistoryStore: Writable<HistoryEntry[]> = persist(
|
||||
writable([]),
|
||||
localStorage(),
|
||||
'manualHistoryStore'
|
||||
);
|
||||
|
||||
export const loaderHistoryStore: Writable<HistoryEntry[]> = writable([] as HistoryEntry[]);
|
||||
|
||||
export const historyStore: Readable<HistoryEntry[]> = derived(
|
||||
[historyModeStore, autoHistoryStore, manualHistoryStore, loaderHistoryStore],
|
||||
([historyMode, autoHistories, manualHistories, loadedHistories], set) => {
|
||||
if (historyMode === 'auto') {
|
||||
set(autoHistories);
|
||||
} else if (historyMode === 'manual') {
|
||||
set(manualHistories);
|
||||
} else if (historyMode === 'loader') {
|
||||
set(loadedHistories);
|
||||
} else {
|
||||
set(autoHistories);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const addHistoryEntry = (entry: HistoryEntry): void => {
|
||||
if (entry.type === 'loader') {
|
||||
loaderHistoryStore.update((entries) => [entry, ...entries]);
|
||||
return;
|
||||
}
|
||||
entry.name = generateSlug(2);
|
||||
if (entry.type !== 'auto') {
|
||||
manualHistoryStore.update((entries) => [entry, ...entries]);
|
||||
return;
|
||||
}
|
||||
autoHistoryStore.update((entries) => {
|
||||
if (entries.length === MAX_AUTO_HISTORY_LENGTH) {
|
||||
entries.pop();
|
||||
}
|
||||
return [entry, ...entries];
|
||||
});
|
||||
};
|
||||
|
||||
export const clearHistoryData = (time?: number): void => {
|
||||
(get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
|
||||
if (get(historyModeStore) !== 'loader') {
|
||||
entries = entries.filter((entry) => time && entry.time != time);
|
||||
}
|
||||
return entries;
|
||||
});
|
||||
};
|
||||
|
||||
export const getPreviousState = (auto: boolean): string => {
|
||||
const entries = get(auto ? autoHistoryStore : manualHistoryStore);
|
||||
if (entries.length > 0) {
|
||||
return JSON.stringify(entries[0].state);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
<script context="module">
|
||||
import { version } from 'mermaid/package.json';
|
||||
import { analytics } from '$lib/util/stats';
|
||||
analytics?.track('version', {
|
||||
mermaidVersion: version
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
interface Link {
|
||||
title: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
}
|
||||
const links: Link[] = [
|
||||
{
|
||||
title: 'Documentation',
|
||||
href: 'https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted'
|
||||
},
|
||||
{
|
||||
title: 'Tutorial',
|
||||
href: 'https://github.com/mermaid-js/mermaid/blob/develop/docs/Tutorials.md'
|
||||
},
|
||||
{
|
||||
title: 'Mermaid',
|
||||
href: 'https://github.com/mermaid-js/mermaid'
|
||||
},
|
||||
{
|
||||
title: 'CLI',
|
||||
href: 'https://github.com/mermaid-js/mermaid-cli'
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
href: 'https://github.com/mermaid-js/mermaid-live-editor',
|
||||
icon: 'fab fa-github fa-lg'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<header class="p-2 bg-indigo-400 text-white font-bold flex flex-wrap items-center">
|
||||
<div class="flex-1 flex justify-between items-center">
|
||||
<a href="/">Mermaid<span class="text-xs font-thin">v{version}</span> Live Editor</a>
|
||||
</div>
|
||||
|
||||
<label for="menu-toggle" class="pointer-cursor lg:hidden block"
|
||||
><svg
|
||||
class="fill-current "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg
|
||||
></label>
|
||||
<input class="hidden" type="checkbox" id="menu-toggle" />
|
||||
|
||||
<div class="hidden lg:flex lg:items-center lg:w-auto w-full" id="menu">
|
||||
<nav>
|
||||
<ul class="lg:flex items-center justify-between text-base pt-4 lg:pt-0">
|
||||
{#each links as { title, href, icon }}
|
||||
<li>
|
||||
<a class="nav-btn" target="_blank" {href}>
|
||||
{#if icon}
|
||||
<i class={icon} />
|
||||
{/if}
|
||||
{title}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
#menu-toggle:checked + #menu {
|
||||
display: block;
|
||||
}
|
||||
header {
|
||||
z-index: 10000;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<script lang="ts">
|
||||
import { updateCode } from '$lib/util/state';
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
|
||||
const samples = {
|
||||
'Flow Chart': `graph 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]`,
|
||||
'Sequence Diagram': `sequenceDiagram
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
John-->>-Alice: I feel great!
|
||||
`,
|
||||
'Class Diagram': `classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
`,
|
||||
'State Diagram': `stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
`,
|
||||
'Gantt Chart': `gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
section Section
|
||||
A task :a1, 2014-01-01, 30d
|
||||
Another task :after a1 , 20d
|
||||
section Another
|
||||
Task in sec :2014-01-12 , 12d
|
||||
another task : 24d
|
||||
`,
|
||||
'Pie Chart': `pie title Pets adopted by volunteers
|
||||
"Dogs" : 386
|
||||
"Cats" : 85
|
||||
"Rats" : 15
|
||||
`,
|
||||
'ER Diagram': `erDiagram
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
CUSTOMER ||--o{ INVOICE : "liable for"
|
||||
DELIVERY-ADDRESS ||--o{ ORDER : receives
|
||||
INVOICE ||--|{ ORDER : covers
|
||||
ORDER ||--|{ ORDER-ITEM : includes
|
||||
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
||||
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
|
||||
`,
|
||||
'Git Graph': `gitGraph:
|
||||
options
|
||||
{
|
||||
"nodeSpacing": 150,
|
||||
"nodeRadius": 10
|
||||
}
|
||||
end
|
||||
commit
|
||||
branch newbranch
|
||||
checkout newbranch
|
||||
commit
|
||||
commit
|
||||
checkout master
|
||||
commit
|
||||
commit
|
||||
merge newbranch
|
||||
|
||||
`,
|
||||
'User Journey': ` journey
|
||||
title My working day
|
||||
section Go to work
|
||||
Make tea: 5: Me
|
||||
Go upstairs: 3: Me
|
||||
Do work: 1: Me, Cat
|
||||
section Go home
|
||||
Go downstairs: 5: Me
|
||||
Sit down: 3: Me
|
||||
`
|
||||
};
|
||||
|
||||
const loadSampleDiagram = (diagramType: string): void => {
|
||||
updateCode(samples[diagramType], true, true);
|
||||
};
|
||||
</script>
|
||||
|
||||
<Card title="Sample Diagrams" isOpen={false}>
|
||||
<div class="flex gap-2 flex-wrap p-2">
|
||||
{#each Object.keys(samples) as sample}
|
||||
<button class="action-btn" on:click={() => loadSampleDiagram(sample)}>{sample}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</Card>
|
||||
@@ -0,0 +1,71 @@
|
||||
<script lang="ts">
|
||||
import { errorStore } from '$lib/util/error';
|
||||
|
||||
import { codeStore } from '$lib/util/state';
|
||||
import { onMount } from 'svelte';
|
||||
import * as mermaid from 'mermaid';
|
||||
|
||||
let code = '';
|
||||
let config = '';
|
||||
let container: HTMLDivElement;
|
||||
let error = false;
|
||||
let outOfSync = false;
|
||||
let manualUpdate = true;
|
||||
onMount(() => {
|
||||
codeStore.subscribe((state) => {
|
||||
try {
|
||||
if (container && state && (state.updateDiagram || state.autoSync)) {
|
||||
if (!state.autoSync) {
|
||||
$codeStore.updateDiagram = false;
|
||||
}
|
||||
outOfSync = false;
|
||||
manualUpdate = true;
|
||||
if (code === state.code && config === state.mermaid) {
|
||||
// Do not render if there is no change in Code/Config
|
||||
return;
|
||||
}
|
||||
code = state.code;
|
||||
config = state.mermaid;
|
||||
const scroll = container.parentElement.parentElement.parentElement.scrollTop;
|
||||
delete container.dataset.processed;
|
||||
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
|
||||
mermaid.render('graph-div', code, (svgCode) => {
|
||||
container.innerHTML = svgCode;
|
||||
});
|
||||
container.parentElement.parentElement.parentElement.scrollTop = scroll;
|
||||
error = false;
|
||||
} else if (manualUpdate) {
|
||||
manualUpdate = false;
|
||||
} else {
|
||||
outOfSync = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('view fail', e);
|
||||
error = true;
|
||||
}
|
||||
});
|
||||
errorStore.subscribe((err) => {
|
||||
if (typeof err === 'undefined') {
|
||||
error = false;
|
||||
} else {
|
||||
error = true;
|
||||
console.log('Error: ', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="view" class="p-2" class:error class:outOfSync>
|
||||
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#view {
|
||||
border: 1px solor darkred;
|
||||
flex: 1;
|
||||
}
|
||||
.error,
|
||||
.outOfSync {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Can be made globally available by placing this
|
||||
* inside `global.d.ts` and removing `export` keyword
|
||||
*/
|
||||
export interface Locals {
|
||||
userid: string;
|
||||
}
|
||||
|
||||
export interface EditorUpdateEvent {
|
||||
text: string;
|
||||
}
|
||||
export interface EditorEvents {
|
||||
update: EditorUpdateEvent;
|
||||
}
|
||||
|
||||
export interface TabEvents {
|
||||
select: Tab;
|
||||
}
|
||||
|
||||
export interface Tab {
|
||||
id: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
code: string;
|
||||
mermaid: string;
|
||||
updateEditor: boolean;
|
||||
updateDiagram: boolean;
|
||||
autoSync: boolean;
|
||||
loader?: LoaderConfig;
|
||||
}
|
||||
|
||||
export interface GistLoaderConfig {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface LoadingState {
|
||||
loading: boolean;
|
||||
message?: string;
|
||||
}
|
||||
export interface FileLoaderConfig {
|
||||
codeURL: string;
|
||||
configURL?: string;
|
||||
}
|
||||
export interface LoaderConfig {
|
||||
type: 'gist' | 'files';
|
||||
config: GistLoaderConfig | FileLoaderConfig;
|
||||
}
|
||||
export type HistoryType = 'auto' | 'manual' | 'loader';
|
||||
export interface HistoryEntry {
|
||||
state: State;
|
||||
time: number;
|
||||
name?: string;
|
||||
type: HistoryType;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
type Loader = (url: string) => Promise<State>;
|
||||
@@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const errorStore = writable(undefined);
|
||||
@@ -0,0 +1,105 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { State } from '$lib/types';
|
||||
import { defaultState } from '../state';
|
||||
import { addHistoryEntry } from '../../components/history/history';
|
||||
|
||||
const codeFileName = 'code.mmd';
|
||||
const configFileName = 'config.json';
|
||||
|
||||
const isValidGist = (files: any): boolean => {
|
||||
return codeFileName in files;
|
||||
};
|
||||
|
||||
const getFileContent = async (file: any): Promise<string> => {
|
||||
if (file.truncated) {
|
||||
return await (await fetch(file.raw_url)).text();
|
||||
}
|
||||
return file.content;
|
||||
};
|
||||
|
||||
interface GistData {
|
||||
code: string;
|
||||
config?: string;
|
||||
author: string;
|
||||
time: number;
|
||||
version: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
const getGistData = async (gistURL: string): Promise<GistData> => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { html_url, files, history } = await (
|
||||
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
|
||||
).json();
|
||||
if (isValidGist(files)) {
|
||||
const code = await getFileContent(files[codeFileName]);
|
||||
let config: string;
|
||||
if (configFileName in files) {
|
||||
config = await getFileContent(files[configFileName]);
|
||||
}
|
||||
const currentItem = history[0];
|
||||
return {
|
||||
url: `${html_url}/${currentItem.version}`,
|
||||
code,
|
||||
config,
|
||||
author: currentItem.user.login,
|
||||
time: new Date(currentItem.committed_at).getTime(),
|
||||
version: (currentItem.version as string).slice(-7)
|
||||
};
|
||||
} else {
|
||||
throw 'Invalid gist provided';
|
||||
}
|
||||
};
|
||||
|
||||
const getStateFromGist = (gist: GistData, gistURL: string = gist.url): State => {
|
||||
const state: State = {
|
||||
...defaultState,
|
||||
code: gist.code,
|
||||
loader: {
|
||||
type: 'gist',
|
||||
config: {
|
||||
url: gistURL
|
||||
}
|
||||
}
|
||||
};
|
||||
gist.config && (state.mermaid = gist.config);
|
||||
return state;
|
||||
};
|
||||
|
||||
export const loadGistData = async (gistURL: string): Promise<State> => {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { history } = await (
|
||||
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
|
||||
).json();
|
||||
const gistHistory: GistData[] = [];
|
||||
for (const entry of history) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const data: GistData = await getGistData(entry.url).catch(() => undefined);
|
||||
data && gistHistory.push(data);
|
||||
}
|
||||
if (gistHistory.length === 0) {
|
||||
throw 'Invalid gist provided';
|
||||
}
|
||||
gistHistory.reverse();
|
||||
const state = getStateFromGist(gistHistory.slice(-1).pop(), gistURL);
|
||||
for (const gist of gistHistory) {
|
||||
addHistoryEntry({
|
||||
state: getStateFromGist(gist),
|
||||
time: gist.time,
|
||||
type: 'loader',
|
||||
url: gist.url,
|
||||
name: `${gist.author} v${gist.version}`
|
||||
});
|
||||
}
|
||||
return state;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import { loadGistData } from './gist';
|
||||
import { updateCodeStore, defaultState } from '../state';
|
||||
import type { Loader, State } from '$lib/types';
|
||||
const loaders: Record<string, Loader> = {
|
||||
gist: loadGistData
|
||||
};
|
||||
|
||||
export const loadDataFromUrl = async (): Promise<void> => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
let state: State = defaultState;
|
||||
let code: string, config: string;
|
||||
let loaded = false;
|
||||
const codeURL: string = searchParams.get('code');
|
||||
const configURL: string = searchParams.get('config');
|
||||
|
||||
if (codeURL) {
|
||||
code = await (await fetch(codeURL)).text();
|
||||
loaded = true;
|
||||
}
|
||||
if (configURL) {
|
||||
config = await (await fetch(configURL)).text();
|
||||
} else {
|
||||
config = defaultState.mermaid;
|
||||
}
|
||||
if (!code) {
|
||||
for (const [key, value] of searchParams.entries()) {
|
||||
if (key in loaders) {
|
||||
try {
|
||||
state = await loaders[key](value);
|
||||
loaded = true;
|
||||
break;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state = {
|
||||
code,
|
||||
mermaid: config,
|
||||
loader: {
|
||||
type: 'files',
|
||||
config: {
|
||||
codeURL,
|
||||
configURL
|
||||
}
|
||||
}
|
||||
} as State;
|
||||
}
|
||||
loaded &&
|
||||
updateCodeStore({
|
||||
...state,
|
||||
autoSync: true,
|
||||
updateDiagram: true,
|
||||
updateEditor: true
|
||||
});
|
||||
// window.location.search = '';
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { writable, Writable } from 'svelte/store';
|
||||
import type { LoadingState } from '$lib/types';
|
||||
|
||||
const defaultLoading: LoadingState = {
|
||||
loading: false
|
||||
};
|
||||
|
||||
export const loadingStateStore: Writable<LoadingState> = writable(defaultLoading);
|
||||
export const initLoading = async <T>(message: string, task: Promise<T>): Promise<T> => {
|
||||
loadingStateStore.set({
|
||||
loading: true,
|
||||
message
|
||||
});
|
||||
const result: T = await task;
|
||||
loadingStateStore.set({
|
||||
loading: false
|
||||
});
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
export const notify = (message: string): void => {
|
||||
alert(message);
|
||||
};
|
||||
|
||||
export const prompt = (message: string): boolean => {
|
||||
return confirm(message);
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
import { writable, get, derived } from 'svelte/store';
|
||||
import { toBase64, fromBase64 } from 'js-base64';
|
||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||
import type { State } from '$lib/types';
|
||||
import { saveStatistcs } from './stats';
|
||||
|
||||
export const defaultState: State = {
|
||||
code: `graph 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]
|
||||
`,
|
||||
mermaid: JSON.stringify(
|
||||
{
|
||||
theme: 'default'
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
updateEditor: false,
|
||||
autoSync: true,
|
||||
updateDiagram: true
|
||||
};
|
||||
|
||||
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
||||
export const base64State = derived([codeStore], ([code], set) => {
|
||||
set(toBase64(JSON.stringify(code), true));
|
||||
});
|
||||
|
||||
export const loadState = (data: string): void => {
|
||||
let state: State;
|
||||
try {
|
||||
const stateStr = fromBase64(data);
|
||||
console.log(`Tring to load state: ${stateStr}`);
|
||||
state = JSON.parse(stateStr);
|
||||
const mermaidConfig =
|
||||
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
|
||||
if (
|
||||
mermaidConfig.securityLevel &&
|
||||
mermaidConfig.securityLevel !== 'strict' &&
|
||||
confirm(
|
||||
`Removing "securityLevel":"${mermaidConfig.securityLevel}" from the config for safety.\nClick Cancel if you trust the source of this Diagram.`
|
||||
)
|
||||
) {
|
||||
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
|
||||
}
|
||||
|
||||
state.mermaid = JSON.stringify(mermaidConfig, null, 2);
|
||||
} catch (e) {
|
||||
if (data) {
|
||||
console.error('Init error', e);
|
||||
}
|
||||
state = get(codeStore);
|
||||
console.log(state);
|
||||
}
|
||||
updateCodeStore({ ...state, updateEditor: true });
|
||||
};
|
||||
|
||||
export const updateCodeStore = (newState: State): void => {
|
||||
codeStore.update((state) => {
|
||||
return { ...state, ...newState };
|
||||
});
|
||||
};
|
||||
|
||||
let prompted = false;
|
||||
export const updateCode = (code: string, updateEditor: boolean, updateDiagram = false): void => {
|
||||
saveStatistcs(code);
|
||||
const lines = (code.match(/\n/g) || '').length + 1;
|
||||
|
||||
if (lines > 50 && !prompted && get(codeStore).autoSync) {
|
||||
const turnOff = confirm(
|
||||
'Long diagram deteced. Turn off Auto Sync? Click the sync logo to manually sync.'
|
||||
);
|
||||
prompted = true;
|
||||
if (turnOff) {
|
||||
updateCodeStore({
|
||||
autoSync: false
|
||||
} as State);
|
||||
}
|
||||
}
|
||||
|
||||
codeStore.update((state) => {
|
||||
return { ...state, code, updateEditor, updateDiagram };
|
||||
});
|
||||
};
|
||||
|
||||
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
||||
codeStore.update((state) => {
|
||||
return { ...state, mermaid: config, updateEditor };
|
||||
});
|
||||
};
|
||||
|
||||
export const initURLSubscription = (): void => {
|
||||
base64State.subscribe((state: string) => {
|
||||
history.replaceState(undefined, undefined, `#${state}`);
|
||||
});
|
||||
};
|
||||
|
||||
export const getStateString = (): string => {
|
||||
return JSON.stringify(get(codeStore));
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { browser } from '$app/env';
|
||||
import type { AnalyticsInstance } from 'analytics';
|
||||
|
||||
export let analytics: AnalyticsInstance;
|
||||
|
||||
export const initAnalytics = async (): Promise<void> => {
|
||||
if (browser && !analytics) {
|
||||
try {
|
||||
const { Analytics } = await import('analytics');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const googleAnalytics = await import('@analytics/google-analytics');
|
||||
analytics = Analytics({
|
||||
app: 'mermaid-live-editor',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
plugins: [
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||
googleAnalytics.init({
|
||||
trackingId: 'UA-153180559-1'
|
||||
})
|
||||
]
|
||||
});
|
||||
} catch {
|
||||
console.info('Analytics blocked ;)');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const detectType = (text: string): string => {
|
||||
return text
|
||||
.replace(/^\s*%%.*\n/g, '\n')
|
||||
.trimStart()
|
||||
.split(' ')[0];
|
||||
};
|
||||
|
||||
// manual debounce
|
||||
let timeout;
|
||||
export const saveStatistcs = (graph: string): void => {
|
||||
if (analytics) {
|
||||
clearTimeout(timeout);
|
||||
// Only save statistcs after a 5 sec delay
|
||||
timeout = setTimeout(function () {
|
||||
const graphType = detectType(graph);
|
||||
console.debug('ga:', 'send', 'event', 'render', graphType);
|
||||
void analytics.track('render', {
|
||||
graphType
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { State } from '$lib/types';
|
||||
import { initURLSubscription, loadState, updateCodeStore } from './state';
|
||||
import { analytics, initAnalytics } from './stats';
|
||||
import { loadDataFromUrl } from './fileLoaders/loader';
|
||||
import { initLoading } from './loading';
|
||||
|
||||
export const loadStateFromURL = (): void => {
|
||||
loadState(window.location.hash.slice(1));
|
||||
};
|
||||
|
||||
export const syncDiagram = (): void => {
|
||||
updateCodeStore({
|
||||
updateDiagram: true
|
||||
} as State);
|
||||
};
|
||||
|
||||
export const initHandler = async (): Promise<void> => {
|
||||
loadStateFromURL();
|
||||
await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
|
||||
syncDiagram();
|
||||
initURLSubscription();
|
||||
await initAnalytics();
|
||||
analytics?.page();
|
||||
};
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
name: 'world',
|
||||
},
|
||||
});
|
||||
|
||||
window.app = app;
|
||||
|
||||
export default app;
|
||||
@@ -1,14 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
import { registerLanguage } from 'monaco-editor/dev/vs/basic-languages/_.contribution.js';
|
||||
registerLanguage({
|
||||
id: 'mermaid',
|
||||
extensions: ['.mmd'],
|
||||
aliases: ['mermaid', 'MERMAID'],
|
||||
loader: function () {
|
||||
return import('./mermaid.js');
|
||||
},
|
||||
});
|
||||
@@ -1,498 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
export var conf = {
|
||||
comments: {
|
||||
lineComment: '*',
|
||||
},
|
||||
brackets: [
|
||||
['[', ']'],
|
||||
['(', ')'],
|
||||
],
|
||||
};
|
||||
var abapKeywords = [
|
||||
'abstract',
|
||||
'add',
|
||||
'add-corresponding',
|
||||
'adjacent',
|
||||
'alias',
|
||||
'aliases',
|
||||
'all',
|
||||
'append',
|
||||
'appending',
|
||||
'ascending',
|
||||
'as',
|
||||
'assert',
|
||||
'assign',
|
||||
'assigned',
|
||||
'assigning',
|
||||
'association',
|
||||
'authority-check',
|
||||
'back',
|
||||
'begin',
|
||||
'binary',
|
||||
'block',
|
||||
'bound',
|
||||
'break-point',
|
||||
'by',
|
||||
'byte',
|
||||
'class',
|
||||
'call',
|
||||
'cast',
|
||||
'changing',
|
||||
'check',
|
||||
'class-data',
|
||||
'class-method',
|
||||
'class-methods',
|
||||
'clear',
|
||||
'close',
|
||||
'cnt',
|
||||
'collect',
|
||||
'commit',
|
||||
'cond',
|
||||
'character',
|
||||
'corresponding',
|
||||
'communication',
|
||||
'component',
|
||||
'compute',
|
||||
'concatenate',
|
||||
'condense',
|
||||
'constants',
|
||||
'conv',
|
||||
'count',
|
||||
'controls',
|
||||
'convert',
|
||||
'create',
|
||||
'currency',
|
||||
'data',
|
||||
'descending',
|
||||
'default',
|
||||
'define',
|
||||
'deferred',
|
||||
'delete',
|
||||
'describe',
|
||||
'detail',
|
||||
'display',
|
||||
'divide',
|
||||
'divide-corresponding',
|
||||
'display-mode',
|
||||
'duplicates',
|
||||
'deleting',
|
||||
'editor-call',
|
||||
'end',
|
||||
'endexec',
|
||||
'endfunction',
|
||||
'ending',
|
||||
'endmodule',
|
||||
'end-of-definition',
|
||||
'end-of-page',
|
||||
'end-of-selection',
|
||||
'end-test-injection',
|
||||
'end-test-seam',
|
||||
'exit-command',
|
||||
'endclass',
|
||||
'endmethod',
|
||||
'endform',
|
||||
'endinterface',
|
||||
'endprovide',
|
||||
'endselect',
|
||||
'endtry',
|
||||
'endwhile',
|
||||
'enum',
|
||||
'event',
|
||||
'events',
|
||||
'exec',
|
||||
'exit',
|
||||
'export',
|
||||
'exporting',
|
||||
'extract',
|
||||
'exception',
|
||||
'exceptions',
|
||||
'field-symbols',
|
||||
'field-groups',
|
||||
'field',
|
||||
'first',
|
||||
'fetch',
|
||||
'fields',
|
||||
'format',
|
||||
'frame',
|
||||
'free',
|
||||
'from',
|
||||
'function',
|
||||
'find',
|
||||
'for',
|
||||
'found',
|
||||
'function-pool',
|
||||
'generate',
|
||||
'get',
|
||||
'handle',
|
||||
'hide',
|
||||
'hashed',
|
||||
'include',
|
||||
'import',
|
||||
'importing',
|
||||
'index',
|
||||
'infotypes',
|
||||
'initial',
|
||||
'initialization',
|
||||
'id',
|
||||
'is',
|
||||
'in',
|
||||
'interface',
|
||||
'interfaces',
|
||||
'init',
|
||||
'input',
|
||||
'insert',
|
||||
'instance',
|
||||
'into',
|
||||
'key',
|
||||
'left-justified',
|
||||
'leave',
|
||||
'like',
|
||||
'line',
|
||||
'line-count',
|
||||
'line-size',
|
||||
'load',
|
||||
'local',
|
||||
'log-point',
|
||||
'length',
|
||||
'left',
|
||||
'leading',
|
||||
'lower',
|
||||
'matchcode',
|
||||
'method',
|
||||
'mesh',
|
||||
'message',
|
||||
'message-id',
|
||||
'methods',
|
||||
'modify',
|
||||
'module',
|
||||
'move',
|
||||
'move-corresponding',
|
||||
'multiply',
|
||||
'multiply-corresponding',
|
||||
'match',
|
||||
'new',
|
||||
'new-line',
|
||||
'new-page',
|
||||
'new-section',
|
||||
'next',
|
||||
'no',
|
||||
'no-gap',
|
||||
'no-gaps',
|
||||
'no-sign',
|
||||
'no-zero',
|
||||
'non-unique',
|
||||
'number',
|
||||
'occurrence',
|
||||
'object',
|
||||
'obligatory',
|
||||
'of',
|
||||
'output',
|
||||
'overlay',
|
||||
'optional',
|
||||
'others',
|
||||
'occurrences',
|
||||
'occurs',
|
||||
'offset',
|
||||
'options',
|
||||
'pack',
|
||||
'parameters',
|
||||
'perform',
|
||||
'places',
|
||||
'position',
|
||||
'print-control',
|
||||
'private',
|
||||
'program',
|
||||
'protected',
|
||||
'provide',
|
||||
'public',
|
||||
'put',
|
||||
'radiobutton',
|
||||
'raising',
|
||||
'ranges',
|
||||
'receive',
|
||||
'receiving',
|
||||
'redefinition',
|
||||
'reduce',
|
||||
'reference',
|
||||
'refresh',
|
||||
'regex',
|
||||
'reject',
|
||||
'results',
|
||||
'requested',
|
||||
'ref',
|
||||
'replace',
|
||||
'report',
|
||||
'reserve',
|
||||
'restore',
|
||||
'result',
|
||||
'return',
|
||||
'returning',
|
||||
'right-justified',
|
||||
'rollback',
|
||||
'read',
|
||||
'read-only',
|
||||
'rp-provide-from-last',
|
||||
'run',
|
||||
'scan',
|
||||
'screen',
|
||||
'scroll',
|
||||
'search',
|
||||
'select',
|
||||
'select-options',
|
||||
'selection-screen',
|
||||
'stamp',
|
||||
'source',
|
||||
'subkey',
|
||||
'separated',
|
||||
'set',
|
||||
'shift',
|
||||
'single',
|
||||
'skip',
|
||||
'sort',
|
||||
'sorted',
|
||||
'split',
|
||||
'standard',
|
||||
'stamp',
|
||||
'starting',
|
||||
'start-of-selection',
|
||||
'sum',
|
||||
'subtract-corresponding',
|
||||
'statics',
|
||||
'step',
|
||||
'stop',
|
||||
'structure',
|
||||
'submatches',
|
||||
'submit',
|
||||
'subtract',
|
||||
'summary',
|
||||
'supplied',
|
||||
'suppress',
|
||||
'section',
|
||||
'syntax-check',
|
||||
'syntax-trace',
|
||||
'system-call',
|
||||
'switch',
|
||||
'tables',
|
||||
'table',
|
||||
'task',
|
||||
'testing',
|
||||
'test-seam',
|
||||
'test-injection',
|
||||
'then',
|
||||
'time',
|
||||
'times',
|
||||
'title',
|
||||
'titlebar',
|
||||
'to',
|
||||
'top-of-page',
|
||||
'trailing',
|
||||
'transfer',
|
||||
'transformation',
|
||||
'translate',
|
||||
'transporting',
|
||||
'types',
|
||||
'type',
|
||||
'type-pool',
|
||||
'type-pools',
|
||||
'unassign',
|
||||
'unique',
|
||||
'uline',
|
||||
'unpack',
|
||||
'update',
|
||||
'upper',
|
||||
'using',
|
||||
'value',
|
||||
'when',
|
||||
'while',
|
||||
'window',
|
||||
'write',
|
||||
'where',
|
||||
'with',
|
||||
'work',
|
||||
'at',
|
||||
'case',
|
||||
'catch',
|
||||
'continue',
|
||||
'do',
|
||||
'elseif',
|
||||
'else',
|
||||
'endat',
|
||||
'endcase',
|
||||
'enddo',
|
||||
'endif',
|
||||
'endloop',
|
||||
'endon',
|
||||
'if',
|
||||
'loop',
|
||||
'on',
|
||||
'raise',
|
||||
'try',
|
||||
'abs',
|
||||
'sign',
|
||||
'ceil',
|
||||
'floor',
|
||||
'trunc',
|
||||
'frac',
|
||||
'acos',
|
||||
'asin',
|
||||
'atan',
|
||||
'cos',
|
||||
'sin',
|
||||
'tan',
|
||||
'cosh',
|
||||
'sinh',
|
||||
'tanh',
|
||||
'exp',
|
||||
'log',
|
||||
'log10',
|
||||
'sqrt',
|
||||
'strlen',
|
||||
'xstrlen',
|
||||
'charlen',
|
||||
'lines',
|
||||
'numofchar',
|
||||
'dbmaxlen',
|
||||
'round',
|
||||
'rescale',
|
||||
'nmax',
|
||||
'nmin',
|
||||
'cmax',
|
||||
'cmin',
|
||||
'boolc',
|
||||
'boolx',
|
||||
'xsdbool',
|
||||
'contains',
|
||||
'contains_any_of',
|
||||
'contains_any_not_of',
|
||||
'matches',
|
||||
'line_exists',
|
||||
'ipow',
|
||||
'char_off',
|
||||
'count',
|
||||
'count_any_of',
|
||||
'count_any_not_of',
|
||||
'distance',
|
||||
'condense',
|
||||
'concat_lines_of',
|
||||
'escape',
|
||||
'find',
|
||||
'find_end',
|
||||
'find_any_of',
|
||||
'find_any_not_of',
|
||||
'insert',
|
||||
'match',
|
||||
'repeat',
|
||||
'replace',
|
||||
'reverse',
|
||||
'segment',
|
||||
'shift_left',
|
||||
'shift_right',
|
||||
'substring',
|
||||
'substring_after',
|
||||
'substring_from',
|
||||
'substring_before',
|
||||
'substring_to',
|
||||
'to_upper',
|
||||
'to_lower',
|
||||
'to_mixed',
|
||||
'from_mixed',
|
||||
'translate',
|
||||
'bit-set',
|
||||
'line_index',
|
||||
'definition',
|
||||
'implementation',
|
||||
'public',
|
||||
'inheriting',
|
||||
'final',
|
||||
];
|
||||
export var language = {
|
||||
defaultToken: 'invalid',
|
||||
ignoreCase: true,
|
||||
tokenPostfix: '.abap',
|
||||
keywords: abapKeywords,
|
||||
typeKeywords: [
|
||||
'abap_bool',
|
||||
'string',
|
||||
'xstring',
|
||||
'any',
|
||||
'clike',
|
||||
'csequence',
|
||||
'numeric',
|
||||
'xsequence',
|
||||
'c',
|
||||
'n',
|
||||
'i',
|
||||
'p',
|
||||
'f',
|
||||
'd',
|
||||
't',
|
||||
'x',
|
||||
],
|
||||
operators: [
|
||||
'+',
|
||||
'-',
|
||||
'/',
|
||||
'*',
|
||||
'=',
|
||||
'<',
|
||||
'>',
|
||||
'<=',
|
||||
'>=',
|
||||
'<>',
|
||||
'><',
|
||||
'=<',
|
||||
'=>',
|
||||
'EQ',
|
||||
'NE',
|
||||
'GE',
|
||||
'LE',
|
||||
'CS',
|
||||
'CN',
|
||||
'CA',
|
||||
'CO',
|
||||
'CP',
|
||||
'NS',
|
||||
'NA',
|
||||
'NP',
|
||||
],
|
||||
symbols: /[=><!~?&+\-*\/\^%]+/,
|
||||
tokenizer: {
|
||||
root: [
|
||||
[
|
||||
/[a-z_$][\w$]*/,
|
||||
{
|
||||
cases: {
|
||||
'@typeKeywords': 'keyword',
|
||||
'@keywords': 'keyword',
|
||||
'@default': 'identifier',
|
||||
},
|
||||
},
|
||||
],
|
||||
{ include: '@whitespace' },
|
||||
[/[:,.]/, 'delimiter'],
|
||||
[/[{}()\[\]]/, '@brackets'],
|
||||
[/@symbols/, { cases: { '@operators': 'operator', '@default': '' } }],
|
||||
[/'/, { token: 'string', bracket: '@open', next: '@stringquote' }],
|
||||
[/\|/, { token: 'string', bracket: '@open', next: '@stringtemplate' }],
|
||||
[/\d+/, 'number'],
|
||||
],
|
||||
stringtemplate: [
|
||||
[/[^\\\|]+/, 'string'],
|
||||
[/\\\|/, 'string'],
|
||||
[/\|/, { token: 'string', bracket: '@close', next: '@pop' }],
|
||||
],
|
||||
stringquote: [
|
||||
[/[^\\']+/, 'string'],
|
||||
[/'/, { token: 'string', bracket: '@close', next: '@pop' }],
|
||||
],
|
||||
whitespace: [
|
||||
[/[ \t\r\n]+/, ''],
|
||||
[/^\*.*$/, 'comment'],
|
||||
[/\".*$/, 'comment'],
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,374 +0,0 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { codeStore, updateCode, updateCodeStore } from '../code-store.js';
|
||||
import Editor from '../components/Editor.svelte';
|
||||
import Config from '../components/Config.svelte';
|
||||
import View from '../components/View.svelte';
|
||||
import Card from '../components/Card.svelte';
|
||||
import Tag from '../components/Tag.svelte';
|
||||
import Links from '../components/Links.svelte';
|
||||
import { fromUrl } from '../code-store.js';
|
||||
// import pkg from '@mermaid-js/mermaid/package.json'
|
||||
import pkg from '@mermaid/package.json';
|
||||
import moment from 'moment';
|
||||
export let mermaidVersion = pkg.version;
|
||||
let historyList = [];
|
||||
onMount(async () => {
|
||||
ga('send', 'pageview');
|
||||
ga('send', 'event', 'version', mermaidVersion, mermaidVersion);
|
||||
|
||||
const historyListKey = '_mermaid_history_';
|
||||
historyList = JSON.parse(localStorage.getItem(historyListKey) || '[]');
|
||||
let hisCode =
|
||||
historyList.length > 0 ? historyList[historyList.length - 1] : null;
|
||||
|
||||
if (params.data) {
|
||||
fromUrl(params.data);
|
||||
} else if (hisCode) {
|
||||
updateCodeStore({
|
||||
code: hisCode.code,
|
||||
mermaid: {},
|
||||
updateEditor: true,
|
||||
});
|
||||
}
|
||||
|
||||
let code = null;
|
||||
codeStore.subscribe((state) => {
|
||||
code = (state && state.code) || code;
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
if (code != hisCode) {
|
||||
//save history
|
||||
historyList.unshift({
|
||||
time: new Date().toISOString(),
|
||||
code: (hisCode = code),
|
||||
});
|
||||
if (historyList.length > 10) {
|
||||
historyList.pop();
|
||||
}
|
||||
localStorage.setItem(historyListKey, JSON.stringify(historyList));
|
||||
}
|
||||
historyList = historyList; //triggered update
|
||||
}, 1 * 60 * 1000);
|
||||
});
|
||||
|
||||
// export let code = '';
|
||||
// export let classes = '';
|
||||
// export let error = {};
|
||||
// export let token = '';
|
||||
// export let expected = '';
|
||||
export let params = {};
|
||||
function loadFlowChart() {
|
||||
loadSampleDiagram('FlowChart');
|
||||
}
|
||||
function loadSequenceDiagram() {
|
||||
loadSampleDiagram('SequenceDiagram');
|
||||
}
|
||||
function loadClassDiagram() {
|
||||
loadSampleDiagram('ClassDiagram');
|
||||
}
|
||||
function loadStateDiagram() {
|
||||
loadSampleDiagram('StateDiagram');
|
||||
}
|
||||
function loadGanttChart() {
|
||||
loadSampleDiagram('GanttChart');
|
||||
}
|
||||
function loadPieChart() {
|
||||
loadSampleDiagram('PieChart');
|
||||
}
|
||||
function loadERDiagram() {
|
||||
loadSampleDiagram('ERDiagram');
|
||||
}
|
||||
function loadSampleDiagram(diagramType) {
|
||||
let code = '';
|
||||
switch (diagramType) {
|
||||
case 'FlowChart':
|
||||
code = `graph 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]
|
||||
`;
|
||||
break;
|
||||
case 'SequenceDiagram':
|
||||
code = `sequenceDiagram
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
John-->>-Alice: I feel great!
|
||||
`;
|
||||
break;
|
||||
case 'ClassDiagram':
|
||||
code = `classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
`;
|
||||
break;
|
||||
case 'StateDiagram':
|
||||
code = `stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
`;
|
||||
break;
|
||||
case 'GanttChart':
|
||||
code = `gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
section Section
|
||||
A task :a1, 2014-01-01, 30d
|
||||
Another task :after a1 , 20d
|
||||
section Another
|
||||
Task in sec :2014-01-12 , 12d
|
||||
another task : 24d
|
||||
`;
|
||||
break;
|
||||
case 'PieChart':
|
||||
code = `pie title Pets adopted by volunteers
|
||||
"Dogs" : 386
|
||||
"Cats" : 85
|
||||
"Rats" : 15
|
||||
`;
|
||||
break;
|
||||
case 'ERDiagram':
|
||||
code = `erDiagram
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
CUSTOMER ||--o{ INVOICE : "liable for"
|
||||
DELIVERY-ADDRESS ||--o{ ORDER : receives
|
||||
INVOICE ||--|{ ORDER : covers
|
||||
ORDER ||--|{ ORDER-ITEM : includes
|
||||
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
||||
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
|
||||
`;
|
||||
break;
|
||||
}
|
||||
toUpdateCodeStore(code);
|
||||
}
|
||||
|
||||
function toUpdateCodeStore(code) {
|
||||
if (!code) return;
|
||||
updateCode(code, true);
|
||||
}
|
||||
|
||||
function relativeTime(t) {
|
||||
return `${moment(t).fromNow()} (${new Date(t).toLocaleString()})`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background-color: #fcfbfc;
|
||||
}
|
||||
#editor-root {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
#col1 {
|
||||
width: 35%;
|
||||
}
|
||||
#col2 {
|
||||
width: 65%;
|
||||
padding-left: 32px;
|
||||
}
|
||||
#link-root {
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
}
|
||||
#link-col1 {
|
||||
width: 50%;
|
||||
}
|
||||
#link-col2 {
|
||||
width: 50%;
|
||||
padding-left: 32px;
|
||||
}
|
||||
#app-title {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
color: #1e60ab;
|
||||
opacity: 0.8;
|
||||
}
|
||||
#title-container {
|
||||
width: fit-content;
|
||||
margin: 0 auto 8px;
|
||||
}
|
||||
#power {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 4rem;
|
||||
}
|
||||
#sampleLoader {
|
||||
padding-left: 10px;
|
||||
border-bottom: 1px solid lightgray;
|
||||
}
|
||||
#historyLoaderSubTitle {
|
||||
display: inline-block;
|
||||
color: #33a2c4;
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
}
|
||||
.button-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.button-style {
|
||||
background-color: #a2d9e2;
|
||||
color: #33a2c4;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #a2d9e2;
|
||||
margin: 0.25rem;
|
||||
}
|
||||
.button-style:hover {
|
||||
background-color: #fff;
|
||||
color: #33a2c4;
|
||||
border: 1px solid #33a2c4;
|
||||
}
|
||||
.button-style:focus {
|
||||
outline: none;
|
||||
}
|
||||
.link-style {
|
||||
text-decoration: none;
|
||||
color: #33a2c4;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="body">
|
||||
<div id="title-container">
|
||||
<h1 id="app-title">Mermaid Live Editor</h1>
|
||||
</div>
|
||||
<div id="sampleLoader">
|
||||
<div class="button-container">
|
||||
<span id="sampleLoaderTitle"><strong>Diagram presets:</strong></span>
|
||||
<button class="button-style" on:click={loadFlowChart}>
|
||||
Flow Chart
|
||||
</button>
|
||||
<button class="button-style" on:click={loadSequenceDiagram}>
|
||||
Sequence Diagram
|
||||
</button>
|
||||
<button class="button-style" on:click={loadClassDiagram}>
|
||||
Class Diagram
|
||||
</button>
|
||||
<button class="button-style" on:click={loadStateDiagram}>
|
||||
State Diagram
|
||||
</button>
|
||||
<button class="button-style" on:click={loadGanttChart}>
|
||||
Gantt Chart
|
||||
</button>
|
||||
<button class="button-style" on:click={loadPieChart}> Pie Chart </button>
|
||||
<button class="button-style" on:click={loadERDiagram}>
|
||||
ER Diagram
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="editor-root">
|
||||
<div id="col1">
|
||||
<Card title="Code" noPadding="true">
|
||||
<Editor data={params.data} />
|
||||
</Card>
|
||||
<Card title="Mermaid Configuration">
|
||||
<Config />
|
||||
</Card>
|
||||
</div>
|
||||
<div id="col2">
|
||||
<Card title="Preview">
|
||||
<View />
|
||||
</Card>
|
||||
<div id="link-root">
|
||||
<div id="link-col1">
|
||||
<Card title="Editing history">
|
||||
<span id="historyLoaderSubTitle">Automatically saves once every
|
||||
minute, up to 10 records.</span>
|
||||
<br />
|
||||
<div id="historyList" class="button-container">
|
||||
{#if historyList.length > 0}
|
||||
{#each historyList as item, i}
|
||||
<button
|
||||
class="button-style"
|
||||
on:click={(e) => toUpdateCodeStore(item.code)}>
|
||||
{relativeTime(item.time)}
|
||||
</button>
|
||||
{/each}
|
||||
{:else}No records.{/if}
|
||||
</div>
|
||||
</Card>
|
||||
<Card title="Links">
|
||||
<div class="button-container">
|
||||
<button class="button-style">
|
||||
<a
|
||||
href="https://mermaid-js.github.io/mermaid"
|
||||
target="_blank"
|
||||
class="link-style">
|
||||
Mermaid Documentation
|
||||
</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a
|
||||
href="https://github.com/mermaid-js/mermaid"
|
||||
target="_blank"
|
||||
class="link-style">
|
||||
Mermaid on GitHub
|
||||
</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a
|
||||
href="https://github.com/mermaid-js/mermaid-live-editor"
|
||||
target="_blank"
|
||||
class="link-style">
|
||||
Live Editor on GitHub
|
||||
</a>
|
||||
</button>
|
||||
<button class="button-style">
|
||||
<a
|
||||
href="https://github.com/mermaid-js/mermaid-cli"
|
||||
target="_blank"
|
||||
class="link-style">
|
||||
Mermaid CLI
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div id="link-col2">
|
||||
<Card title="Actions">
|
||||
<Links />
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="power">
|
||||
Powered by mermaid
|
||||
<Tag color="green">{mermaidVersion}</Tag>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,30 +0,0 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import Editor from '../components/Editor.svelte';
|
||||
import View from '../components/View.svelte';
|
||||
// import { fromUrl } from '../code-store.js';
|
||||
|
||||
onMount(async () => {
|
||||
// fromUrl(params.data);
|
||||
ga('send', 'pageview');
|
||||
});
|
||||
|
||||
// export let code = '';
|
||||
// export let classes = '';
|
||||
|
||||
// export let error = {};
|
||||
// export let token = '';
|
||||
// export let expected = '';
|
||||
// export let params = {};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#view-page {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="view-page">
|
||||
<View />
|
||||
</div>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import '../app.postcss';
|
||||
import { base } from '$app/paths';
|
||||
import { onMount } from 'svelte';
|
||||
import { loadingStateStore } from '$lib/util/loading';
|
||||
// This can be removed once https://github.com/sveltejs/kit/issues/1612 is fixed.
|
||||
// Then move it into src and vite will bundle it automatically.
|
||||
onMount(() => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register(`${base}/service-worker.js`, {
|
||||
scope: `${base}/`
|
||||
})
|
||||
.then(function (registration) {
|
||||
console.log('Registration successful, scope is:', registration.scope);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('Service worker registration failed, error:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<main class="h-screen">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
{#if $loadingStateStore.loading}
|
||||
<div
|
||||
class="w-screen h-screen z-50 absolute left-0 top-0 bg-gray-600 opacity-50 flex align-middle justify-center">
|
||||
<div class="text-indigo-100 text-4xl font-bold my-auto">
|
||||
<div class="loader mx-auto" />
|
||||
<div>{$loadingStateStore.message}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.loader {
|
||||
border: 0.45em solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 0.45em solid #6365f1;
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
-webkit-animation: spin 2s linear infinite; /* Safari */
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
/* Safari */
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,200 @@
|
||||
<script lang="ts">
|
||||
import Editor from '$lib/components/editor/editor.svelte';
|
||||
import Navbar from '$lib/components/navbar.svelte';
|
||||
import Preset from '$lib/components/preset.svelte';
|
||||
import Actions from '$lib/components/actions.svelte';
|
||||
import View from '$lib/components/view.svelte';
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import History from '$lib/components/history/history.svelte';
|
||||
import { updateCode, updateConfig, codeStore, base64State } from '$lib/util/state';
|
||||
import { initHandler, syncDiagram } from '$lib/util/util';
|
||||
import { errorStore } from '$lib/util/error';
|
||||
import { onMount } from 'svelte';
|
||||
import * as mermaid from 'mermaid';
|
||||
import type monaco from 'monaco-editor';
|
||||
import type { EditorUpdateEvent, State, Tab } from '$lib/types';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
let selectedMode = 'code';
|
||||
const languageMap = {
|
||||
code: 'mermaid',
|
||||
config: 'json'
|
||||
};
|
||||
let text = '';
|
||||
let language: 'mermaid' | 'json' = 'mermaid';
|
||||
let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||
$: language = languageMap[selectedMode];
|
||||
$: {
|
||||
if (selectedMode === 'code') {
|
||||
text = $codeStore.code;
|
||||
} else {
|
||||
text = $codeStore.mermaid;
|
||||
}
|
||||
}
|
||||
|
||||
codeStore.subscribe((state: State) => {
|
||||
if (state.updateEditor) {
|
||||
text = selectedMode === 'code' ? state.code : state.mermaid;
|
||||
}
|
||||
});
|
||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||
$codeStore.updateEditor = true;
|
||||
selectedMode = message.detail.id;
|
||||
};
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
id: 'code',
|
||||
title: 'Code',
|
||||
icon: 'fas fa-code'
|
||||
},
|
||||
{
|
||||
id: 'config',
|
||||
title: 'Config',
|
||||
icon: 'fas fa-cogs'
|
||||
}
|
||||
];
|
||||
|
||||
const handleCodeUpdate = (code: string): void => {
|
||||
mermaid.parse(code);
|
||||
updateCode(code, false);
|
||||
};
|
||||
|
||||
const handleConfigUpdate = (config: string): void => {
|
||||
JSON.parse(config);
|
||||
updateConfig(config, false);
|
||||
};
|
||||
|
||||
const updateHandler = (message: CustomEvent<EditorUpdateEvent>) => {
|
||||
try {
|
||||
if (selectedMode === 'code') {
|
||||
handleCodeUpdate(message.detail.text);
|
||||
} else {
|
||||
handleConfigUpdate(message.detail.text);
|
||||
}
|
||||
errorStore.set(undefined);
|
||||
errorMarkers = [];
|
||||
} catch (e) {
|
||||
errorStore.set(e);
|
||||
if (e.hash) {
|
||||
const marker: monaco.editor.IMarkerData = {
|
||||
severity: 8, //Error
|
||||
startLineNumber: e.hash.loc.first_line,
|
||||
startColumn: e.hash.loc.first_column,
|
||||
endLineNumber: e.hash.loc.last_line,
|
||||
endColumn: e.hash.loc.last_column + 1,
|
||||
message: e.str
|
||||
};
|
||||
errorMarkers.push(marker);
|
||||
// Clear all previous errors before this error.
|
||||
errorMarkers = errorMarkers.filter(
|
||||
(m) => m.startLineNumber >= marker.startLineNumber && m.startColumn >= marker.startColumn
|
||||
);
|
||||
}
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const viewDiagram = () => {
|
||||
window.open(`${base}/view#${$base64State}`, '_blank').focus();
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
await initHandler();
|
||||
const resizer = document.getElementById('resizeHandler');
|
||||
const element = document.getElementById('editorPane');
|
||||
const resize = (e) => {
|
||||
const newWidth = e.pageX - element.getBoundingClientRect().left;
|
||||
if (newWidth > 50) {
|
||||
element.style.width = `${newWidth}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const stopResize = () => {
|
||||
window.removeEventListener('mousemove', resize);
|
||||
};
|
||||
resizer.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col overflow-hidden bg-gray-100">
|
||||
<Navbar />
|
||||
<div class="flex-1 flex overflow-hidden">
|
||||
<div class="hidden md:flex flex-col" id="editorPane" style="width: 40%">
|
||||
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} title="Mermaid">
|
||||
<div slot="actions">
|
||||
{#if !$codeStore.autoSync}
|
||||
<button
|
||||
class="bg-indigo-500 hover:bg-indigo-700 rounded px-4 mx-2"
|
||||
title="Sync Diagram"
|
||||
data-cy="sync"
|
||||
on:click={syncDiagram}><i class="fas fa-sync" /></button>
|
||||
{/if}
|
||||
<label
|
||||
for="autoSync"
|
||||
style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
|
||||
<input type="checkbox" id="autoSync" bind:checked={$codeStore.autoSync} />
|
||||
Auto sync
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Editor on:update={updateHandler} {language} bind:text {errorMarkers} />
|
||||
</Card>
|
||||
|
||||
<div class="-mt-2">
|
||||
<Preset />
|
||||
<History />
|
||||
<Actions />
|
||||
</div>
|
||||
</div>
|
||||
<div id="resizeHandler" />
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<Card title="Diagram" isCloseable={false}>
|
||||
<button
|
||||
slot="actions"
|
||||
class="btn"
|
||||
title="View diagram in new page"
|
||||
on:click|stopPropagation={() => viewDiagram()}><i class="far fa-eye" /> View</button>
|
||||
|
||||
<div class="flex-1 overflow-auto">
|
||||
<View />
|
||||
</div>
|
||||
</Card>
|
||||
<div class="md:hidden bg-white rounded shadow p-2 mx-2">
|
||||
Code editing not supported on mobile. Please use a desktop browser.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#resizeHandler {
|
||||
cursor: col-resize;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
#resizeHandler::after {
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: #ccc;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
#resizeHandler:hover::after {
|
||||
margin-left: -2px;
|
||||
background-color: #818cf8;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#resizeHandler {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { onMount } from 'svelte';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
onMount(async () => {
|
||||
// Handle old live editor links and redirect to new version
|
||||
const hash = window.location.hash.split('/');
|
||||
let newURL = 'edit';
|
||||
if (hash.length > 2) {
|
||||
newURL = `${hash[1]}#${hash[2]}`;
|
||||
}
|
||||
await goto(`${base}/${newURL}`, {
|
||||
replaceState: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { base } from '$app/paths';
|
||||
export const get = (): { body: unknown } => {
|
||||
return {
|
||||
body: {
|
||||
short_name: 'Mermaid',
|
||||
name: 'Mermaid Live Editor',
|
||||
icons: [
|
||||
{
|
||||
src: `${base}/icon-192.png`,
|
||||
type: 'image/png',
|
||||
sizes: '192x192'
|
||||
},
|
||||
{
|
||||
src: `${base}/icon-512.png`,
|
||||
type: 'image/png',
|
||||
sizes: '512x512'
|
||||
}
|
||||
],
|
||||
start_url: `${base}/edit/`,
|
||||
background_color: '#6366F1',
|
||||
display: 'standalone',
|
||||
scope: `${base}/edit/`,
|
||||
theme_color: '#6366F1',
|
||||
description: 'FlowChart & Diagrams Editor.',
|
||||
orientation: 'landscape'
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
import View from '$lib/components/view.svelte';
|
||||
import { initHandler } from '$lib/util/util';
|
||||
import { onMount } from 'svelte';
|
||||
onMount(initHandler);
|
||||
</script>
|
||||
|
||||
<View />
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
self.addEventListener('fetch', () => {});
|
||||
@@ -0,0 +1,32 @@
|
||||
import preprocess from 'svelte-preprocess';
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: [
|
||||
preprocess({
|
||||
postcss: true
|
||||
})
|
||||
],
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
pages: `docs${process.env['BETA'] ? '/beta' : ''}`
|
||||
}),
|
||||
paths: process.env['DEPLOY']
|
||||
? {
|
||||
base: `/mermaid-live-editor${process.env['BETA'] ? '/beta' : ''}`
|
||||
}
|
||||
: {},
|
||||
ssr: false,
|
||||
// hydrate the <div id="svelte"> element in src/app.html
|
||||
target: '#svelte',
|
||||
trailingSlash: 'ignore',
|
||||
vite: {
|
||||
optimizeDeps: { include: ['mermaid'] }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,25 @@
|
||||
const { tailwindExtractor } = require('tailwindcss/lib/lib/purgeUnusedStyles');
|
||||
|
||||
module.exports = {
|
||||
mode: 'aot',
|
||||
purge: {
|
||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||
options: {
|
||||
defaultExtractor: (content) => [
|
||||
// If this stops working, please open an issue at https://github.com/svelte-add/tailwindcss/issues rather than bothering Tailwind Labs about it
|
||||
...tailwindExtractor(content),
|
||||
// Match Svelte class: directives (https://github.com/tailwindlabs/tailwindcss/discussions/1731)
|
||||
...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(
|
||||
([_match, group, ..._rest]) => group
|
||||
)
|
||||
]
|
||||
},
|
||||
safelist: [/^svelte-[\d\w]+$/]
|
||||
},
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
variants: {
|
||||
extend: {}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"module": "es2020",
|
||||
"lib": ["es2020", "ESNext", "DOM.Iterable"],
|
||||
"target": "es2019",
|
||||
/**
|
||||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
||||
to enforce using \`import type\` instead of \`import\` for Types.
|
||||
*/
|
||||
"importsNotUsedAsValues": "error",
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
To have warnings/errors of the Svelte compiler at the correct position,
|
||||
enable source maps by default.
|
||||
*/
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.js",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.svelte",
|
||||
"cypress/**/*.ts",
|
||||
"cypress/**/*.js",
|
||||
"static/**/*.js"
|
||||
]
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const path = require('path');
|
||||
|
||||
const mode = process.env.NODE_ENV || 'development';
|
||||
const prod = mode === 'production';
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
bundle: ['./src/main.js']
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
svelte: path.resolve('node_modules', 'svelte'),
|
||||
"@mermaid": 'mermaid'
|
||||
// "@mermaid": '@mermaid-js/mermaid'
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.svelte'],
|
||||
mainFields: ['svelte', 'browser', 'module', 'main']
|
||||
},
|
||||
output: {
|
||||
path: __dirname + '/docs',
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[name].[id].js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.svelte$/,
|
||||
use: {
|
||||
loader: 'svelte-loader',
|
||||
options: {
|
||||
emitCss: true,
|
||||
hotReload: true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
/**
|
||||
* MiniCssExtractPlugin doesn't support HMR.
|
||||
* For developing, use 'style-loader' instead.
|
||||
* */
|
||||
prod ? MiniCssExtractPlugin.loader : 'style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
mode,
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].css'
|
||||
})
|
||||
],
|
||||
devtool: prod ? false: 'source-map'
|
||||
};
|
||||
Reference in New Issue
Block a user